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

#include <fnmatch.h>

#define EFL_ACCESS_OBJECT_PROTECTED
#define EFL_ACCESS_SELECTION_PROTECTED
#define EFL_ACCESS_WIDGET_ACTION_PROTECTED
#define ELM_WIDGET_ITEM_PROTECTED
#define EFL_UI_FOCUS_COMPOSITION_PROTECTED

#include <Elementary.h>
#include <Elementary_Cursor.h>

#include "elm_priv.h"
#include "elm_widget_genlist.h"
#include "elm_interface_scrollable.h"
#include "elm_pan_eo.h"
#include "efl_ui_focus_parent_provider_gen_eo.h"
#include "efl_ui_focus_composition_adapter.eo.h"
#include "elm_genlist_item_eo.h"
#include "elm_genlist_pan_eo.h"
#include "elm_genlist_eo.h"

#define MY_PAN_CLASS ELM_GENLIST_PAN_CLASS

#define MY_PAN_CLASS_NAME "Elm_Genlist_Pan"
#define MY_PAN_CLASS_NAME_LEGACY "elm_genlist_pan"

#define MY_CLASS ELM_GENLIST_CLASS

#define MY_CLASS_NAME "Elm_Genlist"
#define MY_CLASS_NAME_LEGACY "elm_genlist"

// internally allocated
#define CLASS_ALLOCATED     0x3a70f11f

#define MAX_ITEMS_PER_BLOCK 32
#define REORDER_EFFECT_TIME 0.5
#define MULTI_DOWN_TIME 1.0
#define SWIPE_TIME 0.4
#define SCR_HOLD_TIME 0.1
#define ITEM_QUEUE_MAX 128

#define ERR_ABORT(_msg)                         \
   do {                                         \
        ERR(_msg);                              \
        if (getenv("ELM_ERROR_ABORT")) abort(); \
   } while (0)

#define ELM_PRIV_GENLIST_SIGNALS(cmd) \
    cmd(SIG_ACTIVATED, "activated", "") \
    cmd(SIG_CLICKED_DOUBLE, "clicked,double", "") \
    cmd(SIG_CLICKED_RIGHT, "clicked,right", "") \
    cmd(SIG_SELECTED, "selected", "") \
    cmd(SIG_UNSELECTED, "unselected", "") \
    cmd(SIG_EXPANDED, "expanded", "") \
    cmd(SIG_CONTRACTED, "contracted", "") \
    cmd(SIG_EXPAND_REQUEST, "expand,request", "") \
    cmd(SIG_CONTRACT_REQUEST, "contract,request", "") \
    cmd(SIG_REALIZED, "realized", "") \
    cmd(SIG_UNREALIZED, "unrealized", "") \
    cmd(SIG_DRAG_START_UP, "drag,start,up", "") \
    cmd(SIG_DRAG_START_DOWN, "drag,start,down", "") \
    cmd(SIG_DRAG_START_LEFT, "drag,start,left", "") \
    cmd(SIG_DRAG_START_RIGHT, "drag,start,right", "") \
    cmd(SIG_DRAG_STOP, "drag,stop", "") \
    cmd(SIG_DRAG, "drag", "") \
    cmd(SIG_LONGPRESSED, "longpressed", "") \
    cmd(SIG_SCROLL, "scroll", "") \
    cmd(SIG_SCROLL_ANIM_START, "scroll,anim,start", "") \
    cmd(SIG_SCROLL_ANIM_STOP, "scroll,anim,stop", "") \
    cmd(SIG_SCROLL_DRAG_START, "scroll,drag,start", "") \
    cmd(SIG_SCROLL_DRAG_STOP, "scroll,drag,stop", "") \
    cmd(SIG_EDGE_TOP, "edge,top", "") \
    cmd(SIG_EDGE_BOTTOM, "edge,bottom", "") \
    cmd(SIG_EDGE_LEFT, "edge,left", "") \
    cmd(SIG_EDGE_RIGHT, "edge,right", "") \
    cmd(SIG_VBAR_DRAG, "vbar,drag", "") \
    cmd(SIG_VBAR_PRESS, "vbar,press", "") \
    cmd(SIG_VBAR_UNPRESS, "vbar,unpress", "") \
    cmd(SIG_HBAR_DRAG, "hbar,drag", "") \
    cmd(SIG_HBAR_PRESS, "hbar,press", "") \
    cmd(SIG_HBAR_UNPRESS, "hbar,unpress", "") \
    cmd(SIG_MULTI_SWIPE_LEFT, "multi,swipe,left", "") \
    cmd(SIG_MULTI_SWIPE_RIGHT, "multi,swipe,right", "") \
    cmd(SIG_MULTI_SWIPE_UP, "multi,swipe,up", "") \
    cmd(SIG_MULTI_SWIPE_DOWN, "multi,swipe,down", "") \
    cmd(SIG_MULTI_PINCH_OUT, "multi,pinch,out", "") \
    cmd(SIG_MULTI_PINCH_IN, "multi,pinch,in", "") \
    cmd(SIG_SWIPE, "swipe", "") \
    cmd(SIG_MOVED, "moved", "") \
    cmd(SIG_MOVED_AFTER, "moved,after", "") \
    cmd(SIG_MOVED_BEFORE, "moved,before", "") \
    cmd(SIG_INDEX_UPDATE, "index,update", "") \
    cmd(SIG_TREE_EFFECT_FINISHED , "tree,effect,finished", "") \
    cmd(SIG_HIGHLIGHTED, "highlighted", "") \
    cmd(SIG_UNHIGHLIGHTED, "unhighlighted", "") \
    cmd(SIG_ITEM_FOCUSED, "item,focused", "") \
    cmd(SIG_ITEM_UNFOCUSED, "item,unfocused", "") \
    cmd(SIG_PRESSED, "pressed", "") \
    cmd(SIG_RELEASED, "released", "") \
    cmd(SIG_FILTER_DONE, "filter,done", "")

ELM_PRIV_GENLIST_SIGNALS(ELM_PRIV_STATIC_VARIABLE_DECLARE);

static const Evas_Smart_Cb_Description _smart_callbacks[] = {
   ELM_PRIV_GENLIST_SIGNALS(ELM_PRIV_SMART_CALLBACKS_DESC)
   {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */
   {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */
   {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */
   {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */

   {NULL, NULL}
};
#undef ELM_PRIV_GENLIST_SIGNALS

/* edje signals internally used */
static const char SIGNAL_ENABLED[] = "elm,state,enabled";
static const char SIGNAL_DISABLED[] = "elm,state,disabled";
static const char SIGNAL_SELECTED[] = "elm,state,selected";
static const char SIGNAL_UNSELECTED[] = "elm,state,unselected";
static const char SIGNAL_EXPANDED[] = "elm,state,expanded";
static const char SIGNAL_CONTRACTED[] = "elm,state,contracted";
static const char SIGNAL_FLIP_ENABLED[] = "elm,state,flip,enabled";
static const char SIGNAL_FLIP_DISABLED[] = "elm,state,flip,disabled";
static const char SIGNAL_DECORATE_ENABLED[] = "elm,state,decorate,enabled";
static const char SIGNAL_DECORATE_DISABLED[] = "elm,state,decorate,disabled";
static const char SIGNAL_DECORATE_ENABLED_EFFECT[] = "elm,state,decorate,enabled,effect";
static const char SIGNAL_REORDER_ENABLED[] = "elm,state,reorder,enabled";
static const char SIGNAL_REORDER_DISABLED[] = "elm,state,reorder,disabled";
static const char SIGNAL_REORDER_MODE_SET[] = "elm,state,reorder,mode_set";
static const char SIGNAL_REORDER_MODE_UNSET[] = "elm,state,reorder,mode_unset";
static const char SIGNAL_CONTRACT_FLIP[] = "elm,state,contract_flip";
static const char SIGNAL_SHOW[] = "elm,state,show";
static const char SIGNAL_HIDE[] = "elm,state,hide";
static const char SIGNAL_FLIP_ITEM[] = "elm,action,flip_item";
static const char SIGNAL_ODD[] = "elm,state,odd";
static const char SIGNAL_EVEN[] = "elm,state,even";
static const char SIGNAL_FOCUSED[] = "elm,state,focused";
static const char SIGNAL_UNFOCUSED[] = "elm,state,unfocused";
static const char SIGNAL_LIST_SINGLE[] = "elm,state,list,single";
static const char SIGNAL_LIST_FIRST[] = "elm,state,list,first";
static const char SIGNAL_LIST_LAST[] = "elm,state,list,last";
static const char SIGNAL_LIST_MIDDLE[] = "elm,state,list,middle";
static const char SIGNAL_GROUP_SINGLE[] = "elm,state,group,single";
static const char SIGNAL_GROUP_FIRST[] = "elm,state,group,first";
static const char SIGNAL_GROUP_LAST[] = "elm,state,group,last";
static const char SIGNAL_GROUP_MIDDLE[] = "elm,state,group,middle";

static void _item_unrealize(Elm_Gen_Item *it);
static Eina_Bool _item_select(Elm_Gen_Item *it);
static void _item_unselect(Elm_Gen_Item *it);
static void _item_highlight(Elm_Gen_Item *it);
static Eina_Bool _key_action_move(Evas_Object *obj, const char *params);
static Eina_Bool _key_action_select(Evas_Object *obj, const char *params);
static Eina_Bool _key_action_escape(Evas_Object *obj, const char *params);
static void  _calc_job(void *data);
static void  _update_job(void *data);
static Eina_Bool _item_block_recalc(Item_Block *itb, int in, Eina_Bool qadd);
static void _item_mouse_callbacks_add(Elm_Gen_Item *it, Evas_Object *view);
static void _item_mouse_callbacks_del(Elm_Gen_Item *it, Evas_Object *view);
static void _access_activate_cb(void *data EINA_UNUSED,
                                Evas_Object *part_obj EINA_UNUSED,
                                Elm_Object_Item *item);
static void _decorate_item_set(Elm_Gen_Item *);
static void _internal_elm_genlist_clear(Evas_Object *obj);
static Eina_Bool _item_filtered_get(Elm_Gen_Item *it, Elm_Genlist_Data *sd);

static void _elm_genlist_tree_effect_stop(Elm_Genlist_Data *sd);
static Eina_Bool _elm_genlist_tree_effect_setup(Elm_Genlist_Data *sd);

static void _item_expanded_set_noevent(Elm_Gen_Item *it, Eina_Bool expanded);

static Eina_Bool _item_process(Elm_Genlist_Data *sd, Elm_Gen_Item *it);
static void _item_process_post(Elm_Genlist_Data *sd, Elm_Gen_Item *it);

static const Elm_Action key_actions[] = {
   {"move", _key_action_move},
   {"select", _key_action_select},
   {"escape", _key_action_escape},
   {NULL, NULL}
};

static void
_size_cache_free(void *data)
{
   if(data) free(data);
}

static Eina_Bool
_is_no_select(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if ((sd->select_mode == ELM_OBJECT_SELECT_MODE_NONE) ||
       (sd->select_mode == ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY) ||
       (it->select_mode == ELM_OBJECT_SELECT_MODE_NONE) ||
       (it->select_mode == ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY))
     return EINA_TRUE;
   return EINA_FALSE;
}

EOLIAN static void
_elm_genlist_pan_elm_pan_pos_set(Eo *obj, Elm_Genlist_Pan_Data *psd, Evas_Coord x, Evas_Coord y)
{
   Item_Block *itb;

   Elm_Genlist_Data *sd = psd->wsd;

   if ((x == sd->pan_x) && (y == sd->pan_y)) return;
   sd->pan_x = x;
   sd->pan_y = y;

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        if ((itb->y + itb->h) > y)
          {
             Elm_Gen_Item *it;
             Eina_List *l2;

             EINA_LIST_FOREACH(itb->items, l2, it)
               {
                  if ((itb->y + it->y) >= y)
                    {
                       sd->anchor_item = it;
                       sd->anchor_y = -(itb->y + it->y - y);
                       goto done;
                    }
               }
          }
     }
done:
   if (!sd->reorder_move_animator) evas_object_smart_changed(obj);
}

EOLIAN static void
_elm_genlist_pan_elm_pan_pos_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Pan_Data *psd, Evas_Coord *x, Evas_Coord *y)
{
   if (x) *x = psd->wsd->pan_x;
   if (y) *y = psd->wsd->pan_y;
}

EOLIAN static void
_elm_genlist_pan_elm_pan_pos_max_get(const Eo *obj, Elm_Genlist_Pan_Data *psd, Evas_Coord *x, Evas_Coord *y)
{
   Evas_Coord ow, oh;

   evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
   ow = psd->wsd->minw - ow;
   if (ow < 0) ow = 0;
   oh = psd->wsd->minh - oh;
   if (oh < 0) oh = 0;
   if (x) *x = ow;
   if (y) *y = oh;
}

EOLIAN static void
_elm_genlist_pan_elm_pan_pos_min_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Pan_Data *_pd EINA_UNUSED, Evas_Coord *x, Evas_Coord *y)
{
   if (x) *x = 0;
   if (y) *y = 0;
}

EOLIAN static void
_elm_genlist_pan_elm_pan_content_size_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Pan_Data *psd, Evas_Coord *w, Evas_Coord *h)
{
   if (w) *w = psd->wsd->minw;
   if (h) *h = psd->wsd->minh;
}

EOLIAN static void
_elm_genlist_pan_efl_canvas_group_group_del(Eo *obj, Elm_Genlist_Pan_Data *psd)
{
   ecore_job_del(psd->resize_job);

   efl_canvas_group_del(efl_super(obj, MY_PAN_CLASS));
}

EOLIAN static void
_elm_genlist_pan_efl_gfx_entity_position_set(Eo *obj, Elm_Genlist_Pan_Data *psd, Eina_Position2D pos)
{
   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y))
     return;

   efl_gfx_entity_position_set(efl_super(obj, MY_PAN_CLASS), pos);

   psd->wsd->pan_changed = EINA_TRUE;
   evas_object_smart_changed(obj);
}

static void
_elm_genlist_pan_smart_resize_job(void *data)
{
   ELM_GENLIST_PAN_DATA_GET(data, psd);

   elm_layout_sizing_eval(psd->wobj);
   psd->resize_job = NULL;
}

EOLIAN static void
_elm_genlist_pan_efl_gfx_entity_size_set(Eo *obj, Elm_Genlist_Pan_Data *psd, Eina_Size2D size)
{
   Elm_Genlist_Data *sd = psd->wsd;
   Eina_Size2D old;

   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, size.w, size.h))
     return;

   old = efl_gfx_entity_size_get(obj);
   if ((old.w == size.w) && (old.h == size.h)) goto super; // should already be intercepted above
   if ((sd->mode == ELM_LIST_COMPRESS) && (old.w != size.w))
     {
        /* fix me later */
        ecore_job_del(psd->resize_job);
        psd->resize_job =
          ecore_job_add(_elm_genlist_pan_smart_resize_job, obj);
     }
   sd->pan_changed = EINA_TRUE;
   evas_object_smart_changed(obj);
   // if the width changed we may have to resize content if scrollbar went
   // away or appeared to queue a job to deal with it. it should settle in
   // the end to a steady-state
   if (old.w != size.w)
     efl_canvas_group_change(sd->obj);

super:
   efl_gfx_entity_size_set(efl_super(obj, MY_PAN_CLASS), size);
}

static void
_item_text_realize(Elm_Gen_Item *it,
                   Evas_Object *target,
                   Eina_List **source,
                   const char *parts)
{
   const Eina_List *l;
   const char *key;
   char *s;
   char buf[256];

   if (!it->itc->func.text_get) return;

   if (!(*source))
     *source = elm_widget_stringlist_get
        (edje_object_data_get(target, "texts"));
   EINA_LIST_FOREACH(*source, l, key)
     {
        if (parts && fnmatch(parts, key, FNM_PERIOD)) continue;

        s = it->itc->func.text_get
           ((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), key);
        if (s)
          {
             edje_object_part_text_escaped_set(target, key, s);
             free(s);

             snprintf(buf, sizeof(buf), "elm,state,%s,visible", key);
             edje_object_signal_emit(target, buf, "elm");
          }
        else
          {
             edje_object_part_text_set(target, key, "");
          }
        if (_elm_config->atspi_mode)
          efl_access_i18n_name_changed_signal_emit(EO_OBJ(it));
     }
}

static void
_widget_calculate_recursive(Eo *obj)
{
   Elm_Widget_Smart_Data *pd = NULL;
   Evas_Object *child;

   if (!efl_isa(obj, EFL_UI_WIDGET_CLASS)) return;

   pd = efl_data_scope_get(obj, EFL_UI_WIDGET_CLASS);
   if (!pd || !pd->resize_obj)
     return;

   if (!efl_canvas_group_need_recalculate_get(obj))
     {
        if (!efl_isa(pd->resize_obj, EFL_CANVAS_GROUP_CLASS) ||
            !efl_canvas_group_need_recalculate_get(pd->resize_obj))
          return;
     }

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

   efl_canvas_group_calculate(obj);
}

static void
_item_content_realize(Elm_Gen_Item *it,
                      Evas_Object *target,
                      Eina_List **contents,
                      const char *src,
                      const char *parts,
                      Eina_Bool calc)
{
   Evas_Object *content;
   char buf[256];
   Eina_List *source;
   const char *key;

   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (!parts)
     {
        EINA_LIST_FREE(*contents, content)
          evas_object_del(content);
     }
   if ((!it->itc->func.content_get) &&
      ((it->itc->version < 3) || (!it->itc->func.reusable_content_get))) return;

   source = elm_widget_stringlist_get(edje_object_data_get(target, src));

   EINA_LIST_FREE(source, key)
     {
        if (parts && fnmatch(parts, key, FNM_PERIOD))
          continue;

        Evas_Object *old = NULL;
        old = edje_object_part_swallow_get(target, key);

        // Reuse content by popping from the cache
        content = NULL;
        if (it->itc->func.reusable_content_get)
          content = it->itc->func.reusable_content_get(
             (void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), key, old);
        if (!content)
          {
             // Call the content get
             if (it->itc->func.content_get)
               content = it->itc->func.content_get
                  ((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), key);
             if (!content) goto out;
          }

        if (content != old)
          {
             eina_hash_add(sd->content_item_map, &content, it->base->eo_obj);
             // FIXME: Genlist item doesn't update its size when the size of
             // content is changed, so deferred calculation for content should
             // be performed before realization.
             if (efl_isa(content, EFL_UI_WIDGET_CLASS))
               {
                  ELM_WIDGET_DATA_GET_OR_RETURN(content, wd);

                  // FIXME : Layout need sizing eval before group calculate
                  if (efl_class_get(content) == EFL_UI_LAYOUT_BASE_CLASS)
                    elm_layout_sizing_eval(content);

                  _widget_calculate_recursive(content);
               }

             if (!edje_object_part_swallow(target, key, content))
               {
                  ERR("%s (%p) can not be swallowed into %s",
                      evas_object_type_get(content), content, key);
                  evas_object_del(content);
                  goto out;
               }
             elm_widget_sub_object_add(WIDGET(it), content);

             if (elm_widget_is(content))
               {
                  elm_widget_tree_unfocusable_set(content, it->item->unfocusable);
                  if (!calc && (!it->item->unfocusable))
                    _elm_widget_full_eval(content);
               }
          }
        *contents = eina_list_append(*contents, content);

        if (elm_wdg_item_disabled_get(EO_OBJ(it)))
          elm_widget_disabled_set(content, EINA_TRUE);

        snprintf(buf, sizeof(buf), "elm,state,%s,visible", key);
        edje_object_signal_emit(target, buf, "elm");
out:
        if (old && content != old)
          {
             *contents = eina_list_remove(*contents, old);
             evas_object_del(old);
             eina_hash_del_by_key(sd->content_item_map, &old);
          }
     }
}

static void
_item_state_realize(Elm_Gen_Item *it, Evas_Object *target, const char *parts)
{
   Eina_List *src;
   const char *key;
   char buf[4096];

   if (!it->itc->func.state_get) return;

   src = elm_widget_stringlist_get(edje_object_data_get(target, "states"));
   EINA_LIST_FREE(src, key)
     {
        if (parts && fnmatch(parts, key, FNM_PERIOD)) continue;

        Eina_Bool on = it->itc->func.state_get
           ((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), key);

        if (on)
          {
             snprintf(buf, sizeof(buf), "elm,state,%s,active", key);
             edje_object_signal_emit(target, buf, "elm");
          }
        else
          {
             snprintf(buf, sizeof(buf), "elm,state,%s,passive", key);
             edje_object_signal_emit(target, buf, "elm");
          }
     }
   edje_object_message_signal_process(target);
}

/**
 * Apply the right style for the created item view.
 */
static void
_view_style_update(Elm_Gen_Item *it, Evas_Object *view, const char *style)
{
   char buf[1024];
   const char *stacking_even;
   const char *stacking;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   // FIXME:  There exists
   // item, item_compress, item_odd, item_compress_odd,
   // tree, tree_compress, tree_odd, tree_odd_compress
   // But those used case by case. :-(
   // Anyway, belows codes are for backward..
   if (it->decorate_it_set)
     {
        // item, item_compress, item_odd, item_compress_odd
        strncpy(buf, "item", sizeof(buf));
        if (sd->mode == ELM_LIST_COMPRESS)
           strncat(buf, "_compress", sizeof(buf) - strlen(buf) - 1);

        if (it->item->order_num_in & 0x1)
           strncat(buf, "_odd", sizeof(buf) - strlen(buf) - 1);
        strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
        strncat(buf, style, sizeof(buf) - strlen(buf) - 1);
     }
   else
     {
        // item, item_compress, tree, tree_compress
        if (it->item->type & ELM_GENLIST_ITEM_TREE)
           snprintf(buf, sizeof(buf), "tree%s/%s",
                    sd->mode == ELM_LIST_COMPRESS ? "_compress" :
                    "", style ? : "default");
        else
           snprintf(buf, sizeof(buf), "item%s/%s",
                    sd->mode == ELM_LIST_COMPRESS ? "_compress" :
                    "",style ? : "default");
     }

   Eina_Error th_ret =
      elm_widget_theme_object_set(WIDGET(it), view, "genlist", buf,
                                  elm_widget_style_get(WIDGET(it)));
   if (th_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC)
     {
        ERR("%s is not a valid genlist item style. "
            "Automatically falls back into default style.",
            style);
        elm_widget_theme_object_set
          (WIDGET(it), view, "genlist", "item/default", "default");
     }

   edje_object_mirrored_set(view, efl_ui_mirrored_get(WIDGET(it)));
   edje_object_scale_set(view, efl_gfx_entity_scale_get(WIDGET(it))
                         * elm_config_scale_get());

   stacking_even = edje_object_data_get(view, "stacking_even");
   if (!stacking_even) stacking_even = "above";
   it->item->stacking_even = !!strcmp("above", stacking_even);

   stacking = edje_object_data_get(view, "stacking");
   if (!stacking) stacking = "yes";
   it->item->nostacking = !!strcmp("yes", stacking);
}

/**
 * Create a VIEW(it) during _item_realize()
 */
static Evas_Object *
_view_create(Elm_Gen_Item *it, const char *style)
{
   Evas_Object *view = edje_object_add(evas_object_evas_get(WIDGET(it)));
   evas_object_smart_member_add(view, it->item->wsd->pan_obj);
   elm_widget_sub_object_add(WIDGET(it), view);
   edje_object_scale_set(view, efl_gfx_entity_scale_get(WIDGET(it)) *
                         elm_config_scale_get());

   _view_style_update(it, view, style);
   return view;
}

static void
_view_clear(Evas_Object *view, Eina_List **texts, Eina_List **contents)
{
   const char *part;
   Evas_Object *c;
   const Eina_List *l;

   EINA_LIST_FOREACH(*texts, l, part)
     edje_object_part_text_set(view, part, NULL);
   ELM_SAFE_FREE(*texts, elm_widget_stringlist_free);

   if (contents)
     {
        EINA_LIST_FREE(*contents, c)
           evas_object_del(c);
     }
}

static void
_item_scroll(Elm_Genlist_Data *sd)
{
   Evas_Coord gith = 0;
   Elm_Gen_Item *it = NULL;
   Evas_Coord ow, oh, dx = 0, dy = 0, dw = 0, dh = 0;

   if (!sd->show_item) return;

   evas_object_geometry_get(sd->pan_obj, NULL, NULL, &ow, &oh);
   it = sd->show_item;
   dx = it->x + it->item->block->x;
   dy = it->y + it->item->block->y;
   dw = it->item->block->w;
   dh = oh;

   if (dw < 1) return;
   if (ow < 1 || oh < 1) return;

   switch (sd->scroll_to_type)
     {
      case ELM_GENLIST_ITEM_SCROLLTO_TOP:
        if (it->item->group_item) gith = it->item->group_item->item->h;
        dy -= gith;
        break;

      case ELM_GENLIST_ITEM_SCROLLTO_MIDDLE:
        dy += ((it->item->h / 2) - (oh / 2));
        break;

      case ELM_GENLIST_ITEM_SCROLLTO_BOTTOM:
        dy += (it->item->h - oh);
        break;

      case ELM_GENLIST_ITEM_SCROLLTO_IN:
      default:
        if ((sd->expanded_item) &&
            ((sd->show_item->y + sd->show_item->item->block->y
              + sd->show_item->item->h) -
             (sd->expanded_item->y + sd->expanded_item->item->block->y) > oh))
          {
             it = sd->expanded_item;
             if (it->item->group_item) gith = it->item->group_item->item->h;
             dx = it->x + it->item->block->x;
             dy = it->y + it->item->block->y - gith;
             dw = it->item->block->w;
          }
        else
          {
             if ((it->item->group_item) &&
                 (sd->pan_y > (it->y + it->item->block->y)))
               gith = it->item->group_item->item->h;
             dy -= gith;
             dh = it->item->h;
          }
        break;
     }
   if (sd->bring_in)
      elm_interface_scrollable_region_bring_in(sd->obj, dx, dy, dw, dh);
   else
      elm_interface_scrollable_content_region_show
            (sd->obj, dx, dy, dw, dh);

   it->item->show_me = EINA_FALSE;
   sd->show_item = NULL;
   sd->auto_scroll_enabled = EINA_FALSE;
   sd->check_scroll = EINA_FALSE;
}

static void
_elm_genlist_item_unrealize(Elm_Gen_Item *it,
                            Eina_Bool calc)
{
   Evas *e;
   if (!it->realized) return;
   if (it->item->wsd->reorder_it == it)
     {
        WRN("reordering item should not be unrealized");
        return;
     }

   e = evas_object_evas_get(WIDGET(it));
   evas_event_freeze(e);

   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);

   _view_clear(VIEW(it), &(it->texts), NULL);
   ELM_SAFE_FREE(it->item_focus_chain, eina_list_free);

   elm_wdg_item_track_cancel(EO_OBJ(it));

   if (!calc)
     {
        efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_UNREALIZED, EO_OBJ(it));
        if (it->base->func.unrealized) it->base->func.unrealized(EO_OBJ(it));
     }

   _item_unrealize(it);

   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static void
_item_block_unrealize(Item_Block *itb)
{
   Elm_Gen_Item *it;
   const Eina_List *l;
   Eina_Bool dragging = EINA_FALSE;
   Evas *e;

   if (!itb->realized) return;
   e = evas_object_evas_get((itb->sd)->obj);
   evas_event_freeze(e);

   EINA_LIST_FOREACH(itb->items, l, it)
     {
        if (itb->must_recalc || !(it->item->type & ELM_GENLIST_ITEM_GROUP))
          {
             if (it->dragging)
               {
                  dragging = EINA_TRUE;
                  it->want_unrealize = EINA_TRUE;
               }
             else if (it != itb->sd->pin_item)
               _elm_genlist_item_unrealize(it, EINA_FALSE);
          }
     }
   if (!dragging)
     {
        Eina_List *n;

        itb->realized = EINA_FALSE;
        itb->want_unrealize = EINA_TRUE;

        efl_ui_focus_manager_calc_unregister(itb->sd->obj, itb->adapter);
        efl_del(itb->adapter);
        itb->adapter = NULL;

        EINA_LIST_FOREACH(itb->items, n, it)
          {
            efl_ui_focus_manager_calc_unregister(itb->sd->obj, EO_OBJ(it));
          }
     }
   else
     itb->want_unrealize = EINA_FALSE;
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static Eina_Bool
_must_recalc_idler(void *data)
{
   ELM_GENLIST_DATA_GET(data, sd);

   efl_canvas_group_change(sd->obj);
   sd->must_recalc_idler = NULL;
   return ECORE_CALLBACK_CANCEL;
}

static void
_calc_job(void *data)
{
   int in = 0;
   Item_Block *itb, *chb = NULL;
   Elm_Genlist_Data *sd = data;
   Eina_Bool minw_change = EINA_FALSE;
   Eina_Bool did_must_recalc = EINA_FALSE;
   Evas_Coord minw = -1, minh = 0, y = 0, ow = 0, vw = 0;
   Evas *e;

   sd->need_calc = EINA_FALSE;
   evas_object_geometry_get(sd->pan_obj, NULL, NULL, &ow, &sd->h);
   if (sd->mode == ELM_LIST_COMPRESS)
      elm_interface_scrollable_content_viewport_geometry_get
            (sd->obj, NULL, NULL, &vw, NULL);

   if (sd->w != ow) sd->w = ow;

   e = evas_object_evas_get(sd->obj);
   evas_event_freeze(e);
   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        Eina_Bool show_me = EINA_FALSE;

        itb->num = in;
        show_me = itb->show_me;
        itb->show_me = EINA_FALSE;
        if (chb)
          {
             if (itb->realized) _item_block_unrealize(itb);
          }
        if ((itb->changed) || ((itb->must_recalc) && (!did_must_recalc)))
          {
             if (itb->must_recalc)
               {
                  Eina_List *l;
                  Elm_Gen_Item *it;

                  EINA_LIST_FOREACH(itb->items, l, it)
                    it->item->mincalcd = EINA_FALSE;
                  itb->changed = EINA_TRUE;
                  did_must_recalc = EINA_TRUE;
                  if (itb->realized) _item_block_unrealize(itb);
                  itb->must_recalc = EINA_FALSE;
               }
             show_me = _item_block_recalc(itb, in, EINA_FALSE);
             chb = itb;
          }
        itb->y = y;
        itb->x = 0;
        minh += itb->minh;
        if (minw < itb->minw)
          {
             minw = itb->minw;
             if (minw != -1)
               minw_change = EINA_TRUE;
          }
        if ((sd->mode == ELM_LIST_COMPRESS) && (minw > vw))
          {
             minw = vw;
             minw_change = EINA_TRUE;
          }
        itb->w = minw;
        itb->h = itb->minh;
        y += itb->h;
        in += itb->vis_count;
        if ((show_me) && (sd->show_item) && (!sd->show_item->item->queued))
          sd->check_scroll = EINA_TRUE;
     }
   if (minw_change)
     {
        EINA_INLIST_FOREACH(sd->blocks, itb)
          {
             itb->minw = minw;
             itb->w = itb->minw;
          }
     }
   if ((chb) && (EINA_INLIST_GET(chb)->next))
     {
        EINA_INLIST_FOREACH(EINA_INLIST_GET(chb)->next, itb)
          {
             if (itb->realized) _item_block_unrealize(itb);
          }
     }
   sd->realminw = minw;
   if (minw < sd->w) minw = sd->w;
   if ((minw != sd->minw) || (minh != sd->minh))
     {
        sd->minw = minw;
        sd->minh = minh;
        efl_event_callback_legacy_call
          (sd->pan_obj, ELM_PAN_EVENT_CHANGED, NULL);
        elm_layout_sizing_eval(sd->obj);
        if (sd->reorder_it)
          {
              Elm_Gen_Item *it;
              it = sd->reorder_it;
              it->item->w = minw;
          }
        if ((sd->anchor_item) && (sd->anchor_item->item->block)
            && (!sd->auto_scroll_enabled))
          {
             Elm_Gen_Item *it;
             Evas_Coord it_y;

             it = sd->anchor_item;
             it_y = sd->anchor_y;
             elm_interface_scrollable_content_pos_set
               (sd->obj, sd->pan_x, it->item->block->y
               + it->y + it_y, EINA_TRUE);
             sd->anchor_item = it;
             sd->anchor_y = it_y;
          }
     }
   if (did_must_recalc)
     {
        if (!sd->must_recalc_idler)
          sd->must_recalc_idler = ecore_idler_add(_must_recalc_idler, sd->obj);
     }
   if (!sd->show_item) sd->check_scroll = EINA_FALSE;
   if (sd->check_scroll)
     {
        if (EINA_INLIST_GET(sd->show_item) == sd->items->last)
          sd->scroll_to_type = ELM_GENLIST_ITEM_SCROLLTO_IN;

        if ((sd->show_item) && (sd->show_item->item->block))
          _item_scroll(sd);
     }

   evas_object_smart_changed(sd->pan_obj);
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

EOLIAN static void
_elm_genlist_efl_canvas_group_group_calculate(Eo *obj, Elm_Genlist_Data *sd)
{
   Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
   Evas_Coord vmw = 0, vmh = 0;

   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);

   if (sd->on_sub_del) return;;

   evas_object_size_hint_combined_min_get(obj, &minw, NULL);
   evas_object_size_hint_max_get(obj, &maxw, &maxh);

   edje_object_size_min_calc(wd->resize_obj, &vmw, &vmh);

   if (sd->mode == ELM_LIST_COMPRESS)
     {
        Evas_Coord vw = 0, vh = 0;

        elm_interface_scrollable_content_viewport_geometry_get
              (obj, NULL, NULL, &vw, &vh);
        if ((vw != 0) && (vw != sd->prev_viewport_w))
          {
             Item_Block *itb;

             sd->prev_viewport_w = vw;
             /* Free all buckets for updaing changed width */
             eina_hash_free_buckets(sd->size_caches);

             EINA_INLIST_FOREACH(sd->blocks, itb)
               {
                  itb->must_recalc = EINA_TRUE;
               }
             sd->need_calc = EINA_TRUE;
          }
        minw = vmw;
        minh = vmh;
     }
   else
     sd->need_calc = EINA_TRUE;
   if (sd->need_calc)
     _calc_job(sd);

   if (sd->scr_minw)
     {
        maxw = -1;
        minw = vmw + sd->realminw;
     }
   else
     {
         minw = vmw;
     }
   if (sd->scr_minh)
     {
        maxh = -1;
        minh = vmh + sd->minh;
     }
   else
     {
        minw = vmw;
        minh = vmh;
     }

   if ((maxw > 0) && (minw > maxw))
     minw = maxw;
   if ((maxh > 0) && (minh > maxh))
     minh = maxh;

   evas_object_size_hint_min_set(obj, minw, minh);
   evas_object_size_hint_max_set(obj, maxw, maxh);
}

static void
_content_min_limit_cb(Evas_Object *obj,
                      Eina_Bool w,
                      Eina_Bool h)
{
   ELM_GENLIST_DATA_GET(obj, sd);

   if ((sd->mode == ELM_LIST_LIMIT) ||
       (sd->mode == ELM_LIST_EXPAND)) return;
   sd->scr_minw = !!w;
   sd->scr_minh = !!h;

   elm_layout_sizing_eval(obj);
}

static void
_item_contract_emit(Elm_Object_Item *eo_it)
{
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
   Elm_Object_Item *eo_it2;
   Eina_List *l;

   //XXX: for compat
   edje_object_signal_emit(VIEW(it), SIGNAL_CONTRACT_FLIP, "");
   edje_object_signal_emit(VIEW(it), SIGNAL_CONTRACT_FLIP, "elm");
   it->item->tree_effect_finished = EINA_FALSE;

   EINA_LIST_FOREACH(it->item->items, l, eo_it2)
     if (eo_it2) _item_contract_emit(eo_it2);
}

static int
_item_tree_effect_before(Elm_Gen_Item *it)
{
   Elm_Object_Item *eo_it2;
   Eina_List *l;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   EINA_LIST_FOREACH(it->item->items, l, eo_it2)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
        if (it2->parent && (it == it2->parent))
          {
             if (!it2->realized)
               it2->item->tree_effect_hide_me = EINA_TRUE;
             if (sd->move_effect_mode ==
                 ELM_GENLIST_TREE_EFFECT_EXPAND)
               {
                  //XXX: for compat
                  edje_object_signal_emit(VIEW(it2), SIGNAL_HIDE, "");
                  edje_object_signal_emit(VIEW(it2), SIGNAL_HIDE, "elm");
               }
             else if (sd->move_effect_mode ==
                      ELM_GENLIST_TREE_EFFECT_CONTRACT)
               _item_contract_emit(eo_it2);
          }
     }
   return ECORE_CALLBACK_CANCEL;
}

/* returns true if change occurred */
static Eina_Bool
_item_position(Elm_Gen_Item *it,
               Evas_Object *view,
               Evas_Coord it_x,
               Evas_Coord it_y)
{
   Eina_Position2D pos;
   if (!it) return EINA_FALSE;
   if (!view) return EINA_FALSE;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Evas *e = evas_object_evas_get(sd->obj);

   evas_event_freeze(e);
   pos = efl_gfx_entity_position_get(view);
   efl_gfx_entity_geometry_set(view, EINA_RECT(it_x, it_y, it->item->w, it->item->h));
   evas_object_show(view);
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
   return (pos.x != it_x) || (pos.y != it_y);
}

static void
_item_tree_effect(Elm_Genlist_Data *sd,
                  int y)
{
   Elm_Gen_Item *expanded_next_it;
   Elm_Object_Item *eo_it;

   expanded_next_it = sd->expanded_next_item;

   if (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_EXPAND)
     {
        eo_it = elm_genlist_item_prev_get(EO_OBJ(expanded_next_it));
        while (eo_it)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
             if (it->item->expanded_depth <=
                 expanded_next_it->item->expanded_depth) break;
             if (it->item->scrl_y &&
                 (it->item->scrl_y <= expanded_next_it->item->old_scrl_y + y)
                 && (it->item->expanded_depth >
                     expanded_next_it->item->expanded_depth))
               {
                  if (!it->item->tree_effect_finished)
                    {
                       //XXX: for compat
                       edje_object_signal_emit(VIEW(it), "flip_item", "");
                       edje_object_signal_emit(VIEW(it), SIGNAL_FLIP_ITEM,
                                               "elm");
                       _item_position
                         (it, VIEW(it), it->item->scrl_x, it->item->scrl_y);
                       it->item->tree_effect_finished = EINA_TRUE;
                    }
               }
             eo_it = elm_genlist_item_prev_get(eo_it);
          }
     }
   else if (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_CONTRACT)
     {
        eo_it = elm_genlist_item_prev_get(EO_OBJ(expanded_next_it));
        while (eo_it)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
             if ((it->item->scrl_y > expanded_next_it->item->old_scrl_y + y) &&
                 (it->item->expanded_depth >
                  expanded_next_it->item->expanded_depth))
               {
                  if (!it->item->tree_effect_finished)
                    {
                       //XXX: for compat
                       edje_object_signal_emit(VIEW(it), SIGNAL_HIDE, "");
                       edje_object_signal_emit(VIEW(it), SIGNAL_HIDE, "elm");
                       it->item->tree_effect_finished = EINA_TRUE;
                    }
               }
             else
               break;
             eo_it = elm_genlist_item_prev_get(eo_it);
          }
     }
}

static void
_item_sub_items_clear(Elm_Gen_Item *it)
{
   Eina_List *tl = NULL, *l;
   Elm_Object_Item *eo_it2;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   EINA_LIST_FOREACH(it->item->items, l, eo_it2)
     tl = eina_list_append(tl, eo_it2);
   EINA_LIST_FREE(tl, eo_it2)
     efl_del(eo_it2);
}

static void
_item_auto_scroll(Elm_Genlist_Data *sd)
{
   Elm_Object_Item *eo_tmp_item = NULL;

   if ((sd->expanded_item) && (sd->auto_scroll_enabled))
     {
        eo_tmp_item = eina_list_data_get
            (eina_list_last(sd->expanded_item->item->items));
        if (!eo_tmp_item) return;
        ELM_GENLIST_ITEM_DATA_GET(eo_tmp_item, tmp_item);
        sd->show_item = tmp_item;
        sd->bring_in = EINA_TRUE;
        sd->scroll_to_type = ELM_GENLIST_ITEM_SCROLLTO_IN;
        if ((sd->show_item->item->queued) || (!sd->show_item->item->mincalcd))
          {
             sd->show_item->item->show_me = EINA_TRUE;
             sd->auto_scroll_enabled = EINA_FALSE;
          }
        else
          _item_scroll(sd);
     }
}

static void
_item_tree_effect_finish(Elm_Genlist_Data *sd)
{
   Elm_Object_Item *eo_it = NULL;
   const Eina_List *l;
   Item_Block *itb;
   Elm_Gen_Item *it1;
   Evas_Coord y = 0;

   if (sd->tree_effect_animator)
     {
        if (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_CONTRACT)
          _item_sub_items_clear(sd->expanded_item);

        EINA_LIST_FOREACH(sd->expanded_item->item->items, l, eo_it)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
             it->item->tree_effect_finished = EINA_TRUE;
             it->item->old_scrl_y = it->item->scrl_y;
             if (it->item->wsd->move_effect_mode ==
                 ELM_GENLIST_TREE_EFFECT_EXPAND)
               {
                  //XXX: for compat
                  edje_object_signal_emit(VIEW(it), SIGNAL_SHOW, "");
                  edje_object_signal_emit(VIEW(it), SIGNAL_SHOW, "elm");
               }
          }
        if (sd->move_effect_mode ==
            ELM_GENLIST_TREE_EFFECT_EXPAND)
          {
             EINA_INLIST_FOREACH(sd->blocks, itb)
               {
                  EINA_LIST_FOREACH(itb->items, l, it1)
                   {
                       if (it1->item->scrl_y <= y)
                         {
                            it1->item->scrl_y = y + it1->item->h;
                            _elm_genlist_item_unrealize(it1, EINA_FALSE);
                         }
                       y = it1->item->scrl_y;
                   }
               }
          }
     }

   _item_auto_scroll(sd);
   evas_object_lower(sd->event_block_rect);
   evas_object_hide(sd->event_block_rect);
   sd->move_effect_mode = ELM_GENLIST_TREE_EFFECT_NONE;
   sd->move_items = eina_list_free(sd->move_items);

   efl_event_callback_legacy_call(sd->pan_obj, ELM_PAN_EVENT_CHANGED, NULL);
   efl_event_callback_legacy_call
         (sd->obj, ELM_GENLIST_EVENT_TREE_EFFECT_FINISHED, NULL);
   evas_object_smart_changed(sd->pan_obj);

   _elm_genlist_tree_effect_stop(sd);
}

static void
_item_restack(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (it->item->nostacking) return;
   if ((it->item->order_num_in & 0x1) ^ it->item->stacking_even)
     {
        if (it->deco_all_view) evas_object_stack_below(it->deco_all_view, sd->stack[0]);
        else if (it->item->deco_it_view) evas_object_stack_below(it->item->deco_it_view, sd->stack[0]);
        else evas_object_stack_below(VIEW(it), sd->stack[0]);
     }
   else
     {
        if (it->deco_all_view) evas_object_stack_above(it->deco_all_view, sd->stack[0]);
        else if (it->item->deco_it_view) evas_object_stack_above(it->item->deco_it_view, sd->stack[0]);
        else evas_object_stack_above(VIEW(it), sd->stack[0]);
     }
}

static void
_elm_genlist_item_position_state_update(Elm_Gen_Item *it)
{
   unsigned idx = it->item->order_num_in;

   if (!VIEW(it) && !it->deco_all_view) return;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   _item_restack(it);

   if (idx & 0x1)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_ODD, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_ODD, "elm");
     }
   else
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_EVEN, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_EVEN, "elm");
     }

   if (sd->item_count == 1)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_LIST_SINGLE, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_LIST_SINGLE, "elm");
     }
   else if (idx == 0)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_LIST_FIRST, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_LIST_FIRST, "elm");
     }
   else if (idx == sd->item_count - 1)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_LIST_LAST, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_LIST_LAST, "elm");
     }
   else if (idx > 0)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_LIST_MIDDLE, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_LIST_MIDDLE, "elm");
     }

   if (it->parent)
     {
        unsigned first_idx = it->parent->item->order_num_in + 1;
        unsigned count = eina_list_count(it->parent->item->items);

        if (count == 1)
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_GROUP_SINGLE, "elm");
             if (it->deco_all_view)
               edje_object_signal_emit(it->deco_all_view, SIGNAL_GROUP_SINGLE,
                                       "elm");
          }
        else if (idx == first_idx)
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_GROUP_FIRST, "elm");
             if (it->deco_all_view)
               edje_object_signal_emit(it->deco_all_view, SIGNAL_GROUP_FIRST,
                                       "elm");
          }
        else if (EO_OBJ(it) == eina_list_data_get(eina_list_last(it->parent->item->items)))
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_GROUP_LAST, "elm");
             if (it->deco_all_view)
               edje_object_signal_emit(it->deco_all_view, SIGNAL_GROUP_LAST,
                                       "elm");
          }
        else if (idx > first_idx)
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_GROUP_MIDDLE, "elm");
             if (it->deco_all_view)
               edje_object_signal_emit(it->deco_all_view, SIGNAL_GROUP_MIDDLE,
                                       "elm");
          }
     }
}

static void
_item_order_update(Elm_Gen_Item *it, int index, Eina_Bool process)
{
   it->item->order_num_in = index;
   _elm_genlist_item_position_state_update(it);

   if (process) edje_object_message_signal_process(VIEW(it));
}

static void
_elm_genlist_item_state_update(Elm_Gen_Item *it)
{
   if (it->selected)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_SELECTED, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit
             (it->deco_all_view, SIGNAL_SELECTED, "elm");
     }
   if (elm_wdg_item_disabled_get(EO_OBJ(it)))
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_DISABLED, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit
             (it->deco_all_view, SIGNAL_DISABLED, "elm");
     }
   if (it->item->expanded)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_EXPANDED, "elm");
        if (it->deco_all_view)
          edje_object_signal_emit
             (it->deco_all_view, SIGNAL_EXPANDED, "elm");
     }
}

static void
_view_inflate(Evas_Object *view, Elm_Gen_Item *it, Eina_List **sources, Eina_List **contents, Eina_Bool calc)
{
   if (!view) return;
   if (sources) _item_text_realize(it, view, sources, NULL);
   if (contents) _item_content_realize(it, view, contents, "contents", NULL, calc);
   _item_state_realize(it, view, NULL);
}

static void
_elm_genlist_item_index_update(Elm_Gen_Item *it)
{
   if (it->position_update || it->item->block->position_update)
     {
        efl_event_callback_legacy_call
              (WIDGET(it), ELM_GENLIST_EVENT_INDEX_UPDATE, EO_OBJ(it));
        it->position_update = EINA_FALSE;
     }
}

static void
_decorate_all_item_position(Elm_Gen_Item *it,
                            int itx,
                            int ity)
{
   if ((!it) || (!it->item->wsd->decorate_all_mode)) return;
   efl_gfx_entity_geometry_set(it->deco_all_view, EINA_RECT(itx, ity, it->item->w, it->item->h));
}

static void
_decorate_all_item_realize(Elm_Gen_Item *it,
                           Eina_Bool effect_on)
{
   char buf[1024];
   const char *stacking;
   const char *stacking_even;

   if (!it) return;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (it->item->decorate_all_item_realized) return;

   it->deco_all_view = edje_object_add(evas_object_evas_get(WIDGET(it)));
   edje_object_scale_set(it->deco_all_view, efl_gfx_entity_scale_get(WIDGET(it)) *
                         elm_config_scale_get());
   evas_object_smart_member_add(it->deco_all_view, sd->pan_obj);
   elm_widget_sub_object_add(WIDGET(it), it->deco_all_view);

   if (it->item->type & ELM_GENLIST_ITEM_TREE)
     strncpy(buf, "tree", sizeof(buf));
   else strncpy(buf, "item", sizeof(buf));
   if (sd->mode == ELM_LIST_COMPRESS)
     strncat(buf, "_compress", sizeof(buf) - strlen(buf) - 1);

   strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
   strncat(buf, it->itc->decorate_all_item_style, sizeof(buf) - strlen(buf) - 1);

   elm_widget_theme_object_set(WIDGET(it), it->deco_all_view, "genlist", buf,
                               elm_widget_style_get(WIDGET(it)));

   stacking_even = edje_object_data_get(VIEW(it), "stacking_even");
   if (!stacking_even) stacking_even = "above";
   it->item->stacking_even = !!strcmp("above", stacking_even);

   stacking = edje_object_data_get(VIEW(it), "stacking");
   if (!stacking) stacking = "yes";
   it->item->nostacking = !!strcmp("yes", stacking);

   edje_object_mirrored_set
     (it->deco_all_view, efl_ui_mirrored_get(WIDGET(it)));

   _elm_genlist_item_position_state_update(it);
   _elm_genlist_item_state_update(it);

   if (it->item->wsd->reorder_mode)
     {
        edje_object_signal_emit(it->deco_all_view, SIGNAL_REORDER_MODE_UNSET,
                                "elm");
     }

   if (effect_on)
     edje_object_signal_emit
        (it->deco_all_view, SIGNAL_DECORATE_ENABLED_EFFECT, "elm");
   else
     edje_object_signal_emit
        (it->deco_all_view, SIGNAL_DECORATE_ENABLED, "elm");

   _item_mouse_callbacks_del(it, VIEW(it));
   _item_mouse_callbacks_add(it, it->deco_all_view);

   if (it->flipped)
     edje_object_signal_emit
       (it->deco_all_view, SIGNAL_FLIP_ENABLED, "elm");
   _view_inflate(it->deco_all_view, it, NULL, &(it->item->deco_all_contents), EINA_FALSE);
   edje_object_part_swallow
     (it->deco_all_view, "elm.swallow.decorate.content", VIEW(it));

   _decorate_all_item_position(it, it->item->scrl_x, it->item->scrl_y);
   evas_object_show(it->deco_all_view);

   if (it->selected)
     edje_object_signal_emit(it->deco_all_view, SIGNAL_SELECTED, "elm");

   it->item->decorate_all_item_realized = EINA_TRUE;
   it->want_unrealize = EINA_FALSE;
}

//-- tree expand/contract signal handle routine --//
static void
_expand_toggle_signal_cb(void *data,
                         Evas_Object *obj EINA_UNUSED,
                         const char *emission EINA_UNUSED,
                         const char *source EINA_UNUSED)
{
   Elm_Gen_Item *it = data;

   if (it->item->expanded)
     efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_CONTRACT_REQUEST, EO_OBJ(it));
   else
     efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_EXPAND_REQUEST, EO_OBJ(it));
}

static void
_expand_signal_cb(void *data,
                  Evas_Object *obj EINA_UNUSED,
                  const char *emission EINA_UNUSED,
                  const char *source EINA_UNUSED)
{
   Elm_Gen_Item *it = data;

   if (!it->item->expanded)
     efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_EXPAND_REQUEST, EO_OBJ(it));
}

static void
_contract_signal_cb(void *data,
                    Evas_Object *obj EINA_UNUSED,
                    const char *emission EINA_UNUSED,
                    const char *source EINA_UNUSED)
{
   Elm_Gen_Item *it = data;

   if (it->item->expanded)
     efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_CONTRACT_REQUEST, EO_OBJ(it));
}

//-- item cache handle routine --//
// push item cache into caches
static Eina_Bool
_item_cache_push(Elm_Genlist_Data *sd, Item_Cache *itc)
{
   if (!itc || (sd->item_cache_max <= 0))
     return EINA_FALSE;

   sd->item_cache_count++;
   sd->item_cache =
     eina_inlist_prepend(sd->item_cache, EINA_INLIST_GET(itc));

   return EINA_TRUE;
}

// pop item cache from caches
static Item_Cache *
_item_cache_pop(Elm_Genlist_Data *sd, Item_Cache *itc)
{
   if (!itc || (!sd->item_cache) ||
       (sd->item_cache_count <= 0))
     return NULL;

   sd->item_cache =
     eina_inlist_remove (sd->item_cache, EINA_INLIST_GET(itc));
   sd->item_cache_count--;

   return itc;
}

// free one item cache
static void
_item_cache_free(Item_Cache *itc)
{
   Evas_Object *c;

   if (!itc) return;

   evas_object_del(itc->spacer);
   /* does not exist if cache item has just been reused */
   if (itc->base_view)
     {
        Evas_Object *view = itc->base_view;
        efl_wref_del(itc->base_view, &itc->base_view);
        efl_del(view);
        itc->base_view = NULL;
     }
   itc->item_class = NULL;
   EINA_LIST_FREE(itc->contents, c)
     {
        evas_object_del(c);
     }
   ELM_SAFE_FREE(itc, free);
}

// clean up item cache by removing overflowed caches
static void
_item_cache_clean(Elm_Genlist_Data *sd)
{
   Evas *e;

   if (!sd->obj) return;
   e = evas_object_evas_get(sd->obj);
   evas_event_freeze(e);

   while ((sd->item_cache) && (sd->item_cache_count > sd->item_cache_max))
     {
        Item_Cache *itc =
           EINA_INLIST_CONTAINER_GET(sd->item_cache->last, Item_Cache);
        _item_cache_free(_item_cache_pop(sd, itc));
     }
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

// empty all item caches
static void
_item_cache_zero(Elm_Genlist_Data *sd)
{
   int pmax = sd->item_cache_max;

   sd->item_cache_max = 0;
   _item_cache_clean(sd);
   sd->item_cache_max = pmax;
}

// add an item to item cache
static Eina_Bool
_item_cache_add(Elm_Gen_Item *it, Eina_List *contents)
{
   if (it->item->nocache_once || it->item->nocache) return EINA_FALSE;

   Item_Cache *itc = NULL;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Evas_Object *obj = sd->obj;
   Evas_Object *win = efl_provider_find(obj, EFL_UI_WIN_CLASS);
   Evas *e = evas_object_evas_get(obj);

   evas_event_freeze(e);

   if (sd->item_cache_max > 0)
     itc = ELM_NEW(Item_Cache);

   if (!_item_cache_push(sd, itc))
     {
        if (itc) ELM_SAFE_FREE(itc, free);

        evas_event_thaw(e);
        evas_event_thaw_eval(e);
        return EINA_FALSE;
     }
   itc->spacer = it->spacer;
   efl_wref_add(VIEW(it), &itc->base_view);
   itc->item_class = it->itc;
   itc->contents = contents;
   if (it->item->type & ELM_GENLIST_ITEM_TREE)
     {
        itc->tree = 1;
        if (it->item->expanded)
          edje_object_signal_emit(itc->base_view, SIGNAL_CONTRACTED, "elm");
     }

   if (it->selected)
     edje_object_signal_emit(itc->base_view, SIGNAL_UNSELECTED, "elm");
   if (elm_wdg_item_disabled_get(EO_OBJ(it)))
     edje_object_signal_emit(itc->base_view, SIGNAL_ENABLED, "elm");
   if ((EO_OBJ(it) == sd->focused_item) &&
       (elm_win_focus_highlight_enabled_get(win) || _elm_config->win_auto_focus_enable))
     edje_object_signal_emit(itc->base_view, SIGNAL_UNFOCUSED, "elm");

   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
   ELM_SAFE_FREE(it->item->swipe_timer, ecore_timer_del);

   // FIXME: other callbacks?
   edje_object_signal_callback_del_full
     (itc->base_view, "elm,action,expand,toggle", "elm",
     _expand_toggle_signal_cb, it);
   edje_object_signal_callback_del_full
     (itc->base_view, "elm,action,expand", "elm", _expand_signal_cb, it);
   edje_object_signal_callback_del_full
     (itc->base_view, "elm,action,contract", "elm", _contract_signal_cb, it);
   _item_mouse_callbacks_del(it, itc->base_view);

   edje_object_mirrored_set(itc->base_view,
                            efl_ui_mirrored_get(WIDGET(it)));
   edje_object_scale_set(itc->base_view,
                         efl_gfx_entity_scale_get(WIDGET(it))
                         * elm_config_scale_get());

   it->spacer = NULL;
   efl_wref_del(it->base->view, &it->base->view);
   VIEW(it) = NULL;
   evas_object_hide(itc->base_view);
// lower eocalloverhead - no need to do this
//   evas_object_move(itc->base_view, -9999, -9999);

   _item_cache_clean(sd);

   evas_event_thaw(e);
   evas_event_thaw_eval(e);

   return EINA_TRUE;
}

// find an item from item cache and remove it from the cache
static Eina_Bool
_item_cache_find(Elm_Gen_Item *it)
{
   if (it->item->nocache_once || it->item->nocache) return EINA_FALSE;

   Item_Cache *itc = NULL;
   Eina_Inlist *l;
   Eina_Bool tree = 0;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (it->item->type & ELM_GENLIST_ITEM_TREE) tree = 1;
   EINA_INLIST_FOREACH_SAFE(sd->item_cache, l, itc)
     {
        Evas_Object *obj;

        if ((itc->tree == tree) &&
            (((!it->itc) && (!itc->item_class)) ||
             (it->itc && itc->item_class &&
              (it->itc == itc->item_class))))
          {
             itc = _item_cache_pop(sd, itc);
             if (!itc) continue;

             it->spacer = itc->spacer;
             VIEW_SET(it, itc->base_view);
             itc->spacer = NULL;
             efl_wref_del(itc->base_view, &itc->base_view);
             itc->base_view = NULL;
             EINA_LIST_FREE(itc->contents, obj)
               {
                  if (elm_widget_is(obj))
                    elm_widget_tree_unfocusable_set(obj, it->item->unfocusable);
                  evas_object_show(obj);
               }
             itc->contents = NULL;
             _item_cache_free(itc);
             return EINA_TRUE;
          }
     }
   return EINA_FALSE;
}

static Eina_List *
_content_cache_add(Elm_Gen_Item *it, Eina_List **cache)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, pd);
   Evas_Object *content = NULL;
   EINA_LIST_FREE(it->contents, content)
     {
        *cache = eina_list_append(*cache, content);
        eina_hash_del_by_key(pd->content_item_map, &content);
        if (elm_widget_is(content)) elm_widget_tree_unfocusable_set(content, EINA_TRUE);
        evas_object_hide(content);
     }

   return *cache;
}

static char *
_access_info_cb(void *data, Evas_Object *obj EINA_UNUSED)
{
   char *ret;
   Eina_Strbuf *buf;

   Elm_Gen_Item *it = (Elm_Gen_Item *)data;
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, NULL);

   buf = eina_strbuf_new();

   if (it->itc->func.text_get)
     {
        const Eina_List *l;
        const char *key;

        if (!(it->texts)) it->texts =
          elm_widget_stringlist_get(edje_object_data_get(VIEW(it), "texts"));

        EINA_LIST_FOREACH(it->texts, l, key)
          {
             char *s = it->itc->func.text_get
                ((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), key);

             if (s)
               {
                  if (eina_strbuf_length_get(buf) > 0) eina_strbuf_append(buf, ", ");
                  eina_strbuf_append(buf, s);
                  free(s);
               }
          }
     }

   ret = eina_strbuf_string_steal(buf);
   eina_strbuf_free(buf);
   return ret;
}

static char *
_access_state_cb(void *data, Evas_Object *obj EINA_UNUSED)
{
   Elm_Gen_Item *it = (Elm_Gen_Item *)data;
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, NULL);

   if (it->base->disabled)
     return strdup(E_("State: Disabled"));

   return NULL;
}

static void
_access_on_highlight_cb(void *data)
{
   Evas_Coord x, y, w, h;
   Evas_Coord sx, sy, sw, sh;
   Elm_Gen_Item *it = (Elm_Gen_Item *)data;
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   evas_object_geometry_get(it->base->view, &x, &y, &w, &h);
   // XXX There would be a reason.
   if ((w == 0) && (h == 0)) return;

   evas_object_geometry_get(it->base->widget, &sx, &sy, &sw, &sh);
   if ((x < sx) || (y < sy) || ((x + w) > (sx + sw)) || ((y + h) > (sy + sh)))
     elm_genlist_item_bring_in(EO_OBJ(it),
                               ELM_GENLIST_ITEM_SCROLLTO_IN);
}

static void
_access_widget_item_register(Elm_Gen_Item *it)
{
   Elm_Access_Info *ai;

   _elm_access_widget_item_register(it->base);

   ai = _elm_access_info_get(it->base->access_obj);

   _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, it);
   _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, it);
   _elm_access_on_highlight_hook_set(ai, _access_on_highlight_cb, it);
   _elm_access_activate_callback_set(ai, _access_activate_cb, EO_OBJ(it));
}

static void
_elm_genlist_item_focus_update(Elm_Gen_Item *it)
{
   const char *focus_raise;
   Evas_Object *obj = WIDGET(it);
   Evas_Object *win = efl_provider_find(obj, EFL_UI_WIN_CLASS);
   ELM_GENLIST_DATA_GET(obj, sd);

   if (elm_win_focus_highlight_enabled_get(win)
       || _elm_config->win_auto_focus_enable)
     edje_object_signal_emit(VIEW(it), SIGNAL_FOCUSED, "elm");

   focus_raise = edje_object_data_get(VIEW(it), "focusraise");
   if ((focus_raise) && (!strcmp(focus_raise, "on")))
     {
        Elm_Gen_Item *git;
        Eina_List *l;

        evas_object_raise(VIEW(it));
        EINA_LIST_FOREACH(sd->group_items, l, git)
          {
             if (git->realized) evas_object_raise(VIEW(git));
          }
     }
}

static void
_item_realize(Elm_Gen_Item *it, const int index, Eina_Bool calc)
{
   const char *treesize;
   Item_Size *size = NULL;
   int tsize = 20;
   int in = index;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (it->realized)
     {
        if (it->item->order_num_in != in)
          {
             _item_order_update(it, in, EINA_TRUE);
             _elm_genlist_item_index_update(it);
          }
        return;
     }

   if (sd->tree_effect_enabled ||
       (!_item_cache_find(it)))
     {
        VIEW_SET(it, _view_create(it, it->itc->item_style));
        if (it->item->nocache_once)
          it->item->nocache_once = EINA_FALSE;
     }

   /* access */
   if (_elm_config->access_mode) _access_widget_item_register(it);

   _item_order_update(it, in, EINA_FALSE);

   if (sd->reorder_mode)
     edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_MODE_SET, "elm");
   else
     edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_MODE_UNSET, "elm");

   treesize = edje_object_data_get(VIEW(it), "treesize");
   if (treesize) tsize = atoi(treesize);

   if (edje_object_part_exists(VIEW(it), "elm.swallow.pad"))
     {
        if (!it->spacer && treesize)
          {
             it->spacer =
                evas_object_rectangle_add(evas_object_evas_get(WIDGET(it)));
             evas_object_color_set(it->spacer, 0, 0, 0, 0);
             elm_widget_sub_object_add(WIDGET(it), it->spacer);
          }

        evas_object_size_hint_min_set
           (it->spacer, (it->item->expanded_depth * tsize) *
            elm_config_scale_get(), 1);
        edje_object_part_swallow(VIEW(it), "elm.swallow.pad", it->spacer);
     }
   else
     {
        ELM_SAFE_FREE(it->spacer, evas_object_del);
     }

   if (!calc)
     {
        edje_object_signal_callback_add
          (VIEW(it), "elm,action,expand,toggle", "elm",
          _expand_toggle_signal_cb, it);
        edje_object_signal_callback_add
          (VIEW(it), "elm,action,expand", "elm", _expand_signal_cb, it);
        edje_object_signal_callback_add
          (VIEW(it), "elm,action,contract", "elm", _contract_signal_cb, it);
        _item_mouse_callbacks_add(it, VIEW(it));

        if ((sd->decorate_all_mode) && (!it->deco_all_view) &&
            (it->item->type != ELM_GENLIST_ITEM_GROUP) &&
            (it->itc->decorate_all_item_style))
          _decorate_all_item_realize(it, EINA_FALSE);

        _elm_genlist_item_state_update(it);
        _elm_genlist_item_index_update(it);

        if (EO_OBJ(it) == sd->focused_item)
          {
             _elm_genlist_item_focus_update(it);
             _elm_widget_item_highlight_in_theme(WIDGET(it), EO_OBJ(it));
             _elm_widget_highlight_in_theme_update(WIDGET(it));
             _elm_widget_focus_highlight_start(WIDGET(it));
          }
     }

   size = eina_hash_find(sd->size_caches, &(it->itc));
   /* homogeneous genlist shortcut */
   if ((calc) && (sd->homogeneous) && (!it->item->mincalcd) && size)
     {
        it->item->w = it->item->minw = size->minw;
        it->item->h = it->item->minh = size->minh;
        size->expanded_depth = it->item->expanded_depth;
        it->item->mincalcd = EINA_TRUE;
     }
   else
     {
        if (eina_list_count(it->contents) != 0)
          ERR_ABORT("If you see this error, please notify us and we"
                    "will fix it");

        _view_inflate(VIEW(it), it, &it->texts, &it->contents, calc);
        if (it->has_contents != (!!it->contents))
          it->item->mincalcd = EINA_FALSE;
        it->has_contents = !!it->contents;
        if (it->flipped)
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_FLIP_ENABLED, "elm");
             _item_content_realize(it, VIEW(it), &it->item->flip_contents,
                                   "flips", NULL, EINA_FALSE);
          }

        /* access: unregister item which have no text and content */
        if (_elm_config->access_mode && !it->texts && !it->contents)
          _elm_access_widget_item_unregister(it->base);

        if (!it->item->mincalcd)
          {
             if (sd->homogeneous && size)
               {
                  it->item->w = it->item->minw = size->minw;
                  it->item->h = it->item->minh = size->minh;
                  it->item->mincalcd = EINA_TRUE;
                  if (it->item->block->realized)
                    it->item->w = it->item->block->w;
               }
             else
               {
                  Evas_Coord mw = -1, mh = -1;

                  if (it->select_mode != ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY)
                    elm_coords_finger_size_adjust(1, &mw, 1, &mh);
                  if (sd->mode == ELM_LIST_COMPRESS)
                    mw = sd->prev_viewport_w;
                  // Process signal for proper size calc with text and content visible.
                  edje_object_message_signal_process(VIEW(it));
                  edje_object_size_min_restricted_calc(VIEW(it), &mw, &mh, mw, mh);
                  it->item->w = it->item->minw = mw;
                  it->item->h = it->item->minh = mh;
                  it->item->mincalcd = EINA_TRUE;

                  if (sd->homogeneous)
                    {
                       if (size)
                         eina_hash_del_by_key(sd->size_caches, &(it->itc));

                       size = ELM_NEW(Item_Size);
                       size->itc = it->itc;
                       size->expanded_depth = it->item->expanded_depth;
                       size->minw = mw;
                       size->minh = mh;
                       eina_hash_add(sd->size_caches, &(it->itc), size);
                    }
               }
          }
        if (!calc) evas_object_show(VIEW(it));
     }

   if (it->tooltip.content_cb)
     {
        elm_wdg_item_tooltip_content_cb_set(EO_OBJ(it), it->tooltip.content_cb, it->tooltip.data, NULL);
        elm_wdg_item_tooltip_style_set(EO_OBJ(it), it->tooltip.style);
        elm_wdg_item_tooltip_window_mode_set(EO_OBJ(it), it->tooltip.free_size);
     }

   if (it->mouse_cursor)
     elm_wdg_item_cursor_set(EO_OBJ(it), it->mouse_cursor);

   it->realized = EINA_TRUE;
   it->want_unrealize = EINA_FALSE;

   if (!calc)
     {
        if (it->item->tree_effect_hide_me)
          {
             if (sd->move_effect_mode
                 != ELM_GENLIST_TREE_EFFECT_NONE)
               {
                  //XXX: for compat
                  edje_object_signal_emit(VIEW(it), SIGNAL_HIDE, "");
                  edje_object_signal_emit(VIEW(it), SIGNAL_HIDE, "elm");
               }
             it->item->tree_effect_hide_me = EINA_FALSE;
          }

        if (it->item->type == ELM_GENLIST_ITEM_NONE)
          {
             Evas_Object* eobj;
             Eina_List* l;
             EINA_LIST_FOREACH(it->contents, l, eobj)
                if (elm_widget_is(eobj) && elm_object_focus_allow_get(eobj))
                  it->item_focus_chain = eina_list_append
                      (it->item_focus_chain, eobj);

          }

        if (it->item->type == ELM_GENLIST_ITEM_TREE)
          {
             Evas_Object* t_eobj;
             Eina_List* tl;
             EINA_LIST_FOREACH(it->contents, tl, t_eobj)
                if (elm_widget_is(t_eobj) && elm_object_focus_allow_get(t_eobj))
                  it->item_focus_chain = eina_list_append
                      (it->item_focus_chain, t_eobj);

          }

        efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_REALIZED, EO_OBJ(it));
        if (it->base->func.realized) it->base->func.realized(EO_OBJ(it));
     }

   //Send a signal so that an item changes its style according to its expand depth
   if (it->item->expanded_depth > 0)
     {
        char buf[126];
        snprintf(buf, sizeof(buf), "elm,state,expanded_depth,%d",
                 it->item->expanded_depth);
        edje_object_signal_emit(VIEW(it), buf, "elm");
     }

   if ((!calc) && (sd->decorate_all_mode) &&
       (it->item->type != ELM_GENLIST_ITEM_GROUP))
     {
        if (it->itc->decorate_all_item_style)
          {
             if (!it->deco_all_view)
               _decorate_all_item_realize(it, EINA_FALSE);
             edje_object_message_signal_process(it->deco_all_view);
          }
     }

   if (it->decorate_it_set) _decorate_item_set(it);

   edje_object_message_signal_process(VIEW(it));

   if (sd->focus_on_realization == it)
     {
        _elm_widget_item_highlight_in_theme(WIDGET(it), EO_OBJ(it));
        _elm_widget_highlight_in_theme_update(WIDGET(it));
        _elm_widget_focus_highlight_start(WIDGET(it));
        efl_ui_focus_manager_focus_set(WIDGET(it), EO_OBJ(it));
        sd->focus_on_realization = NULL;
     }
}

static void
_tree_effect_animator_cb(void *data, const Efl_Event *event EINA_UNUSED)
{
   int in = 0;
   const Eina_List *l;
   int y = 0, dy = 0, dh = 0;
   double effect_duration = 0.3, t;
   ELM_GENLIST_DATA_GET(data, sd);
   Eina_Bool end = EINA_FALSE, vis = EINA_TRUE;
   Evas_Coord ox, oy, ow, oh, cvx, cvy, cvw, cvh;
   Elm_Gen_Item *it = NULL, *it2, *expanded_next_it;
   Elm_Object_Item *eo_it = NULL, *eo_it2;

   t = ((0.0 > (t = ecore_time_get() - sd->start_time)) ? 0.0 : t);
   evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);
   evas_output_viewport_get
     (evas_object_evas_get(sd->pan_obj), &cvx, &cvy, &cvw, &cvh);
   if (t > effect_duration) end = EINA_TRUE;

   // Below while statement is needed, when the genlist is resized.
   it2 = sd->expanded_item;
   eo_it2 = EO_OBJ(it2);
   while (eo_it2 && vis)
     {
        it2 = efl_data_scope_get(eo_it2, ELM_GENLIST_ITEM_CLASS);
        evas_object_move(VIEW(it2), it2->item->scrl_x, it2->item->scrl_y);
        vis = (ELM_RECTS_INTERSECT(it2->item->scrl_x, it2->item->scrl_y,
                                   it2->item->w, it2->item->h, cvx, cvy, cvw,
                                   cvh));
        eo_it2 = elm_genlist_item_prev_get(eo_it2);
     }

   if (sd->expanded_next_item)
     {
        expanded_next_it = sd->expanded_next_item;

        /* move items */
        EINA_LIST_FOREACH(sd->move_items, l, it)
          {
             if (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_EXPAND)
               {
                  expanded_next_it->item->old_scrl_y =
                    sd->expanded_item->item->old_scrl_y
                    + sd->expanded_item->item->h;
                  if (expanded_next_it->item->scrl_y <=
                      expanded_next_it->item->old_scrl_y) /* did not
                                                           * calculate
                                                           * next item
                                                           * position */
                    expanded_next_it->item->scrl_y = cvy + cvh;

                  dy = ((expanded_next_it->item->scrl_y >= (cvy + cvh)) ?
                        cvy + cvh : expanded_next_it->item->scrl_y) -
                    expanded_next_it->item->old_scrl_y;
               }
             else if (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_CONTRACT)
               {
                  if (expanded_next_it->item->scrl_y >
                      expanded_next_it->item->old_scrl_y) /* did not
                                                           * calculate
                                                           * next item
                                                           * position */
                    expanded_next_it->item->old_scrl_y = cvy + cvh;

                  if (expanded_next_it->item->old_scrl_y > (cvy + cvh))
                    {
                       dy = (sd->expanded_item->item->scrl_y +
                             sd->expanded_item->item->h) - cvy + cvh;
                       expanded_next_it->item->old_scrl_y = cvy + cvh;
                    }
                  else
                    {
                       dy = (sd->expanded_item->item->scrl_y +
                             sd->expanded_item->item->h) -
                         expanded_next_it->item->old_scrl_y;
                    }
               }

             if (t <= effect_duration)
               {
                  y = ((1 - (1 - (t / effect_duration)) *
                        (1 - (t / effect_duration))) * dy);
               }
             else
               {
                  end = EINA_TRUE;
                  y = dy;
               }

             if (!it->realized && !it->item->queued)
               _item_realize(it, in, 0);
             if (!it->hide) in++;

             if (it != expanded_next_it)
               {
                  it->item->old_scrl_y =
                    expanded_next_it->item->old_scrl_y +
                    expanded_next_it->item->h + dh;

                  dh += it->item->h;
               }

             if ((it->item->old_scrl_y + y) < (cvy + cvh))
               _item_position(it, VIEW(it), it->item->scrl_x,
                              it->item->old_scrl_y + y);
          }
        /* tree effect */
        _item_tree_effect(sd, y);
     }
   else
     {
        int expanded_item_num = 0;
        int num = 0;

        if (sd->expanded_item)
          eo_it = elm_genlist_item_next_get(EO_OBJ(sd->expanded_item));

        eo_it2 = eo_it;
        while (eo_it2)
          {
             expanded_item_num++;
             eo_it2 = elm_genlist_item_next_get(eo_it2);
          }

        while (eo_it)
          {
             it = efl_data_scope_get(eo_it, ELM_GENLIST_ITEM_CLASS);
             num++;
             if (sd->expanded_item->item->expanded_depth >=
                 it->item->expanded_depth) break;
             if (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_EXPAND)
               {
                  if (!it->item->tree_effect_finished)
                    {
                       if (t >= (((num - 1) * effect_duration) /
                                 expanded_item_num))
                         {
                            //XXX: for compat
                            edje_object_signal_emit(VIEW(it), "flip_item", "");
                            edje_object_signal_emit(VIEW(it), SIGNAL_FLIP_ITEM,
                                                    "elm");
                            _item_position(it, VIEW(it), it->item->scrl_x,
                                           it->item->scrl_y);
                            it->item->tree_effect_finished = EINA_TRUE;
                         }
                    }
               }
             eo_it = elm_genlist_item_next_get(eo_it);
          }
     }

   if (end)
     {
        _item_tree_effect_finish(sd);
        _elm_genlist_tree_effect_stop(sd);
     }
}

static void
_group_items_recalc(void *data)
{
   Eina_List *l;
   Elm_Gen_Item *git;
   Elm_Genlist_Data *sd = data;
   Evas_Coord vy;
   Evas *e = evas_object_evas_get(sd->obj);

   evas_event_freeze(e);
   EINA_LIST_FOREACH(sd->group_items, l, git)
     {
        if (git->item->want_realize)
          {
             if (!git->realized) _item_realize(git, git->item->order_num_in, EINA_FALSE);
             if (sd->pin_item && git == sd->pin_item->item->group_item &&
                 sd->pin_item->item->scrl_y <= (git->item->scrl_y + git->item->h))
               {
                  elm_interface_scrollable_content_viewport_geometry_get
                    (sd->obj, NULL, &vy, NULL, NULL);
                  if ((git->item->scrl_y + git->item->h) > vy)
                    {
                       sd->pin_item->item->scrl_y = git->item->scrl_y + git->item->h;
                       evas_object_move(VIEW(sd->pin_item),
                                        sd->pin_item->item->scrl_x, sd->pin_item->item->scrl_y);
                    }
               }
             else if (sd->pin_item && sd->pin_item_top && git != sd->pin_item->item->group_item &&
                      (git->item->scrl_y < (sd->pin_item->item->scrl_y + sd->pin_item->item->h)))
               git->item->scrl_y = sd->pin_item->item->scrl_y + sd->pin_item->item->h;

             efl_gfx_entity_geometry_set(VIEW(git), EINA_RECT(git->item->scrl_x, git->item->scrl_y, sd->minw, git->item->h));
             evas_object_stack_above(VIEW(git), sd->stack[1]);
             evas_object_show(VIEW(git));
          }
        else if (!git->item->want_realize && git->realized)
          {
             if (!git->dragging)
               _elm_genlist_item_unrealize(git, EINA_FALSE);
          }
     }
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static Eina_Bool
_reorder_move_animator_cb(void *data)
{
   double t;
   Elm_Gen_Item *it = data;
   Eina_Bool down = EINA_FALSE;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   int y, dy = it->item->h / 10 * elm_config_scale_get(), diff;

   t = ((0.0 > (t = ecore_loop_time_get()
                  - sd->start_time)) ? 0.0 : t);

   if (t <= REORDER_EFFECT_TIME)
     y = (1 * sin((t / REORDER_EFFECT_TIME) * (M_PI / 2)) * dy);
   else y = dy;

   diff = abs(it->item->old_scrl_y - it->item->scrl_y);
   if (diff < dy) y = diff;
   else if (diff > it->item->h) y = diff / 2;

   if (it->item->old_scrl_y < it->item->scrl_y)
     {
        it->item->old_scrl_y += y;
        down = EINA_TRUE;
     }
   else if (it->item->old_scrl_y > it->item->scrl_y)
     {
        it->item->old_scrl_y -= y;
        down = EINA_FALSE;
     }

   if (it->deco_all_view)
     _item_position
       (it, it->deco_all_view, it->item->scrl_x, it->item->old_scrl_y);
   else
     _item_position(it, VIEW(it), it->item->scrl_x, it->item->old_scrl_y);
   _group_items_recalc(sd);

   if ((sd->reorder_pan_move) ||
       (down && it->item->old_scrl_y >= it->item->scrl_y) ||
       (!down && it->item->old_scrl_y <= it->item->scrl_y))
     {
        it->item->old_scrl_y = it->item->scrl_y;
        it->item->move_effect_enabled = EINA_FALSE;
        sd->reorder_move_animator = NULL;
        return ECORE_CALLBACK_CANCEL;
     }

   return ECORE_CALLBACK_RENEW;
}

static int
_reorder_item_space_get(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Elm_Gen_Item *reorder_it = sd->reorder_it;
   Evas_Coord rox, roy, row, roh, oy, oh;
   Eina_Bool top = EINA_FALSE;

   if (!reorder_it) return 0;

   evas_object_geometry_get(sd->pan_obj, NULL, &oy, NULL, &oh);
   evas_object_geometry_get
     (sd->VIEW(reorder_it), &rox, &roy, &row, &roh);

   if ((sd->reorder_start_y < it->item->block->y) &&
       (roy - oy + (roh / 2) >= it->item->block->y - sd->pan_y))
     {
        it->item->block->reorder_offset =
          sd->reorder_it->item->h * -1;
        if (it->item->block->count == 1)
          sd->reorder_rel = it;
     }
   else if ((sd->reorder_start_y >= it->item->block->y) &&
            (roy - oy + (roh / 2) <= it->item->block->y - sd->pan_y))
     {
        it->item->block->reorder_offset = sd->reorder_it->item->h;
     }
   else
     it->item->block->reorder_offset = 0;

   it->item->scrl_y += it->item->block->reorder_offset;

   top = (ELM_RECTS_INTERSECT
            (it->item->scrl_x, it->item->scrl_y, it->item->w, it->item->h,
            rox, roy + (roh / 2), row, 1));
   if (top)
     {
        sd->reorder_rel = it;
        it->item->scrl_y += sd->reorder_it->item->h;
        return sd->reorder_it->item->h;
     }
   else
     return 0;
}

static void
_pin_item_recalc(Elm_Gen_Item *it)
{
   Evas_Coord vx, vy, vw, vh;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->pin_item_top = EINA_FALSE;
   elm_interface_scrollable_content_viewport_geometry_get
     (sd->obj, &vx, &vy, &vw, &vh);
   if (it->item->scrl_x < vx)
     it->item->scrl_x = vx;
   else if (it->item->scrl_x + it->item->w > vx + vw)
     it->item->scrl_x = vx + vw - it->item->w;

   if (it->item->scrl_y < vy)
   {
      sd->pin_item_top = EINA_TRUE;
      it->item->scrl_y = vy;
   }
   else if (it->item->scrl_y + it->item->h > vy + vh)
     it->item->scrl_y = vy + vh - it->item->h;

   efl_gfx_entity_geometry_set(VIEW(it), EINA_RECT(it->item->scrl_x, it->item->scrl_y, it->item->w, it->item->h));
   evas_object_show(VIEW(it));
}

static void
_item_block_position(Item_Block *itb, const int blk_idx)
{
   Elm_Gen_Item *it;
   Elm_Gen_Item *git;
   const Eina_List *l;
   Eina_Bool vis = EINA_FALSE;
   Evas_Coord y = 0, ox, oy, ow, oh, cvx, cvy, cvw, cvh;
   Elm_Genlist_Data *sd = NULL;
   int vis_count = 0;
   Evas *e = evas_object_evas_get((itb->sd)->obj);

   evas_event_freeze(e);
   evas_object_geometry_get(itb->sd->pan_obj, &ox, &oy, &ow, &oh);
   evas_output_viewport_get(e, &cvx, &cvy, &cvw, &cvh);

   EINA_LIST_FOREACH(itb->items, l, it)
     {
        sd = it->item->wsd;
        if (sd->reorder_it == it) continue;

        if (it->itc->func.filter_get)
          _item_filtered_get(it, sd);
        if (it->hide)
          {
             if (it->realized) evas_object_hide(VIEW(it));
             continue;
          }
        it->x = 0;
        it->y = y;
        it->item->w = itb->w;
        it->item->scrl_x = itb->x + it->x - sd->pan_x + ox;
        it->item->scrl_y = itb->y + it->y - sd->pan_y + oy;

        vis = (ELM_RECTS_INTERSECT
                 (it->item->scrl_x, it->item->scrl_y, it->item->w, it->item->h,
                 cvx, cvy, cvw, cvh));
        if (!(it->item->type & ELM_GENLIST_ITEM_GROUP))
          {
             if ((itb->realized) && (!it->realized))
               {
                  if (vis) _item_realize(it, blk_idx + vis_count, EINA_FALSE);
               }
             if ((blk_idx + vis_count) != it->item->order_num_in)
               {
                  _item_order_update(it, blk_idx + vis_count, EINA_TRUE);
               }
             if (it->realized)
               {
                  if (vis || it->dragging)
                    {
                       if (sd->reorder_mode)
                         y += _reorder_item_space_get(it);
                       git = it->item->group_item;
                       if (git)
                         {
                            if (git->item->scrl_y < oy)
                              git->item->scrl_y = oy;
                            if ((git->item->scrl_y + git->item->h) >
                                (it->item->scrl_y + it->item->h))
                              git->item->scrl_y = (it->item->scrl_y +
                                                   it->item->h) - git->item->h;
                            git->item->scrl_x = it->item->scrl_x;
                            git->item->want_realize = EINA_TRUE;
                         }
                       if ((sd->reorder_it) &&
                           (it->item->old_scrl_y != it->item->scrl_y))
                         {
                            if (!it->item->move_effect_enabled)
                              {
                                 it->item->move_effect_enabled = EINA_TRUE;
                                 sd->reorder_move_animator =
                                   ecore_evas_animator_add(e,
                                     _reorder_move_animator_cb, it);
                              }
                         }
                       if (!it->item->move_effect_enabled)
                         {
                            if ((sd->decorate_all_mode) &&
                                (it->itc->decorate_all_item_style))
                              _decorate_all_item_position
                                (it, it->item->scrl_x, it->item->scrl_y);
                            else
                              {
                                 if (!sd->tree_effect_enabled ||
                                     (sd->move_effect_mode ==
                                      ELM_GENLIST_TREE_EFFECT_NONE) ||
                                     ((sd->move_effect_mode !=
                                       ELM_GENLIST_TREE_EFFECT_NONE) &&
                                      (it->item->old_scrl_y ==
                                       it->item->scrl_y)))
                                   {
                                      if (it->item->deco_it_view)
                                        _item_position
                                          (it, it->item->deco_it_view,
                                          it->item->scrl_x,
                                          it->item->scrl_y);
                                      else
                                        _item_position
                                          (it, VIEW(it), it->item->scrl_x,
                                          it->item->scrl_y);
                                   }
                              }
                            it->item->old_scrl_y = it->item->scrl_y;
                         }
                    }
                  else
                    {
                       if (!sd->tree_effect_animator && (it != sd->pin_item))
                         _elm_genlist_item_unrealize(it, EINA_FALSE);
                    }
               }
          }
        else
          {
             if (vis) it->item->want_realize = EINA_TRUE;
          }
        y += it->item->h;
        vis_count++;

        if (it == sd->pin_item)
          _pin_item_recalc(it);
     }

   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static inline void
_flush_block_order(Elm_Genlist_Data *sd)
{
   Item_Block *itb;
   Eina_List *blocks = NULL;

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        if (itb->adapter)
          blocks = eina_list_append(blocks, itb->adapter);
     }

   efl_ui_focus_manager_calc_update_order(sd->obj, sd->obj, blocks);
}

static void
_item_block_realize(Item_Block *itb)
{
   Elm_Gen_Item *it;
   Eina_List *n;

   if (itb->realized) return;

   itb->realized = EINA_TRUE;
   itb->want_unrealize = EINA_FALSE;

   if (!itb->adapter)
     {
        itb->adapter = efl_add(EFL_UI_FOCUS_COMPOSITION_ADAPTER_CLASS, itb->sd->obj);
        efl_ui_focus_composition_adapter_focus_manager_parent_set(itb->adapter, itb->sd->obj);
        efl_ui_focus_composition_adapter_focus_manager_object_set(itb->adapter, itb->sd->obj);
        efl_ui_focus_manager_calc_register_logical(itb->sd->obj, itb->adapter, itb->sd->obj, NULL);
        _flush_block_order(itb->sd);
     }

   EINA_LIST_FOREACH(itb->items, n, it)
     {
        efl_ui_focus_manager_calc_register_logical(itb->sd->obj, EO_OBJ(it), itb->adapter, NULL);
     }
}

static Eina_Bool
_elm_genlist_tree_effect_setup(Elm_Genlist_Data *sd)
{
   if (!sd->tree_effect_animator)
     {
        _item_tree_effect_before(sd->expanded_item);
        evas_object_raise(sd->event_block_rect);
        evas_object_stack_below(sd->event_block_rect, sd->stack[1]);
        evas_object_show(sd->event_block_rect);
        sd->start_time = ecore_time_get();
        efl_event_callback_add(sd->obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _tree_effect_animator_cb, sd->obj);
        sd->tree_effect_animator = 1;
        return EINA_TRUE;
     }
   return EINA_FALSE;
}

static void
_elm_genlist_tree_effect_stop(Elm_Genlist_Data *sd)
{
   if (sd->tree_effect_animator)
     {
        sd->tree_effect_animator = 0;
        efl_event_callback_del(sd->obj, EFL_CANVAS_OBJECT_EVENT_ANIMATOR_TICK, _tree_effect_animator_cb, sd->obj);
     }
}

EOLIAN static void
_elm_genlist_pan_efl_canvas_group_group_calculate(Eo *obj, Elm_Genlist_Pan_Data *psd)
{
   Evas_Coord ox, oy, ow, oh, cvx, cvy, cvw, cvh;
   Evas_Coord vx = 0, vy = 0, vw = 0, vh = 0;
   Elm_Gen_Item *git;
   Item_Block *itb;
   Eina_List *l;
   int in = 0;
   Evas *e = evas_object_evas_get(obj);;

   Elm_Genlist_Data *sd = psd->wsd;

   efl_canvas_group_need_recalculate_set(obj, EINA_FALSE);
   evas_event_freeze(e);

   if (sd->pan_changed)
     {
        _calc_job(sd);
        sd->pan_changed = EINA_FALSE;
     }

   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
   evas_output_viewport_get(e, &cvx, &cvy, &cvw, &cvh);

   if (sd->tree_effect_enabled &&
       (sd->move_effect_mode != ELM_GENLIST_TREE_EFFECT_NONE))
     {
        _elm_genlist_tree_effect_setup(sd);
     }

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        itb->w = sd->minw;
        if (ELM_RECTS_INTERSECT(itb->x - sd->pan_x + ox,
                                itb->y - sd->pan_y + oy,
                                itb->w, itb->h,
                                cvx, cvy, cvw, cvh))
          {
             if ((!itb->realized) || (itb->changed))
               _item_block_realize(itb);
             _item_block_position(itb, in);
          }
        else
          {
             if (itb->realized) _item_block_unrealize(itb);
             if (sd->pin_item && itb == sd->pin_item->item->block)
               {
                  if (!sd->pin_item->realized)
                    _item_realize(sd->pin_item, sd->pin_item->item->order_num_in, EINA_FALSE);
                  sd->pin_item->item->w = itb->w;
                  sd->pin_item->item->scrl_x = itb->x - sd->pan_x + ox;
                  sd->pin_item->item->scrl_y = itb->y - sd->pan_y + oy;
                  _pin_item_recalc(sd->pin_item);
               }
          }
        in += itb->vis_count;
     }
   if ((!sd->reorder_it) || (sd->reorder_pan_move))
     _group_items_recalc(sd);
   if ((sd->reorder_mode) && (sd->reorder_it))
     {
        if (sd->pan_y != sd->reorder_old_pan_y)
          sd->reorder_pan_move = EINA_TRUE;
        else sd->reorder_pan_move = EINA_FALSE;

        evas_object_raise(sd->VIEW(reorder_it));
        evas_object_stack_below(sd->VIEW(reorder_it), sd->stack[1]);
        sd->reorder_old_pan_y = sd->pan_y;
        sd->start_time = ecore_loop_time_get();
     }

   if (!sd->tree_effect_enabled ||
       (sd->move_effect_mode == ELM_GENLIST_TREE_EFFECT_NONE))
     _item_auto_scroll(sd);

   elm_interface_scrollable_content_pos_get((sd)->obj, &vx, &vy);
   elm_interface_scrollable_content_viewport_geometry_get
         ((sd)->obj, NULL, NULL, &vw, &vh);

   if (sd->reorder_fast == 1)
      elm_interface_scrollable_content_region_show((sd)->obj, vx, vy - 10, vw, vh);
   else if (sd->reorder_fast == -1)
      elm_interface_scrollable_content_region_show((sd)->obj, vx, vy + 10, vw, vh);

   if (sd->focused_item && !sd->item_loop_enable)
     _elm_widget_focus_highlight_start(psd->wobj);

   EINA_LIST_FOREACH(sd->group_items, l, git)
     {
        git->item->want_realize = EINA_FALSE;
        if (git->realized) evas_object_raise(VIEW(git));
     }

   if (sd->pin_item)
     evas_object_raise(VIEW(sd->pin_item));
   //update item before the render to prevent delayed update by job.
   if (sd->update_job)
     {
        ELM_SAFE_FREE(sd->update_job, ecore_job_del);
        _update_job(sd->obj);
     }

   evas_event_thaw(e);
   evas_event_thaw_eval(e);

   efl_event_callback_legacy_call
         (psd->wobj, ELM_INTERFACE_SCROLLABLE_EVENT_CHANGED, NULL);
}

EOLIAN static void
_elm_genlist_pan_efl_object_destructor(Eo *obj, Elm_Genlist_Pan_Data *psd)
{
   efl_data_unref(psd->wobj, psd->wsd);
   efl_destructor(efl_super(obj, MY_PAN_CLASS));
}

EOLIAN static void
_elm_genlist_pan_class_constructor(Efl_Class *klass)
{
      evas_smart_legacy_type_register(MY_PAN_CLASS_NAME_LEGACY, klass);
}

static Eina_Bool
_item_multi_select_up(Elm_Genlist_Data *sd)
{
   Elm_Object_Item *eo_prev;

   if (!sd->selected) return EINA_FALSE;
   if (!sd->multi) return EINA_FALSE;

   eo_prev = elm_genlist_item_prev_get(sd->last_selected_item);
   while (eo_prev)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_prev, prev);
        if ((!_is_no_select(prev)) &&
            (!elm_object_item_disabled_get(eo_prev)) && (!prev->hide))
          break;
        eo_prev = EO_OBJ(ELM_GEN_ITEM_PREV(prev));
     }
   if (!eo_prev) return EINA_TRUE;

   if (elm_genlist_item_selected_get(eo_prev))
     {
        elm_genlist_item_selected_set(sd->last_selected_item, EINA_FALSE);
        sd->last_selected_item = eo_prev;
     }
   else
     {
        elm_genlist_item_selected_set(eo_prev, EINA_TRUE);
     }
   return EINA_TRUE;
}

static Eina_Bool
_item_multi_select_down(Elm_Genlist_Data *sd)
{
   Elm_Object_Item *eo_next;

   if (!sd->selected) return EINA_FALSE;
   if (!sd->multi) return EINA_FALSE;

   eo_next = elm_genlist_item_next_get(sd->last_selected_item);
   while ((eo_next))
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_next, next);
        if ((!_is_no_select(next)) &&
            (!elm_object_item_disabled_get(eo_next)) && (!next->hide))
          break;
        eo_next = EO_OBJ(ELM_GEN_ITEM_NEXT(next));
     }
   if (!eo_next) return EINA_TRUE;

   if (elm_genlist_item_selected_get(eo_next))
     {
        elm_genlist_item_selected_set(sd->last_selected_item, EINA_FALSE);
        sd->last_selected_item = eo_next;
     }
   else
     {
        elm_genlist_item_selected_set(eo_next, EINA_TRUE);
     }

   return EINA_TRUE;
}

static Eina_Bool
_all_items_deselect(Elm_Genlist_Data *sd)
{
   if (!sd->selected) return EINA_FALSE;

   sd->deselecting = eina_list_clone(sd->selected);
   while (sd->deselecting)
     {
        Elm_Object_Item *it = eina_list_data_get(sd->deselecting);

        sd->deselecting = eina_list_remove_list(sd->deselecting, sd->deselecting);
        elm_genlist_item_selected_set(it, EINA_FALSE);
     }

   return EINA_TRUE;
}

static Eina_Bool
_item_single_select_up(Elm_Genlist_Data *sd)
{
   Elm_Gen_Item *prev = NULL;

   if (!sd->selected)
     prev = ELM_GEN_ITEM_FROM_INLIST(sd->items->last);
   else
     {
        Elm_Object_Item *eo_prev = elm_genlist_item_prev_get
           (sd->last_selected_item);
        prev = efl_data_scope_get(eo_prev, ELM_GENLIST_ITEM_CLASS);
     }

   while (prev)
     {
        if ((!_is_no_select(prev)) &&
            (!elm_object_item_disabled_get(EO_OBJ(prev))) && (!prev->hide))
          break;
        prev = ELM_GEN_ITEM_PREV(prev);
     }

   if (!prev) return EINA_FALSE;

   _all_items_deselect(sd);

   elm_genlist_item_selected_set(EO_OBJ(prev), EINA_TRUE);
   return EINA_TRUE;
}

static Eina_Bool
_item_single_select_down(Elm_Genlist_Data *sd)
{
   Elm_Gen_Item *next = NULL;
   Elm_Object_Item *eo_next = NULL;

   if (!sd->selected)
     next = ELM_GEN_ITEM_FROM_INLIST(sd->items);
   else
     {
        eo_next = elm_genlist_item_next_get(sd->last_selected_item);
        next = efl_data_scope_get(eo_next, ELM_GENLIST_ITEM_CLASS);
     }

   while (next)
     {
        if ((!_is_no_select(next)) &&
            (!elm_object_item_disabled_get(EO_OBJ(next))) && (!next->hide))
          break;
        next = ELM_GEN_ITEM_NEXT(next);
     }

   if (!next) return EINA_FALSE;

   _all_items_deselect(sd);

   elm_genlist_item_selected_set(EO_OBJ(next), EINA_TRUE);

   return EINA_TRUE;
}

static void
_elm_genlist_item_focused(Elm_Object_Item *eo_it)
{
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
   Evas_Object *obj = WIDGET(it);
   ELM_GENLIST_DATA_GET(obj, sd);

   if (_is_no_select(it) ||
       (eo_it == sd->focused_item) ||
       (elm_wdg_item_disabled_get(eo_it)))
     return;

   if (it != sd->pin_item)
     {
        switch (_elm_config->focus_autoscroll_mode)
          {
           case ELM_FOCUS_AUTOSCROLL_MODE_SHOW:
              elm_genlist_item_show(eo_it,
                                    ELM_GENLIST_ITEM_SCROLLTO_IN);
              break;
           case ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN:
              elm_genlist_item_bring_in(eo_it,
                                        ELM_GENLIST_ITEM_SCROLLTO_IN);
              break;
           default:
              break;
          }
     }

   sd->focused_item = eo_it;

   if (it->realized)
     _elm_genlist_item_focus_update(it);
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_ITEM_FOCUSED, eo_it);
   if (_elm_config->atspi_mode)
     efl_access_state_changed_signal_emit(eo_it, EFL_ACCESS_STATE_TYPE_FOCUSED, EINA_TRUE);
}

static void
_elm_genlist_item_unfocused(Elm_Object_Item *eo_it)
{
   if (!eo_it) return;

   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
   Evas_Object *obj = WIDGET(it);
   Evas_Object *win = efl_provider_find(obj, EFL_UI_WIN_CLASS);
   ELM_GENLIST_DATA_GET(obj, sd);

   if (_is_no_select(it))
     return;

   if ((!sd->focused_item) ||
       (eo_it != sd->focused_item))
     return;

   if (elm_win_focus_highlight_enabled_get(win))
     {
        ELM_GENLIST_ITEM_DATA_GET(sd->focused_item, focus_it);
        edje_object_signal_emit(VIEW(focus_it), SIGNAL_UNFOCUSED, "elm");
     }

   sd->focused_item = NULL;
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_ITEM_UNFOCUSED, eo_it);
   if (_elm_config->atspi_mode)
     efl_access_state_changed_signal_emit(eo_it, EFL_ACCESS_STATE_TYPE_FOCUSED, EINA_FALSE);
}

static Eina_Bool
_item_focused_next(Evas_Object *obj, Elm_Focus_Direction dir)
{
   ELM_GENLIST_DATA_GET(obj, sd);
   Elm_Gen_Item *next;
   Elm_Object_Item *eo_next;
   Elm_Object_Item *eo_first_item;
   Elm_Object_Item *eo_last_item;

   if (!sd->focused_item)
     {
        if (dir == ELM_FOCUS_UP)
          next = ELM_GEN_ITEM_FROM_INLIST(sd->items->last);
        else if (dir == ELM_FOCUS_DOWN)
          next = ELM_GEN_ITEM_FROM_INLIST(sd->items);
        else
          return EINA_FALSE;

        while ((next) &&
               ((elm_wdg_item_disabled_get(EO_OBJ(next))) ||
               (_is_no_select(next))))
          if (!next->hide) next = ELM_GEN_ITEM_NEXT(next);
     }
   else
     {
        ELM_GENLIST_ITEM_DATA_GET(sd->focused_item, focus_it);
        if (dir == ELM_FOCUS_UP)
          {
             eo_first_item = elm_genlist_first_item_get(WIDGET(focus_it));
             if (eo_first_item == sd->focused_item) return EINA_FALSE;
             eo_next = elm_genlist_item_prev_get(sd->focused_item);
             next = efl_data_scope_get(eo_next, ELM_GENLIST_ITEM_CLASS);

             while ((next) &&
                    ((elm_wdg_item_disabled_get(eo_next)) ||
                    (_is_no_select(next))))
               {
                  eo_next = elm_genlist_item_prev_get(eo_next);
                  next = efl_data_scope_get(eo_next, ELM_GENLIST_ITEM_CLASS);
               }
          }
        else if (dir == ELM_FOCUS_DOWN)
          {
             eo_last_item = elm_genlist_last_item_get(WIDGET(focus_it));
             if (eo_last_item == sd->focused_item) return EINA_FALSE;
             eo_next = elm_genlist_item_next_get(sd->focused_item);
             next = efl_data_scope_get(eo_next, ELM_GENLIST_ITEM_CLASS);

             while ((next) &&
                    ((elm_wdg_item_disabled_get(eo_next)) ||
                    (_is_no_select(next))))
               {
                  eo_next = elm_genlist_item_next_get(eo_next);
                  next = efl_data_scope_get(eo_next, ELM_GENLIST_ITEM_CLASS);
               }
          }
        else
          return EINA_FALSE;

        if (!next) return EINA_FALSE;
     }

   elm_object_item_focus_set(EO_OBJ(next), EINA_TRUE);

   return EINA_TRUE;
}

static void
_elm_genlist_item_content_focus_set(Elm_Gen_Item *it, Elm_Focus_Direction dir)
{
   Evas_Object *focused_obj = NULL;
   Eina_List *l;
   if (!it) return;

   if (!it->item->wsd->focus_on_selection_enabled) return;

   if (!it->item_focus_chain)
     {
        elm_object_focus_set(VIEW(it), EINA_TRUE);
        return;
     }

   EINA_LIST_FOREACH(it->item_focus_chain, l, focused_obj)
     if (elm_object_focus_get(focused_obj)) break;

   /* FOCUS-FIXME
   if (focused_obj && (dir != ELM_FOCUS_PREVIOUS))
     {
        Evas_Object *nextfocus;
        Elm_Object_Item *nextfocus_item;
        if (efl_ui_widget_focus_next_get(focused_obj, dir, &nextfocus, &nextfocus_item))
          {
             if (nextfocus_item)
               elm_object_item_focus_set(nextfocus_item, EINA_TRUE);
             else
               elm_object_focus_set(nextfocus, EINA_TRUE);
             return;
          }
     }
   */

   if (!l) l = it->item_focus_chain;

   if (dir == ELM_FOCUS_RIGHT)
     {
        l = eina_list_next(l);
        if (!l) l = it->item_focus_chain;
     }
   else if (dir == ELM_FOCUS_LEFT)
     {
        l = eina_list_prev(l);
        if (!l) l = eina_list_last(it->item_focus_chain);
     }

   elm_object_focus_set(eina_list_data_get(l), EINA_TRUE);
}

static Eina_Bool
_key_action_move_dir(Evas_Object *obj, Elm_Focus_Direction dir, Eina_Bool multi)
{
   ELM_GENLIST_DATA_GET(obj, sd);
   Elm_Object_Item *it = NULL;
   Eina_Bool ret = EINA_FALSE;
   Evas_Coord v = 0;
   Evas_Coord min = 0;
   Eina_Bool focus_only = EINA_FALSE;

   // get content size and viewport size
   elm_interface_scrollable_content_viewport_geometry_get
         (obj, NULL, NULL, NULL, &v);
   elm_interface_scrollable_content_size_get(obj, NULL, &min);

   if (_elm_config->item_select_on_focus_disable)
     {
        ret = _item_focused_next(obj, dir);
     }
   else
     {
        if (multi)
          {
             if (dir == ELM_FOCUS_UP)
               ret = _item_multi_select_up(sd);
             else if (dir == ELM_FOCUS_DOWN)
               ret = _item_multi_select_down(sd);
          }
        else
          {
             if ((sd->focused_item) && (dir == ELM_FOCUS_UP))
               ret = _item_single_select_up(sd);
             else if (dir == ELM_FOCUS_DOWN)
               ret = _item_single_select_down(sd);
          }
     }
   if (ret)
     return EINA_TRUE;

   focus_only = _elm_config->item_select_on_focus_disable;
   // handle item loop feature
   if (sd->item_loop_enable && !sd->item_looping_on)
     {
        if (min < v)
          {
             if (dir == ELM_FOCUS_UP)
               {
                  elm_layout_signal_emit(obj, "elm,action,looping,up", "elm");
                  sd->item_looping_on = EINA_TRUE;
               }
             else if (dir == ELM_FOCUS_DOWN)
               {
                  elm_layout_signal_emit(obj, "elm,action,looping,down", "elm");
                  sd->item_looping_on = EINA_TRUE;
               }
          }
        else
          {
             if (dir == ELM_FOCUS_UP)
               {
                  it = elm_genlist_last_item_get(obj);
                  ELM_GENLIST_ITEM_DATA_GET(it, gen_it);
                  while (_is_no_select(gen_it) || elm_wdg_item_disabled_get(it))
                    it = elm_genlist_item_prev_get(it);
               }
            else if (dir == ELM_FOCUS_DOWN)
              {
                 it = elm_genlist_first_item_get(obj);
                 ELM_GENLIST_ITEM_DATA_GET(it, gen_it);
                 while (_is_no_select(gen_it) || elm_wdg_item_disabled_get(it))
                   it = elm_genlist_item_next_get(it);
              }

             if (it && focus_only)
               elm_object_item_focus_set(it, EINA_TRUE);
             else if (it)
               elm_genlist_item_selected_set(it, EINA_TRUE);
          }
        return EINA_TRUE;
     }
   else if (sd->item_looping_on)
     return EINA_TRUE;

   return EINA_FALSE;
}

static Eina_Bool
_key_action_move(Evas_Object *obj, const char *params)
{
   ELM_GENLIST_DATA_GET(obj, sd);
   const char *dir = params;

   Evas_Coord x = 0;
   Evas_Coord y = 0;
   Evas_Coord v_w = 0;
   Evas_Coord v_h = 0;
   Evas_Coord step_x = 0;
   Evas_Coord step_y = 0;
   Evas_Coord page_x = 0;
   Evas_Coord page_y = 0;
   Elm_Object_Item *it = NULL;
   Evas_Coord pan_max_x = 0, pan_max_y = 0;

   if (!sd->items) return EINA_FALSE;
   elm_interface_scrollable_content_pos_get(obj, &x, &y);
   elm_interface_scrollable_step_size_get(obj, &step_x, &step_y);
   elm_interface_scrollable_page_size_get(obj, &page_x, &page_y);
   elm_interface_scrollable_content_viewport_geometry_get
         (obj, NULL, NULL, &v_w, &v_h);

   _elm_widget_focus_auto_show(obj);

   if (!strcmp(dir, "up_multi"))
     {
        if (_key_action_move_dir(obj, ELM_FOCUS_UP, EINA_TRUE)) return EINA_TRUE;
        else if (_key_action_move_dir(obj, ELM_FOCUS_UP, EINA_FALSE)) return EINA_TRUE;
        else return EINA_FALSE;
     }
   else if (!strcmp(dir, "down_multi"))
     {
        if (_key_action_move_dir(obj, ELM_FOCUS_DOWN, EINA_TRUE)) return EINA_TRUE;
        else if (_key_action_move_dir(obj, ELM_FOCUS_DOWN, EINA_FALSE)) return EINA_TRUE;
        else return EINA_FALSE;
     }
   else if (!strcmp(dir, "first"))
     {
        it = elm_genlist_first_item_get(obj);
        if (it)
          {
             if (_elm_config->item_select_on_focus_disable)
               {
                  elm_object_item_focus_set(it, EINA_TRUE);
                  elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_TOP);
               }
             else
               {
                  elm_genlist_item_selected_set(it, EINA_TRUE);
               }
             return EINA_TRUE;
          }
     }
   else if (!strcmp(dir, "last"))
     {
        it = elm_genlist_last_item_get(obj);
        if (it)
          {
             if (_elm_config->item_select_on_focus_disable)
               {
                  elm_object_item_focus_set(it, EINA_TRUE);
                  elm_genlist_item_show(it, ELM_GENLIST_ITEM_SCROLLTO_BOTTOM);
               }
             else
               {
                  elm_genlist_item_selected_set(it, EINA_TRUE);
               }
             return EINA_TRUE;
          }
     }
   else if (!strcmp(dir, "prior"))
     {
        if (page_y < 0)
          y -= -(page_y * v_h) / 100;
        else
          y -= page_y;
     }
   else if (!strcmp(dir, "next"))
     {
        if (page_y < 0)
          y += -(page_y * v_h) / 100;
        else
          y += page_y;
     }
   else return EINA_FALSE;

   elm_obj_pan_pos_max_get(sd->pan_obj, &pan_max_x, &pan_max_y);
   if (x < 0)
     x = 0;
   else if (x > pan_max_x)
     x = pan_max_x;
   if (y < 0)
     y = 0;
   else if (y > pan_max_y)
     y = pan_max_y;

   elm_interface_scrollable_content_pos_set(obj, x, y, EINA_TRUE);

   return EINA_TRUE;
}

static Eina_Bool
_key_action_select(Evas_Object *obj, const char *params)
{
   Elm_Object_Item *eo_it = NULL;

   ELM_GENLIST_DATA_GET(obj, sd);
   if (!sd->items) return EINA_FALSE;

   eo_it = elm_object_focused_item_get(obj);
   if (!eo_it) return EINA_TRUE;
   elm_genlist_item_expanded_set(eo_it, !elm_genlist_item_expanded_get(eo_it));
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);

   if (sd->multi &&
       ((sd->multi_select_mode != ELM_OBJECT_MULTI_SELECT_MODE_WITH_CONTROL) ||
        (!strcmp(params, "multi"))))
     {
        if (!it->selected)
          {
             _item_highlight(it);
             if (_item_select(it)) goto deleted;
          }
        else
         _item_unselect(it);
     }
   else
     {
        if (!it->selected)
          {
             while (sd->selected)
               {
                  Elm_Object_Item *eo_sel = sd->selected->data;
                  Elm_Gen_Item *sel = efl_data_scope_get(eo_sel, ELM_GENLIST_ITEM_CLASS);
                  _item_unselect(sel);
               }
          }
        else
          {
             const Eina_List *l, *l_next;
             Elm_Object_Item *eo_it2;

             EINA_LIST_FOREACH_SAFE(sd->selected, l, l_next, eo_it2)
               {
                  ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
                  if (it2 != it)
                    _item_unselect(it2);
               }
          }
        _item_highlight(it);
        if (_item_select(it)) goto deleted;
     }

   efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_ACTIVATED, EO_OBJ(it));

   return EINA_TRUE;

deleted:
   return EINA_FALSE;
}

static Eina_Bool
_key_action_escape(Evas_Object *obj, const char *params EINA_UNUSED)
{
   ELM_GENLIST_DATA_GET(obj, sd);

   if (!_all_items_deselect(sd)) return EINA_FALSE;
   return EINA_TRUE;
}

EOLIAN static Eina_Bool
_elm_genlist_efl_ui_widget_widget_sub_object_add(Eo *obj, Elm_Genlist_Data *_pd EINA_UNUSED, Evas_Object *sobj)
{
   /* skipping layout's code, which registers size hint changing
    * callback on sub objects. this is here because items'
    * content_get() routines may change hints on the objects after
    * creation, thus issuing TOO MANY sizing_eval()'s here. they are
    * not needed at here anyway, so let's skip listening to those
    * hints changes */
   return elm_widget_sub_object_add(efl_cast(obj, EFL_UI_WIDGET_CLASS), sobj);
}

EOLIAN static Eina_Bool
_elm_genlist_efl_ui_widget_widget_sub_object_del(Eo *obj, Elm_Genlist_Data *sd, Evas_Object *sobj)
{
   Eina_Bool int_ret = EINA_FALSE;

   /* XXX: hack -- also skipping sizing recalculation on
    * sub-object-del. genlist's crazy code paths (like groups and
    * such) seem to issue a whole lot of deletions and Evas bitches
    * about too many recalculations */
   sd->on_sub_del = EINA_TRUE;
   int_ret = elm_widget_sub_object_del(efl_super(obj, MY_CLASS), sobj);
   sd->on_sub_del = EINA_FALSE;
   return int_ret;
}

/*
 * This function searches the nearest visible item based on the given item.
 * If the given item is in the genlist viewport, this returns the given item.
 * Or this searches the realized items and checks the nearest fully visible item
 * according to the given item's position.
 */
static Elm_Object_Item *
_elm_genlist_nearest_visible_item_get(Evas_Object *obj, Elm_Object_Item *eo_it)
{
   Evas_Coord vx = 0, vy = 0, vw = 0, vh = 0; // genlist viewport geometry
   Evas_Coord ix = 0, iy = 0, iw = 0, ih = 0; // given item geometry
   Evas_Coord cx = 0, cy = 0, cw = 0, ch = 0; // candidate item geometry
   Eina_List *item_list = NULL, *l = NULL;
   Elm_Object_Item *first_item, *eo_item = NULL;
   ELM_GENLIST_DATA_GET(obj, sd);
   Eina_Bool search_next = EINA_FALSE;

   if (!eo_it) return NULL;
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);

   evas_object_geometry_get(sd->pan_obj, &vx, &vy, &vw, &vh);

   if (it->realized)
     {
        evas_object_geometry_get(VIEW(it), &ix, &iy, &iw, &ih);

        if (ELM_RECTS_INCLUDE(vx, vy, vw, vh, ix, iy, iw, ih))
          {
             if (!elm_object_item_disabled_get(eo_it))
               return eo_it;
             else
               search_next = EINA_TRUE;
          }
     }

   item_list = elm_genlist_realized_items_get(obj);
   /* if first realized item is before parameter item then parameter item is
    * off viewport towards bottom: start at end of list */
   first_item = eina_list_data_get(item_list);
   ELM_GENLIST_ITEM_DATA_GET(first_item, first_it);

   if ((iy < vy) || search_next || (!first_it) || (first_it->position > it->position))
     {
        EINA_LIST_FOREACH(item_list, l, eo_item)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_item, item);
             evas_object_geometry_get(VIEW(item), &cx, &cy, &cw, &ch);
             if (ELM_RECTS_INCLUDE(vx, vy, vw, vh, cx, cy, cw, ch) &&
                 !elm_object_item_disabled_get(eo_item))
               {
                  eina_list_free(item_list);
                  return eo_item;
               }
          }
     }
   else
     {
        EINA_LIST_REVERSE_FOREACH(item_list, l, eo_item)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_item, item);
             evas_object_geometry_get(VIEW(item), &cx, &cy, &cw, &ch);
             if (ELM_RECTS_INCLUDE(vx, vy, vw, vh, cx, cy, cw, ch) &&
                 !elm_object_item_disabled_get(eo_item))
               {
                  eina_list_free(item_list);
                  return eo_item;
               }
          }
     }
   eina_list_free(item_list);

   return it->realized ? eo_it : NULL;
}

EOLIAN static void
_elm_genlist_efl_ui_focus_manager_setup_on_first_touch(Eo *obj, Elm_Genlist_Data *sd, Efl_Ui_Focus_Direction direction EINA_UNUSED, Efl_Ui_Focus_Object *entry EINA_UNUSED)
{
   Elm_Object_Item *eo_it = NULL;
   Eina_Bool is_sel = EINA_FALSE;

   if (!sd->items)
     {
        efl_ui_focus_manager_setup_on_first_touch(efl_super(obj, MY_CLASS), direction, entry);
     }
   else
     {
        if (sd->last_focused_item)
          eo_it = sd->last_focused_item;
        else if (sd->last_selected_item)
          eo_it = sd->last_selected_item;
        else if (_elm_config->first_item_focus_on_first_focus_in)
          {
             eo_it = elm_genlist_first_item_get(obj);
             is_sel = EINA_TRUE;
          }

        while (eo_it)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
             if ((!_is_no_select(it)) && (!elm_object_item_disabled_get(eo_it)))
               break;
             eo_it = EO_OBJ(ELM_GEN_ITEM_NEXT(it));
          }

        if (eo_it)
          {
             eo_it = _elm_genlist_nearest_visible_item_get(obj, eo_it);
             if (eo_it)
               {
                  if (!_elm_config->item_select_on_focus_disable && is_sel)
                    elm_genlist_item_selected_set(eo_it, EINA_TRUE);
                  else
                    elm_object_item_focus_set(eo_it, EINA_TRUE);
                 _elm_widget_focus_highlight_start(obj);
                 //set it again in the manager, there might be the case that the manager focus history and internal item foused logic are in different states
                 if (efl_ui_focus_manager_request_subchild(obj, eo_it))
                   efl_ui_focus_manager_focus_set(obj, eo_it);
               }
          }
        else
          {
             //Just set evas focus on the genlist itself, events will pass on and a other element will be taken
             efl_ui_focus_object_focus_set(obj, EINA_TRUE);
          }
      }
}

EOLIAN static Efl_Ui_Focus_Object*
_elm_genlist_efl_ui_focus_manager_manager_focus_get(const Eo *obj, EINA_UNUSED Elm_Genlist_Data *pd)
{
   Eo *focused_obj = efl_ui_focus_manager_focus_get(efl_super(obj, MY_CLASS));
   Eo *registered_manager = efl_ui_focus_object_focus_manager_get(obj);

   if (!focused_obj && efl_ui_focus_manager_redirect_get(registered_manager))
     return (Efl_Ui_Focus_Object*) obj;

   return focused_obj;
}

static Efl_Ui_Focus_Object*
_select_candidate(Eo *obj, EINA_UNUSED Elm_Genlist_Data *pd, Efl_Ui_Focus_Direction direction)
{
   Elm_Object_Item *first = elm_genlist_first_item_get(obj);
   Elm_Object_Item *last = elm_genlist_last_item_get(obj);

   switch(direction)
     {
        case EFL_UI_FOCUS_DIRECTION_DOWN:
        case EFL_UI_FOCUS_DIRECTION_RIGHT:
          elm_object_item_focus_set(first, EINA_TRUE);
          return obj;
        break;
        case EFL_UI_FOCUS_DIRECTION_UP:
        case EFL_UI_FOCUS_DIRECTION_LEFT:
          elm_object_item_focus_set(last, EINA_TRUE);
          return obj;
        break;
        case EFL_UI_FOCUS_DIRECTION_NEXT:
        case EFL_UI_FOCUS_DIRECTION_PREVIOUS:
          //do not go further with logical movement
          return NULL;
        break;
        default:
          ERR("Uncaught focus direction");
          return NULL;
        break;
     }
}

EOLIAN static Efl_Ui_Focus_Object*
_elm_genlist_efl_ui_focus_manager_move(Eo *obj, Elm_Genlist_Data *pd, Efl_Ui_Focus_Direction direction)
{
   if (efl_ui_focus_manager_focus_get(obj) == obj)
     {
        return _select_candidate(obj, pd, direction);
     }
   else
     {
        return efl_ui_focus_manager_move(efl_super(obj, MY_CLASS), direction);
     }
}

EOLIAN static Eina_Bool
_elm_genlist_efl_ui_focus_object_on_focus_update(Eo *obj, Elm_Genlist_Data *sd)
{
   Eina_Bool int_ret = EINA_FALSE;

   int_ret = efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS));
   if (!int_ret) return EINA_FALSE;

   if (efl_ui_focus_object_focus_get(obj) && (sd->items) && (sd->selected) &&
       (!sd->last_selected_item))
     {
        sd->last_selected_item = eina_list_data_get(sd->selected);
     }

   return EINA_TRUE;
}

static Eina_Bool _elm_genlist_smart_focus_next_enable = EINA_FALSE;

static void
_mirrored_set(Evas_Object *obj,
              Eina_Bool rtl)
{
   ELM_GENLIST_DATA_GET(obj, sd);

   _item_cache_zero(sd);
   efl_ui_mirrored_set(obj, rtl);
}

EOLIAN static Eina_Error
_elm_genlist_efl_ui_widget_theme_apply(Eo *obj, Elm_Genlist_Data *sd)
{
   Item_Block *itb;
   Eina_Error int_ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
   Eina_List *l;
   Elm_Gen_Item *it;
   Evas *e;

   int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
   if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;

   elm_interface_scrollable_reset_signals(obj);
   e = evas_object_evas_get(obj);
   evas_event_freeze(e);
   _mirrored_set(obj, efl_ui_mirrored_get(obj));

   eina_hash_free_buckets(sd->size_caches);
   sd->minw = sd->minh = sd->realminw = 0;

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        if (itb->realized) _item_block_unrealize(itb);
        EINA_LIST_FOREACH(itb->items, l, it)
          it->item->mincalcd = EINA_FALSE;

        itb->changed = EINA_TRUE;
     }
   if (sd->obj && efl_finalized_get(obj))
     efl_canvas_group_change(sd->obj);
   elm_layout_sizing_eval(obj);
   evas_event_thaw(e);
   evas_event_thaw_eval(e);

   return int_ret;
}

/* FIXME: take off later. maybe this show region coords belong in the
 * interface (new api functions, set/get)? */
static void
_show_region_hook(void *data EINA_UNUSED, Evas_Object *obj, Eina_Rect r)
{
   ELM_GENLIST_DATA_GET_OR_RETURN(obj, sd);

   //x & y are screen coordinates, Add with pan coordinates
   r.x += sd->pan_x;
   r.y += sd->pan_y;
   elm_interface_scrollable_content_region_show(obj, r.x, r.y, r.w, r.h);
}

static void
_item_highlight(Elm_Gen_Item *it)
{
   const char *selectraise;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (_is_no_select(it) ||
       (!sd->highlight) ||
       (it->highlighted) || elm_wdg_item_disabled_get(EO_OBJ(it)) ||
       (it->item->deco_it_view))
     return;

   edje_object_signal_emit(VIEW(it), SIGNAL_SELECTED, "elm");
   if (it->deco_all_view)
     edje_object_signal_emit(it->deco_all_view, SIGNAL_SELECTED, "elm");
   efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_HIGHLIGHTED, EO_OBJ(it));

   selectraise = edje_object_data_get(VIEW(it), "selectraise");
   if ((selectraise) && (!strcmp(selectraise, "on")))
     {
        if (it->deco_all_view) evas_object_stack_below(it->deco_all_view, sd->stack[1]);
        else evas_object_stack_below(VIEW(it), sd->stack[1]);
        if ((it->item->group_item) && (it->item->group_item->realized))
          evas_object_stack_above(it->item->VIEW(group_item), sd->stack[1]);
        if (sd->pin_item && sd->pin_item->realized)
          evas_object_stack_above(VIEW(sd->pin_item), sd->stack[1]);
     }
   it->highlighted = EINA_TRUE;
}

static void
_item_unhighlight(Elm_Gen_Item *it)
{
   if (!it->highlighted) return;

   edje_object_signal_emit(VIEW(it), SIGNAL_UNSELECTED, "elm");
   efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_UNHIGHLIGHTED, EO_OBJ(it));
   if (it->deco_all_view)
     edje_object_signal_emit(it->deco_all_view, SIGNAL_UNSELECTED, "elm");
   _item_restack(it);
   it->highlighted = EINA_FALSE;
}

static void
_item_block_position_update(Eina_Inlist *list,
                            int idx)
{
   Item_Block *tmp;

   EINA_INLIST_FOREACH(list, tmp)
     {
        tmp->position = idx++;
        tmp->position_update = EINA_TRUE;
     }
}

static void
_item_position_update(Eina_List *list,
                      int idx)
{
   Elm_Gen_Item *it;
   Eina_List *l;

   EINA_LIST_FOREACH(list, l, it)
     {
        it->position = idx++;
        it->position_update = EINA_TRUE;
     }
}

static void
_item_block_merge(Item_Block *left,
                  Item_Block *right)
{
   Eina_List *l;
   Elm_Gen_Item *it2;

   EINA_LIST_FOREACH(right->items, l, it2)
     {
        if (right->realized)
          efl_ui_focus_manager_calc_unregister(right->sd->obj, EO_OBJ(it2));
        it2->item->block = left;
        left->count++;
        left->changed = EINA_TRUE;
        if (left->realized)
          efl_ui_focus_manager_calc_register_logical(right->sd->obj, EO_OBJ(it2), left->adapter, NULL);
     }
   left->items = eina_list_merge(left->items, right->items);
}

static void
_item_block_del(Elm_Gen_Item *it)
{
   Eina_Inlist *il;
   Item_Block *itb = it->item->block;
   Eina_Bool block_changed = EINA_FALSE;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   itb->items = eina_list_remove(itb->items, it);
   itb->count--;
   itb->changed = EINA_TRUE;
   efl_canvas_group_change(sd->pan_obj);
   efl_canvas_group_change(sd->obj);
   if (itb->realized) efl_ui_focus_manager_calc_unregister(itb->sd->obj, EO_OBJ(it));
   if (itb->count < 1)
     {
        Item_Block *itbn;

        il = EINA_INLIST_GET(itb);
        itbn = (Item_Block *)(il->next);
        if (it->parent)
          {
             it->parent->item->items =
                   eina_list_remove(it->parent->item->items, EO_OBJ(it));
             if (!it->parent->item->items)
               sd->top_level_parent_items--;
          }
        else
          {
             _item_block_position_update(il->next, itb->position);
             sd->blocks = eina_inlist_remove(sd->blocks, il);
          }
        free(itb);
        if (itbn) itbn->changed = EINA_TRUE;
     }
   else
     {
        if (itb->count < (sd->max_items_per_block / 2))
          {
             Item_Block *itbp;
             Item_Block *itbn;

             il = EINA_INLIST_GET(itb);
             itbp = (Item_Block *)(il->prev);
             itbn = (Item_Block *)(il->next);

             /* merge block with previous */
             if ((itbp) &&
                 ((itbp->count + itb->count) <
                  (sd->max_items_per_block +
                   (sd->max_items_per_block / 2))))
               {
                  _item_block_merge(itbp, itb);
                  _item_block_position_update
                    (EINA_INLIST_GET(itb)->next, itb->position);
                  sd->blocks = eina_inlist_remove
                      (sd->blocks, EINA_INLIST_GET(itb));
                  free(itb);
                  block_changed = EINA_TRUE;
               }
             /* merge block with next */
             else if ((itbn) &&
                      ((itbn->count + itb->count) <
                       (sd->max_items_per_block +
                        (sd->max_items_per_block / 2))))
               {
                  _item_block_merge(itb, itbn);
                  _item_block_position_update
                    (EINA_INLIST_GET(itbn)->next, itbn->position);
                  sd->blocks =
                    eina_inlist_remove(sd->blocks, EINA_INLIST_GET(itbn));
                  free(itbn);
                  block_changed = EINA_TRUE;
               }
          }
     }

   if (block_changed)
     {
        sd->pan_changed = EINA_TRUE;
        evas_object_smart_changed(sd->pan_obj);
     }
}

static void
_decorate_all_item_unrealize(Elm_Gen_Item *it)
{
   if ((!it) || (!it->item->decorate_all_item_realized)) return;

   edje_object_part_unswallow(it->deco_all_view, VIEW(it));
   evas_object_smart_member_add(VIEW(it), it->item->wsd->pan_obj);
   elm_widget_sub_object_add(WIDGET(it), VIEW(it));
   _elm_genlist_item_position_state_update(it);
   _elm_genlist_item_state_update(it);

   if (it->item->wsd->reorder_mode)
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_MODE_SET, "elm");
        edje_object_signal_emit(it->deco_all_view, SIGNAL_REORDER_MODE_UNSET,
                                "elm");
     }

   _view_clear(it->deco_all_view, &(it->item->deco_all_texts),
               &(it->item->deco_all_contents));

   edje_object_signal_emit(VIEW(it), SIGNAL_DECORATE_DISABLED, "elm");

   edje_object_message_signal_process(it->deco_all_view);
   _item_mouse_callbacks_del(it, it->deco_all_view);
   _item_mouse_callbacks_add(it, VIEW(it));

   ELM_SAFE_FREE(it->deco_all_view, evas_object_del);

   it->item->decorate_all_item_realized = EINA_FALSE;
}

static void
_elm_genlist_item_del_not_serious(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Elm_Object_Item *eo_it = EO_OBJ(it);

   elm_wdg_item_pre_notify_del(eo_it);

   if (it->selected)
     {
        sd->selected = eina_list_remove(sd->selected, EO_OBJ(it));
        if (sd->deselecting)
          sd->deselecting = eina_list_remove(sd->deselecting, it);
     }
   if (sd->last_focused_item == eo_it)
     sd->last_focused_item = NULL;
   if (sd->focused_item == eo_it)
     sd->focused_item = NULL;
   if (sd->last_selected_item == eo_it)
     sd->last_selected_item = NULL;
   if (sd->mode_item == it)
     sd-> mode_item = NULL;

   if (it->itc->func.del)
     it->itc->func.del((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it));
}

static void
_elm_genlist_item_del_serious(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->item_count--;
   _elm_genlist_item_del_not_serious(it);

   sd->items = eina_inlist_remove(sd->items, EINA_INLIST_GET(it));
   if (it->tooltip.del_cb)
     it->tooltip.del_cb((void *)it->tooltip.data, WIDGET(it), it);
   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
   if (it->item->type & ELM_GENLIST_ITEM_GROUP)
     sd->group_items = eina_list_remove(sd->group_items, it);

   ELM_SAFE_FREE(sd->state, eina_inlist_sorted_state_free);
   efl_canvas_group_change(sd->pan_obj);
   efl_canvas_group_change(sd->obj);

   ELM_SAFE_FREE(it->item, free);
}

static void
_item_del(Elm_Gen_Item *it)
{
   Evas *e;
   Evas_Object *obj = WIDGET(it);
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   e = evas_object_evas_get(obj);
   evas_event_freeze(e);

   if (it->item->rel_revs)
     {
        Elm_Gen_Item *tmp;
        EINA_LIST_FREE(it->item->rel_revs, tmp)
          {
             if (tmp->item->queued && !(tmp->base)->on_deletion)
               {
                  tmp->item->queued = EINA_FALSE;
                  sd->queue = eina_list_remove(sd->queue, tmp);
                  if (_item_process(sd, tmp))
                    _item_process_post(sd, tmp);
               }
             tmp->item->rel = NULL;
          }
     }
   if (it->item->rel)
     {
        it->item->rel->item->rel_revs =
           eina_list_remove(it->item->rel->item->rel_revs, it);
     }
   elm_genlist_item_subitems_clear(EO_OBJ(it));
   if (sd->show_item == it) sd->show_item = NULL;
   if (sd->pin_item == it) sd->pin_item = NULL;
   if (it->realized) _elm_genlist_item_unrealize(it, EINA_FALSE);
   if (it->item->decorate_all_item_realized) _decorate_all_item_unrealize(it);
   if (it->item->block) _item_block_del(it);
   if (it->item->queued)
     sd->queue = eina_list_remove(sd->queue, it);
   if (sd->anchor_item == it)
     {
        sd->anchor_item = ELM_GEN_ITEM_NEXT(it);
        if (!sd->anchor_item)
          sd->anchor_item =
            ELM_GEN_ITEM_PREV(it);
     }
   if (sd->expanded_item == it)
     {
        if (sd->tree_effect_animator)
          {
             _item_tree_effect_finish(sd);
             _elm_genlist_tree_effect_stop(sd);
          }
        sd->expanded_item = NULL;
        sd->move_effect_mode = ELM_GENLIST_TREE_EFFECT_NONE;
     }
   if (sd->expanded_next_item == it) sd->expanded_next_item = NULL;
   if (sd->move_items) sd->move_items = eina_list_remove(sd->move_items, it);
   if (it->parent)
     {
        it->parent->item->items = eina_list_remove(it->parent->item->items, EO_OBJ(it));
        if (!it->parent->item->items)
          sd->top_level_parent_items--;
     }
   ELM_SAFE_FREE(it->item->swipe_timer, ecore_timer_del);
   _elm_genlist_item_del_serious(it);

   if (it->itc->refcount <= 1 && eina_hash_find(sd->size_caches, &(it->itc)))
     eina_hash_del_by_key(sd->size_caches, it->itc);
   elm_genlist_item_class_unref((Elm_Genlist_Item_Class *)it->itc);
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
   if (!sd->queue) _item_scroll(sd);
}

static void
_item_unselect(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   _item_unhighlight(it); /* unhighlight the item first */
   if (!it->selected) return; /* then check whether the item is selected */

  if (it->item->wsd->focus_on_selection_enabled)
     {
        Evas_Object* eobj;
        Eina_List* l;
        EINA_LIST_FOREACH(it->item_focus_chain, l, eobj)
          elm_object_focus_set(eobj, EINA_FALSE);
     }

   it->selected = EINA_FALSE;
   sd->selected = eina_list_remove(sd->selected, EO_OBJ(it));
   evas_object_smart_callback_call
          (WIDGET(it), "unselected", EO_OBJ(it));
   if (_elm_config->atspi_mode)
     efl_access_state_changed_signal_emit(EO_OBJ(it), EFL_ACCESS_STATE_TYPE_SELECTED, EINA_FALSE);
}

static void
_item_mouse_in_cb(void *data,
                  Evas *evas EINA_UNUSED,
                  Evas_Object *obj EINA_UNUSED,
                  void *event_info EINA_UNUSED)
{
   Elm_Gen_Item *it = data;
   if (!elm_object_item_disabled_get(EO_OBJ(it)) &&
       (_elm_config->focus_move_policy == ELM_FOCUS_MOVE_POLICY_IN))
     elm_object_item_focus_set(EO_OBJ(it), EINA_TRUE);
}

static void
_item_mouse_move_cb(void *data,
                    Evas *evas EINA_UNUSED,
                    Evas_Object *obj,
                    void *event_info)
{
   Elm_Gen_Item *it = data;
   Evas_Event_Mouse_Move *ev = event_info;
   Evas_Coord ox, oy, ow, oh, it_scrl_y, y_pos;
   Evas_Coord minw = 0, minh = 0, x, y, w, h, dx, dy, adx, ady;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Elm_Object_Item *eo_it = EO_OBJ(it);

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     {
        if (!sd->on_hold)
          {
             sd->on_hold = EINA_TRUE;
             if ((!sd->wasselected) && (!it->flipped))
               _item_unselect(it);
          }
     }
   else if (it->down && ELM_RECTS_POINT_OUT(x, y, w, h, ev->cur.canvas.x, ev->cur.canvas.y) &&
            !sd->reorder_it)
     {
        ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
        if ((!sd->wasselected) && (!it->flipped))
          _item_unselect(it);
        it->base->still_in = EINA_FALSE;
     }

   if (sd->multi_touched)
     {
        sd->cur_x = ev->cur.canvas.x;
        sd->cur_y = ev->cur.canvas.y;
        return;
     }
   if ((it->dragging) && (it->down))
     {
        if (sd->movements == SWIPE_MOVES)
          sd->swipe = EINA_TRUE;
        else
          {
             sd->history[sd->movements].x = ev->cur.canvas.x;
             sd->history[sd->movements].y = ev->cur.canvas.y;
             if (abs((sd->history[sd->movements].x -
                      sd->history[0].x)) > 40)
               sd->swipe = EINA_TRUE;
             else
               sd->movements++;
          }
        ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
        evas_object_smart_callback_call(WIDGET(it), "drag", eo_it);
        return;
     }
   if ((!it->down) || (sd->longpressed))
     {
        ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
        if ((sd->reorder_mode) && (sd->reorder_it))
          {
             Eina_Bool changed;
             evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);

             if (ev->cur.canvas.y < (oy + (sd->reorder_it->item->h / 2)))
                sd->reorder_fast = 1;
             else if (ev->cur.canvas.y > (oy + oh - (sd->reorder_it->item->h  / 2)))
                sd->reorder_fast = -1;
             else sd->reorder_fast = 0;

             it_scrl_y = ev->cur.canvas.y - sd->reorder_it->dy;

             if (!sd->reorder_start_y)
               sd->reorder_start_y = it->item->block->y + it->y;

             if (it_scrl_y < oy)
               y_pos = oy;
             else if (it_scrl_y + sd->reorder_it->item->h > oy + oh)
               y_pos = oy + oh - sd->reorder_it->item->h;
             else
               y_pos = it_scrl_y;

             if (it->deco_all_view)
               changed = _item_position(it, it->deco_all_view, it->item->scrl_x, y_pos);
             else
               changed = _item_position(it, VIEW(it), it->item->scrl_x, y_pos);

             efl_canvas_group_change(sd->obj);
             if (changed)
               efl_canvas_group_change(sd->pan_obj);
          }
        return;
     }
   if (it->select_mode != ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY)
     elm_coords_finger_size_adjust(1, &minw, 1, &minh);

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   x = ev->cur.canvas.x - x;
   y = ev->cur.canvas.y - y;
   dx = x - it->dx;
   adx = dx;
   if (adx < 0) adx = -dx;

   dy = y - it->dy;
   ady = dy;
   if (ady < 0) ady = -dy;

   minw /= 2;
   minh /= 2;

   // gah! annoying drag detection - leave this alone
   if (h < w)
     {
        if (minw < h) minw = h;
        if (minh < h) minh = h;
     }
   else
     {
        if (minw < w) minw = w;
        if (minh < w) minh = w;
     }
   if (minw < 5) minw = 5;
   if (minh < 5) minh = 5;

   if ((adx > minw) || (ady > minh))
     {
        it->dragging = EINA_TRUE;
        ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
        if (dy < 0)
          {
             if (ady > adx)
               evas_object_smart_callback_call
                 (WIDGET(it), "drag,start,up", eo_it);
             else
               {
                  if (dx < 0)
                    evas_object_smart_callback_call
                      (WIDGET(it), "drag,start,left", eo_it);
                  else
                    evas_object_smart_callback_call
                      (WIDGET(it), "drag,start,right", eo_it);
               }
          }
        else
          {
             if (ady > adx)
               evas_object_smart_callback_call
                 (WIDGET(it), "drag,start,down", eo_it);
             else
               {
                  if (dx < 0)
                    evas_object_smart_callback_call
                      (WIDGET(it), "drag,start,left", eo_it);
                  else
                    evas_object_smart_callback_call
                      (WIDGET(it), "drag,start,right", eo_it);
               }
          }
     }
}

static Eina_Bool
_long_press_cb(void *data)
{
   Elm_Gen_Item *it = data;
   Elm_Object_Item *eo_it_tmp;
   Eina_List *list;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (_is_no_select(it) ||
       elm_wdg_item_disabled_get(EO_OBJ(it)) || (it->dragging))
     goto end;

   sd->longpressed = EINA_TRUE;
   evas_object_smart_callback_call
         (WIDGET(it), "longpressed", EO_OBJ(it));
   if ((sd->reorder_mode) && !(it->item->type & ELM_GENLIST_ITEM_GROUP))
     {
        sd->reorder_it = it;
        sd->reorder_start_y = 0;
        if (it->deco_all_view)
          evas_object_stack_below(it->deco_all_view, sd->stack[1]);
        else
          evas_object_stack_below(VIEW(it), sd->stack[1]);

        elm_interface_scrollable_hold_set(sd->obj, EINA_TRUE);
        elm_interface_scrollable_bounce_allow_set
              (sd->obj, EINA_FALSE, EINA_FALSE);

        list = elm_genlist_realized_items_get
            ((sd)->obj);
        EINA_LIST_FREE(list, eo_it_tmp)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it_tmp, it_tmp);
             if (it != it_tmp) _item_unselect(it_tmp);
          }

        if (elm_genlist_item_expanded_get(EO_OBJ(it)))
          {
             elm_genlist_item_expanded_set(EO_OBJ(it), EINA_FALSE);
             return ECORE_CALLBACK_RENEW;
          }

        if (!sd->decorate_all_mode)
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_ENABLED, "elm");
             if (_elm_config->atspi_mode)
               efl_access_state_changed_signal_emit(EO_OBJ(it), EFL_ACCESS_STATE_TYPE_ANIMATED, EINA_TRUE);
          }
     }

end:
   it->long_timer = NULL;
   return ECORE_CALLBACK_CANCEL;
}

static void
_swipe_do(Elm_Gen_Item *it)
{
   int i, sum = 0;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (_is_no_select(it) ||
       elm_wdg_item_disabled_get(EO_OBJ(it))) return;

   sd->swipe = EINA_FALSE;
   for (i = 0; i < sd->movements; i++)
     {
        sum += sd->history[i].x;
        if (abs(sd->history[0].y - sd->history[i].y) > 10)
          return;
     }

   sum /= sd->movements;
   if (abs(sum - sd->history[0].x) <= 10) return;
   efl_event_callback_legacy_call
         (WIDGET(it), ELM_GENLIST_EVENT_SWIPE, EO_OBJ(it));
}

static Eina_Bool
_swipe_cancel(void *data)
{
   Elm_Gen_Item *it = data;
   if (!it) return ECORE_CALLBACK_CANCEL;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->swipe = EINA_FALSE;
   sd->movements = 0;

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_multi_cancel(void *data)
{
   ELM_GENLIST_DATA_GET(data, sd);

   if (!sd) return ECORE_CALLBACK_CANCEL;
   sd->multi_timeout = EINA_TRUE;

   return ECORE_CALLBACK_RENEW;
}

static void
_multi_touch_gesture_eval(Elm_Gen_Item *it)
{
   Evas_Coord minw = 0, minh = 0;
   Evas_Coord off_x, off_y, off_mx, off_my;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Elm_Object_Item *eo_it = EO_OBJ(it);

   sd->multi_touched = EINA_FALSE;
   ELM_SAFE_FREE(sd->multi_timer, ecore_timer_del);
   if (sd->multi_timeout)
     {
        sd->multi_timeout = EINA_FALSE;
        return;
     }

   elm_coords_finger_size_adjust(1, &minw, 1, &minh);
   off_x = abs(sd->cur_x - sd->prev_x);
   off_y = abs(sd->cur_y - sd->prev_y);
   off_mx = abs(sd->cur_mx - sd->prev_mx);
   off_my = abs(sd->cur_my - sd->prev_my);

   if (((off_x > minw) || (off_y > minh)) && ((off_mx > minw)
                                              || (off_my > minh)))
     {
        if ((off_x + off_mx) > (off_y + off_my))
          {
             if ((sd->cur_x > sd->prev_x) && (sd->cur_mx > sd->prev_mx))
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_SWIPE_RIGHT, eo_it);
             else if ((sd->cur_x < sd->prev_x) && (sd->cur_mx < sd->prev_mx))
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_SWIPE_LEFT, eo_it);
             else if (abs(sd->cur_x - sd->cur_mx) >
                      abs(sd->prev_x - sd->prev_mx))
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_PINCH_OUT, eo_it);
             else
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_PINCH_IN, eo_it);
          }
        else
          {
             if ((sd->cur_y > sd->prev_y) && (sd->cur_my > sd->prev_my))
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_SWIPE_DOWN, eo_it);
             else if ((sd->cur_y < sd->prev_y) && (sd->cur_my < sd->prev_my))
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_SWIPE_UP, eo_it);
             else if (abs(sd->cur_y - sd->cur_my) >
                      abs(sd->prev_y - sd->prev_my))
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_PINCH_OUT, eo_it);
             else
               efl_event_callback_legacy_call
                     (WIDGET(it), ELM_GENLIST_EVENT_MULTI_PINCH_IN, eo_it);
          }
     }

   sd->multi_timeout = EINA_FALSE;
}

static void
_item_multi_down_cb(void *data,
                    Evas *evas EINA_UNUSED,
                    Evas_Object *obj EINA_UNUSED,
                    void *event_info)
{
   Elm_Gen_Item *it = data;
   Evas_Event_Multi_Down *ev = event_info;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if ((sd->multi_device != 0) || (sd->multi_touched)
       || (sd->multi_timeout))
     return;

   sd->multi_device = ev->device;
   sd->multi_down = EINA_TRUE;
   sd->multi_touched = EINA_TRUE;
   sd->prev_mx = ev->canvas.x;
   sd->prev_my = ev->canvas.y;
   if (!sd->wasselected) _item_unselect(it);
   sd->wasselected = EINA_FALSE;
   sd->longpressed = EINA_FALSE;
   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
   if (it->dragging)
     {
        it->dragging = EINA_FALSE;
        evas_object_smart_callback_call
              (WIDGET(it), "drag,stop", EO_OBJ(it));
     }
   ELM_SAFE_FREE(it->item->swipe_timer, ecore_timer_del);
   if (sd->on_hold)
     {
        sd->swipe = EINA_FALSE;
        sd->movements = 0;
        sd->on_hold = EINA_FALSE;
     }
}

static void
_item_multi_up_cb(void *data,
                  Evas *evas EINA_UNUSED,
                  Evas_Object *obj EINA_UNUSED,
                  void *event_info)
{
   Elm_Gen_Item *it = data;
   Evas_Event_Multi_Up *ev = event_info;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (sd->multi_device != ev->device) return;
   sd->multi_device = 0;
   sd->multi_down = EINA_FALSE;
   if (sd->mouse_down) return;
   _multi_touch_gesture_eval(data);
}

static void
_item_multi_move_cb(void *data,
                    Evas *evas EINA_UNUSED,
                    Evas_Object *obj EINA_UNUSED,
                    void *event_info)
{
   Elm_Gen_Item *it = data;
   Evas_Event_Multi_Move *ev = event_info;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (sd->multi_device != ev->device) return;
   sd->cur_mx = ev->cur.canvas.x;
   sd->cur_my = ev->cur.canvas.y;
}

static void
_item_mouse_down_cb(void *data,
                    Evas *evas EINA_UNUSED,
                    Evas_Object *obj,
                    void *event_info)
{
   Evas_Event_Mouse_Down *ev = event_info;
   Elm_Gen_Item *it = data;
   Evas_Coord x, y;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Elm_Object_Item *eo_it = EO_OBJ(it);

   if (ev->button == 3)
     {
        evas_object_geometry_get(obj, &x, &y, NULL, NULL);
        it->dx = ev->canvas.x - x;
        it->dy = ev->canvas.y - y;
        return;
     }
   if (ev->button != 1) return;
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     {
        sd->on_hold = EINA_TRUE;
     }

   it->down = EINA_TRUE;
   it->dragging = EINA_FALSE;
   evas_object_geometry_get(obj, &x, &y, NULL, NULL);
   it->dx = ev->canvas.x - x;
   it->dy = ev->canvas.y - y;
   sd->mouse_down = EINA_TRUE;
   if (!sd->multi_touched)
     {
        sd->prev_x = ev->canvas.x;
        sd->prev_y = ev->canvas.y;
        sd->multi_timeout = EINA_FALSE;
        ecore_timer_del(sd->multi_timer);
        sd->multi_timer = ecore_timer_add(MULTI_DOWN_TIME, _multi_cancel, sd->obj);
     }
   sd->longpressed = EINA_FALSE;
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sd->on_hold = EINA_TRUE;
   else sd->on_hold = EINA_FALSE;
   if (sd->on_hold) return;
   sd->wasselected = it->selected;

   ecore_timer_del(it->item->swipe_timer);
   it->item->swipe_timer = ecore_timer_add(SWIPE_TIME, _swipe_cancel, it);
   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
   if (it->realized)
     it->long_timer = ecore_timer_add
         (sd->longpress_timeout, _long_press_cb, it);
   else
     it->long_timer = NULL;
   sd->swipe = EINA_FALSE;
   sd->movements = 0;
   it->base->still_in = EINA_TRUE;

   if (_is_no_select(it) ||
        elm_wdg_item_disabled_get((Eo *)eo_it))
     return;

   // and finally call the user callbacks.
   // NOTE: keep this code at the bottom, as the user can change the
   //       list at this point (clear, delete, etc...)
   _item_highlight(it);
   if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
     {
        evas_object_smart_callback_call
              ( WIDGET(it), "clicked,double", eo_it);
        efl_event_callback_legacy_call
              (WIDGET(it), ELM_GENLIST_EVENT_ACTIVATED, eo_it);
     }
   evas_object_smart_callback_call
         (WIDGET(it), "pressed", eo_it);
}

static Item_Block *
_item_block_new(Elm_Genlist_Data *sd,
                Eina_Bool prepend)
{
   Item_Block *itb;

   itb = calloc(1, sizeof(Item_Block));
   if (!itb) return NULL;
   itb->sd = sd;
   if (prepend)
     {
        sd->blocks = eina_inlist_prepend(sd->blocks, EINA_INLIST_GET(itb));
        _item_block_position_update(sd->blocks, 0);
     }
   else
     {
        sd->blocks = eina_inlist_append(sd->blocks, EINA_INLIST_GET(itb));
        itb->position_update = EINA_TRUE;
        if (sd->blocks != EINA_INLIST_GET(itb))
          {
             itb->position =
               ((Item_Block *)(EINA_INLIST_GET(itb)->prev))->position + 1;
          }
        else
          {
             itb->position = 0;
          }
     }

   return itb;
}

static void
_update_block_registration(Item_Block *itb, Elm_Gen_Item *element)
{
   Elm_Gen_Item *it = NULL;
   Eina_List *order = NULL;
   const Eina_List *l;

   if (!itb->realized) return;

   efl_ui_focus_manager_calc_register_logical(itb->sd->obj, EO_OBJ(element), itb->adapter, NULL);

   EINA_LIST_FOREACH(itb->items, l, it)
     {
        order = eina_list_append(order, EO_OBJ(it));
     }

   efl_ui_focus_manager_calc_update_order(itb->sd->obj, itb->adapter, order);
}

/**
 * @internal
 *
 * This function adds an item to a block's item list. This may or may not
 * rearrange existing blocks and create a new block.
 *
 */
static Eina_Bool
_item_block_add(Elm_Genlist_Data *sd,
                Elm_Gen_Item *it)
{
   Item_Block *itb = NULL;

   // when a new item does not depend on another item
   if (!it->item->rel)
     {
newblock:
        if (it->item->rel)
          {
             // FIXME: Why is this case here??? This doesn't make sense at all!
             // There shouldn't be a goto in the first place!
             itb = calloc(1, sizeof(Item_Block));
             if (!itb) return EINA_FALSE;
             itb->sd = sd;
             if (!it->item->rel->item->block)
               {
                  sd->blocks =
                    eina_inlist_append(sd->blocks, EINA_INLIST_GET(itb));
                  itb->items = eina_list_append(itb->items, it);
                  _update_block_registration(itb, it);
                  itb->position_update = EINA_TRUE;
                  it->position = eina_list_count(itb->items);
                  it->position_update = EINA_TRUE;

                  if (sd->blocks != EINA_INLIST_GET(itb))
                    {
                       itb->position =
                         ((Item_Block *)
                          (EINA_INLIST_GET(itb)->prev))->position + 1;
                    }
                  else
                    {
                       itb->position = 0;
                    }
               }
             else
               {
                  Eina_List *tmp;

                  tmp = eina_list_data_find_list(itb->items, it->item->rel);
                  if (it->item->before)
                    {
                       sd->blocks = eina_inlist_prepend_relative
                           (sd->blocks, EINA_INLIST_GET(itb),
                           EINA_INLIST_GET(it->item->rel->item->block));
                       itb->items =
                         eina_list_prepend_relative_list(itb->items, it, tmp);

                       /* Update index from where we prepended */
                       _item_position_update
                         (eina_list_prev(tmp), it->item->rel->position);
                       _item_block_position_update
                         (EINA_INLIST_GET(itb),
                         it->item->rel->item->block->position);
                    }
                  else
                    {
                       sd->blocks = eina_inlist_append_relative
                           (sd->blocks, EINA_INLIST_GET(itb),
                           EINA_INLIST_GET(it->item->rel->item->block));
                       itb->items =
                         eina_list_append_relative_list(itb->items, it, tmp);

                       /* Update block index from where we appended */
                       _item_position_update
                         (eina_list_next(tmp), it->item->rel->position + 1);
                       _item_block_position_update
                         (EINA_INLIST_GET(itb),
                         it->item->rel->item->block->position + 1);
                    }
               }
          }
        else
          {
             // item move_before, prepend, insert_before, sorted_insert with before
             if (it->item->before)
               {
                  if (sd->blocks)
                    {
                       itb = (Item_Block *)(sd->blocks);
                       if (itb->count >= sd->max_items_per_block)
                         {
                            itb = _item_block_new(sd, EINA_TRUE);
                            if (!itb) return EINA_FALSE;
                         }
                    }
                  else
                    {
                       itb = _item_block_new(sd, EINA_TRUE);
                       if (!itb) return EINA_FALSE;
                    }
                  itb->items = eina_list_prepend(itb->items, it);
                  _update_block_registration(itb, it);
                  _item_position_update(itb->items, 0);
               }
             // item move_after, append, insert_after, sorted_insert without before
             else
               {
                  if (sd->blocks)
                    {
                       itb = (Item_Block *)(sd->blocks->last);
                       if (itb->count >= sd->max_items_per_block)
                         {
                            itb = _item_block_new(sd, EINA_FALSE);
                            if (!itb) return EINA_FALSE;
                         }
                    }
                  else
                    {
                       itb = _item_block_new(sd, EINA_FALSE);
                       if (!itb) return EINA_FALSE;
                    }
                  itb->items = eina_list_append(itb->items, it);
                  _update_block_registration(itb, it);
                  it->position = eina_list_count(itb->items);
               }
          }
     }
   // when a new item depends on another item
   else
     {
        Eina_List *tmp;

        if (it->item->rel->item->queued)
          {
             /* NOTE: for a strange reason eina_list and eina_inlist
                don't have the same property on sorted insertion
                order, so the queue is not always ordered like the
                item list.  This lead to issue where we depend on an
                item that is not yet created. As a quick work around,
                we reschedule the calc of the item and stop reordering
                the list to prevent any nasty issue to show up here.
              */
             sd->queue = eina_list_append(sd->queue, it);
             sd->requeued = EINA_TRUE;
             it->item->queued = EINA_TRUE;

             return EINA_FALSE;
          }
        itb = it->item->rel->item->block;
        if (!itb) goto newblock;
        tmp = eina_list_data_find_list(itb->items, it->item->rel);
        if (it->item->before)
          {
             itb->items = eina_list_prepend_relative_list(itb->items, it, tmp);
             _update_block_registration(itb, it);
             _item_position_update
               (eina_list_prev(tmp), it->item->rel->position);
          }
        else
          {
             itb->items = eina_list_append_relative_list(itb->items, it, tmp);
             _update_block_registration(itb, it);
             _item_position_update
               (eina_list_next(tmp), it->item->rel->position + 1);
          }
     }

   itb->count++;
   itb->changed = EINA_TRUE;
   it->item->block = itb;
   efl_canvas_group_change(itb->sd->obj);

   if (itb->count > itb->sd->max_items_per_block)
     {
        int newc;
        Item_Block *itb2;
        Elm_Gen_Item *it2;
        Eina_Bool done = EINA_FALSE;

        newc = itb->count / 2;

        if (EINA_INLIST_GET(itb)->prev)
          {
             Item_Block *itbp = (Item_Block *)(EINA_INLIST_GET(itb)->prev);

             if (itbp->count + newc < sd->max_items_per_block / 2)
               {
                  /* moving items to previous block */
                  while ((itb->count > newc) && (itb->items))
                    {
                       it2 = eina_list_data_get(itb->items);
                       itb->items = eina_list_remove_list
                           (itb->items, itb->items);
                       itb->count--;
                       efl_ui_focus_manager_calc_unregister(itb->sd->obj, EO_OBJ(it2));

                       itbp->items = eina_list_append(itbp->items, it2);
                       _update_block_registration(itbp, it2);
                       it2->item->block = itbp;
                       itbp->count++;
                       if (!it2->hide)
                         {
                            itb->vis_count--;
                            itbp->num--;
                            itbp->vis_count++;
                         }

                       if (it2->realized)
                         {
                            _item_block_realize(itbp);
                         }
                    }

                  done = EINA_TRUE;
               }
          }

        if (!done && EINA_INLIST_GET(itb)->next)
          {
             Item_Block *itbn = (Item_Block *)(EINA_INLIST_GET(itb)->next);

             if (itbn->count + newc < sd->max_items_per_block / 2)
               {
                  /* moving items to next block */
                  while ((itb->count > newc) && (itb->items))
                    {
                       Eina_List *l;

                       l = eina_list_last(itb->items);
                       it2 = eina_list_data_get(l);
                       itb->items = eina_list_remove_list(itb->items, l);
                       itb->count--;
                       efl_ui_focus_manager_calc_unregister(itb->sd->obj, EO_OBJ(it2));

                       itbn->items = eina_list_prepend(itbn->items, it2);
                       _update_block_registration(itbn, it2);
                       it2->item->block = itbn;
                       itbn->count++;
                       if (!it2->hide)
                         {
                            itb->h -= it->item->h;
                            itb->vis_count--;
                            itbn->h += it->item->h;
                            itbn->vis_count++;
                         }

                       if (it2->realized)
                         {
                            _item_block_realize(itbn);
                         }
                    }

                  done = EINA_TRUE;
               }
          }

        if (!done)
          {
             /* moving items to new block */
             itb2 = calloc(1, sizeof(Item_Block));
             if (!itb2) return EINA_FALSE;
             itb2->sd = sd;
             sd->blocks =
               eina_inlist_append_relative(sd->blocks, EINA_INLIST_GET(itb2),
                                           EINA_INLIST_GET(itb));
             itb2->changed = EINA_TRUE;
             while ((itb->count > newc) && (itb->items))
               {
                  Eina_List *l;

                  l = eina_list_last(itb->items);
                  it2 = l->data;
                  itb->items = eina_list_remove_list(itb->items, l);
                  itb->count--;
                  efl_ui_focus_manager_calc_unregister(itb->sd->obj, EO_OBJ(it2));

                  itb2->items = eina_list_prepend(itb2->items, it2);
                  _update_block_registration(itb2, it2);
                  it2->item->block = itb2;
                  itb2->count++;
                  if (!it2->hide)
                    {
                       itb->vis_count--;
                       itb->h -= it2->item->h;
                       itb2->vis_count++;
                       itb2->h += it2->item->h;
                    }

                  if (it2->realized)
                    {
                       _item_block_realize(itb2);
                    }
               }

             itb2->num = itb->num + itb->vis_count;
             itb2->x = itb->x;
             itb2->w = itb->w;
             itb2->y = itb->y + itb->h;
          }
     }

   return EINA_TRUE;
}

static Eina_Bool
_item_process(Elm_Genlist_Data *sd,
              Elm_Gen_Item *it)
{
   if (!_item_block_add(sd, it)) return EINA_FALSE;
   if (!sd->blocks)
     _item_block_realize(it->item->block);

   return EINA_TRUE;
}

static void
_item_process_post(Elm_Genlist_Data *sd, Elm_Gen_Item *it)
{
   Eina_Bool show_me = EINA_FALSE;

   EINA_SAFETY_ON_FALSE_RETURN(it && it->item && it->item->block);
   if (it->item->block->changed)
     {
        show_me = _item_block_recalc
            (it->item->block, it->item->block->num, EINA_TRUE);
        it->item->block->changed = 0;
        if (sd->pan_changed)
          {
             evas_object_smart_changed(sd->pan_obj);
          }
     }
   if (show_me) it->item->block->show_me = EINA_TRUE;

   /* when prepending, move the scroller along with the first selected
    * item to create the illusion that we're watching the selected
    * item this prevents the selected item being scrolled off the
    * viewport
    */
   if (sd->selected && it->item->before && !it->hide)
     {
        int y = 0, h;
        Elm_Object_Item *eo_it2;

        eo_it2 = sd->selected->data;
        ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
        if (!it2->item->block) return;
        if (it2->item->order_num_in < it->item->order_num_in) return;
        elm_interface_scrollable_content_pos_get(sd->obj, NULL, &y);
        evas_object_geometry_get(sd->pan_obj, NULL, NULL, NULL, &h);
        if ((it->y + it->item->block->y > y + h) ||
            (it->y + it->item->block->y + it->item->h < y))
          /* offscreen, just update */
           elm_interface_scrollable_content_region_show
            (sd->obj, it2->x + it2->item->block->x, y, it2->item->block->w, h);
        else
           elm_interface_scrollable_content_region_show
            (sd->obj, it->x + it->item->block->x, y + it->item->h, it->item->block->w, h);
     }
}

static int
_queue_process(Elm_Genlist_Data *sd)
{
   int n;
   double t0, t;
   Evas *e = evas_object_evas_get(sd->obj);

   t0 = ecore_time_get();
   evas_event_freeze(e);
   for (n = 0; (sd->queue) && (n < ITEM_QUEUE_MAX); n++)
     {
        Elm_Gen_Item *it;

        it = eina_list_data_get(sd->queue);
        sd->queue = eina_list_remove_list(sd->queue, sd->queue);
        it->item->queued = EINA_FALSE;
        if (!_item_process(sd, it)) continue;
        t = ecore_time_get();
        _item_process_post(sd, it);
        /* same as eina_inlist_count > 1 */
        if (sd->blocks && sd->blocks->next)
          {
             if ((t - t0) > (ecore_animator_frametime_get())) break;
          }
     }
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
   return n;
}

static Eina_Bool
_idle_process(void *data,
              Eina_Bool *wakeup)
{
   Elm_Genlist_Data *sd = data;

   if (_queue_process(sd) > 0) *wakeup = EINA_TRUE;
   if (!sd->queue)
     {
        return ECORE_CALLBACK_CANCEL;
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_item_idle_enterer(void *data)
{
   Eina_Bool wakeup = EINA_FALSE;
   ELM_GENLIST_DATA_GET(data, sd);
   Eina_Bool ok = _idle_process(sd, &wakeup);

   if (wakeup)
     {
        // wake up mainloop
        efl_canvas_group_change(sd->obj);
     }
   if (ok == ECORE_CALLBACK_CANCEL) sd->queue_idle_enterer = NULL;

   return ok;
}

static void
_requeue_idle_enterer(Elm_Genlist_Data *sd)
{
   ecore_idle_enterer_del(sd->queue_idle_enterer);
   sd->queue_idle_enterer = ecore_idle_enterer_add(_item_idle_enterer, sd->obj);
}

static void
_item_queue(Elm_Genlist_Data *sd,
            Elm_Gen_Item *it,
            Eina_Compare_Cb cb)
{
   Evas_Coord w = 0;

   if (it->item->queued) return;
   it->item->queued = EINA_TRUE;
   if (cb && !sd->requeued)
     sd->queue = eina_list_sorted_insert(sd->queue, cb, it);
   else
     sd->queue = eina_list_append(sd->queue, it);
// FIXME: why does a freeze then thaw here cause some genlist
// elm_genlist_item_append() to be much much slower?
//   evas_event_freeze(evas_object_evas_get(sd->obj));
   while ((sd->queue) && ((!sd->blocks) || (!sd->blocks->next)))
     {
        ELM_SAFE_FREE(sd->queue_idle_enterer, ecore_idle_enterer_del);
        _queue_process(sd);
     }
   while ((sd->queue) && (sd->blocks) &&
          (sd->homogeneous) && (sd->mode == ELM_LIST_COMPRESS))
     {
        ELM_SAFE_FREE(sd->queue_idle_enterer, ecore_idle_enterer_del);
        _queue_process(sd);
     }

//   evas_event_thaw(evas_object_evas_get(sd->obj));
//   evas_event_thaw_eval(evas_object_evas_get(sd->obj));
   evas_object_geometry_get(sd->obj, NULL, NULL, &w, NULL);
   if (w > 0) _requeue_idle_enterer(sd);

   if (_elm_config->atspi_mode)
     {
        efl_access_added(EO_OBJ(it));
        efl_access_children_changed_added_signal_emit(sd->obj, EO_OBJ(it));
     }
}

/* If the application wants to know the relative item, use
 * elm_genlist_item_prev_get(it)*/
static void
_item_move_after(Elm_Gen_Item *it,
                 Elm_Gen_Item *after)
{
   if (!it) return;
   if (!after) return;
   if (it == after) return;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->items =
     eina_inlist_remove(sd->items, EINA_INLIST_GET(it));
   if (it->item->block) _item_block_del(it);

   sd->items = eina_inlist_append_relative
       (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(after));
   if (it->item->rel)
      it->item->rel->item->rel_revs =
         eina_list_remove(it->item->rel->item->rel_revs, it);
   it->item->rel = after;
   after->item->rel_revs = eina_list_append(after->item->rel_revs, it);
   it->item->before = EINA_FALSE;
   if (after->item->group_item) it->item->group_item = after->item->group_item;
   _item_queue(sd, it, NULL);

   efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_MOVED_AFTER, EO_OBJ(it));
}

static void
_access_activate_cb(void *data EINA_UNUSED,
                    Evas_Object *part_obj EINA_UNUSED,
                    Elm_Object_Item *eo_it)
{
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
   if (!it) return;

   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   if (!sd) return;

   if (sd->multi)
     {
        if (!it->selected)
          {
             _item_highlight(it);
             _item_select(it);
          }
        else
          _item_unselect(it);
     }
   else
     {
        if (!it->selected)
          {
             while (sd->selected)
               {
                  Elm_Object_Item *eo_sel = sd->selected->data;
                  Elm_Gen_Item *sel = efl_data_scope_get(eo_sel, ELM_GENLIST_ITEM_CLASS);
                  _item_unselect(sel);
               }
          }
        else
          {
             const Eina_List *l, *l_next;
             Elm_Object_Item *eo_it2;

             EINA_LIST_FOREACH_SAFE(sd->selected, l, l_next, eo_it2)
               {
                  ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
                  if (it2 != it)
                    _item_unselect(it2);
               }
          }
        _item_highlight(it);
        _item_select(it);
     }
}

/* If the application wants to know the relative item, use
 * elm_genlist_item_next_get(it)*/
static void
_item_move_before(Elm_Gen_Item *it,
                  Elm_Gen_Item *before)
{
   if (!it) return;
   if (!before) return;
   if (it == before) return;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->items =
     eina_inlist_remove(sd->items, EINA_INLIST_GET(it));
   if (it->item->block) _item_block_del(it);
   sd->items = eina_inlist_prepend_relative
       (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(before));
   if (it->item->rel)
      it->item->rel->item->rel_revs =
         eina_list_remove(it->item->rel->item->rel_revs, it);
   it->item->rel = before;
   before->item->rel_revs = eina_list_append(before->item->rel_revs, it);
   it->item->before = EINA_TRUE;
   if (before->item->group_item)
     it->item->group_item = before->item->group_item;
   _item_queue(sd, it, NULL);

   efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_MOVED_BEFORE, EO_OBJ(it));
}

static void
_item_mouse_up_cb(void *data,
                  Evas *evas EINA_UNUSED,
                  Evas_Object *obj EINA_UNUSED,
                  void *event_info)
{
   Evas_Event_Mouse_Up *ev = event_info;
   Eina_Bool dragged = EINA_FALSE;
   Elm_Gen_Item *it = data;
   Evas_Coord x, y, dx, dy;

   if ((ev->button == 3) && (!it->dragging))
     {
        evas_object_geometry_get(obj, &x, &y, NULL, NULL);
        dx = it->dx - (ev->canvas.x - x);
        dy = it->dy - (ev->canvas.y - y);
        if (dx < 0) dx = -dx;
        if (dy < 0) dy = -dy;
        if ((dx < 5) && (dy < 5))
          evas_object_smart_callback_call
                ( WIDGET(it), "clicked,right", EO_OBJ(it));
        return;
     }

   if (ev->button != 1) return;
   it->down = EINA_FALSE;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   evas_object_ref(WIDGET(it));
   efl_ref(EO_OBJ(it));
   sd->mouse_down = EINA_FALSE;

   efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_RELEASED, EO_OBJ(it));

   if (sd->multi_touched)
     {
        if ((!sd->multi) && (!it->selected) && (it->highlighted))
          _item_unhighlight(it);
        if (sd->multi_down) goto early;
        _multi_touch_gesture_eval(it);
        goto early;
     }
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     sd->on_hold = EINA_TRUE;
   else sd->on_hold = EINA_FALSE;
   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);
   if (it->dragging)
     {
        it->dragging = EINA_FALSE;
        evas_object_smart_callback_call
              (WIDGET(it), "drag,stop", EO_OBJ(it));
        dragged = 1;
     }
   ELM_SAFE_FREE(it->item->swipe_timer, ecore_timer_del);
   if (sd->multi_timer)
     {
        ELM_SAFE_FREE(sd->multi_timer, ecore_timer_del);
        sd->multi_timeout = EINA_FALSE;
     }
   if (sd->swipe)
     {
        if (!sd->wasselected) _item_unselect(it);
        _swipe_do(it);
        sd->longpressed = EINA_FALSE;
        sd->on_hold = EINA_FALSE;
        sd->wasselected = EINA_FALSE;
        goto early;
     }
   if ((sd->reorder_mode) && (sd->reorder_it))
     {
        Evas_Coord it_scrl_y = ev->canvas.y - sd->reorder_it->dy;
        sd->reorder_fast = 0;

        if (sd->reorder_rel &&
            (sd->reorder_it->parent == sd->reorder_rel->parent))
          {
             if (it_scrl_y <= sd->reorder_rel->item->scrl_y)
               _item_move_before(sd->reorder_it, sd->reorder_rel);
             else
               _item_move_after(sd->reorder_it, sd->reorder_rel);
             efl_event_callback_legacy_call(WIDGET(it), ELM_GENLIST_EVENT_MOVED, EO_OBJ(it));
          }
        else
          {
             efl_canvas_group_change(sd->obj);
          }
        edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_DISABLED, "elm");
        if (_elm_config->atspi_mode)
          efl_access_state_changed_signal_emit(EO_OBJ(it), EFL_ACCESS_STATE_TYPE_ANIMATED, EINA_FALSE);
        sd->reorder_it = sd->reorder_rel = NULL;
        elm_interface_scrollable_hold_set(sd->obj, EINA_FALSE);
        elm_interface_scrollable_bounce_allow_set
              (sd->obj, sd->h_bounce, sd->v_bounce);
     }
   if (sd->longpressed)
     {
        if (!sd->wasselected) _item_unselect(it);
        sd->longpressed = EINA_FALSE;
        sd->wasselected = EINA_FALSE;
        goto early;
     }
   if (dragged)
     {
        if (it->want_unrealize)
          {
             _elm_genlist_item_unrealize(it, EINA_FALSE);
             if (it->item->block->want_unrealize)
               _item_block_unrealize(it->item->block);
          }
     }

   /* this is handled implicitly in _item_unselect() below unless there
    * will be no select
    */
   if (!it->selected && it->highlighted && _is_no_select(it))
     _item_unhighlight(it);

   if ((ev->flags != EVAS_BUTTON_NONE) ||
       (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) ||
       !it->base->still_in ||
       _is_no_select(it) ||
       (elm_wdg_item_disabled_get(EO_OBJ(it))))
     goto early;

   evas_object_ref(sd->obj);

   if (sd->focused_item != EO_OBJ(it))
     elm_object_item_focus_set(EO_OBJ(it), EINA_TRUE);

   if (efl_invalidated_get(EO_OBJ(it))) goto deleted;

   if (sd->multi &&
       ((sd->multi_select_mode != ELM_OBJECT_MULTI_SELECT_MODE_WITH_CONTROL) ||
        (evas_key_modifier_is_set(ev->modifiers, "Control"))))
     {
        if (!it->selected)
          {
             /* this should only be handled on mouse down
             _item_highlight(it);
             */
             if (_item_select(it)) goto deleted;
          }
        else
         _item_unselect(it);
     }
   else
     {
        if (!it->selected)
          {
             while (sd->selected)
               {
                  Elm_Object_Item *eo_sel = sd->selected->data;
                  Elm_Gen_Item *sel = efl_data_scope_get(eo_sel, ELM_GENLIST_ITEM_CLASS);
                  _item_unselect(sel);
               }
          }
        else
          {
             const Eina_List *l, *l_next;
             Elm_Object_Item *eo_it2;

             EINA_LIST_FOREACH_SAFE(sd->selected, l, l_next, eo_it2)
               {
                  ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
                  if (it2 != it)
                    _item_unselect(it2);
               }
          }
        /* this should only be handled on mouse down
        _item_highlight(it);
        */
        if (_item_select(it)) goto deleted;
     }

 deleted:
   evas_object_unref(sd->obj);
 early:
   evas_object_unref(WIDGET(it));
   efl_unref(EO_OBJ(it));
}

static void
_item_mouse_callbacks_add(Elm_Gen_Item *it,
                          Evas_Object *view)
{
   if (it->callbacks) return ;
   it->callbacks = EINA_TRUE;
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MOUSE_DOWN, _item_mouse_down_cb, it);
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MOUSE_UP, _item_mouse_up_cb, it);
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MOUSE_MOVE, _item_mouse_move_cb, it);
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MULTI_DOWN, _item_multi_down_cb, it);
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MULTI_UP, _item_multi_up_cb, it);
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MULTI_MOVE, _item_multi_move_cb, it);
   evas_object_event_callback_add
     (view, EVAS_CALLBACK_MOUSE_IN, _item_mouse_in_cb, it);
}

static void
_item_mouse_callbacks_del(Elm_Gen_Item *it,
                          Evas_Object *view)
{
   if (!it->callbacks) return ;
   it->callbacks = EINA_FALSE;
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MOUSE_DOWN, _item_mouse_down_cb, it);
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MOUSE_UP, _item_mouse_up_cb, it);
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MOUSE_MOVE, _item_mouse_move_cb, it);
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MULTI_DOWN, _item_multi_down_cb, it);
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MULTI_UP, _item_multi_up_cb, it);
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MULTI_MOVE, _item_multi_move_cb, it);
   evas_object_event_callback_del_full
     (view, EVAS_CALLBACK_MOUSE_IN, _item_mouse_in_cb, it);
}

static Eina_Bool
_scroll_hold_timer_cb(void *data)
{
   ELM_GENLIST_DATA_GET(data, sd);

   if (!data) return ECORE_CALLBACK_CANCEL;

   elm_interface_scrollable_hold_set(sd->obj, EINA_FALSE);
   sd->scr_hold_timer = NULL;

   return ECORE_CALLBACK_CANCEL;
}

static void
_decorate_item_unrealize(Elm_Gen_Item *it, Eina_Bool state_update)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Evas_Object *obj = sd->obj;
   Evas *e;

   if (!it->item->deco_it_view) return;

   e = evas_object_evas_get(obj);
   evas_event_freeze(e);

   _view_clear(it->item->deco_it_view, &(it->item->deco_it_texts),
               &(it->item->deco_it_contents));

   edje_object_part_unswallow(it->item->deco_it_view, VIEW(it));
   ELM_SAFE_FREE(it->item->deco_it_view, evas_object_del);
   evas_object_smart_member_add(VIEW(it), sd->pan_obj);
   evas_object_clip_set(VIEW(it), evas_object_clip_get(sd->pan_obj));
   if (state_update)
     {
        _elm_genlist_item_state_update(it);
        _item_order_update(it, it->item->order_num_in, EINA_TRUE);
     }

   if (sd->mode_item != it)
     it->decorate_it_set = EINA_FALSE;

   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static void
_decorate_item_finished_signal_cb(void *data,
                                  Evas_Object *obj,
                                  const char *emission EINA_UNUSED,
                                  const char *source EINA_UNUSED)
{
   Elm_Gen_Item *it = data;
   char buf[1024];
   Evas *te;

   if (!it || !obj) return;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   te = evas_object_evas_get(obj);

   if ((!it->realized) || (!it->item->deco_it_view)) return;

   evas_event_freeze(te);
   snprintf(buf, sizeof(buf), "elm,state,%s,passive,finished",
            sd->decorate_it_type);
   edje_object_signal_callback_del_full
     (it->item->deco_it_view, buf, "elm", _decorate_item_finished_signal_cb, it);
   it->item->nocache_once = EINA_FALSE;
   _decorate_item_unrealize(it, EINA_TRUE);
   if (it->item->group_item)
     evas_object_stack_above(it->item->VIEW(group_item), sd->stack[1]);

   evas_event_thaw(te);
   evas_event_thaw_eval(te);
}

static void
_item_unrealize(Elm_Gen_Item *it)
{
   Evas_Object *c;
   Eina_List *cache = NULL;

   EINA_LIST_FREE(it->item->flip_contents, c)
     evas_object_del(c);

   /* access */
   if (_elm_config->access_mode == ELM_ACCESS_MODE_ON)
     _elm_access_widget_item_unregister(it->base);

   // unswallow VIEW(it) first then manipulate VIEW(it)
   _decorate_item_unrealize(it, EINA_FALSE);
   if (it->item->wsd->decorate_all_mode) _decorate_all_item_unrealize(it);

   if (!_item_cache_add(it, _content_cache_add(it, &cache)))
     {
        Evas_Object *view = VIEW(it);
        efl_wref_del(VIEW(it), &VIEW(it));
        ELM_SAFE_FREE(view, efl_del);
        VIEW(it) = NULL;
        it->callbacks = EINA_FALSE;
        ELM_SAFE_FREE(it->spacer, evas_object_del);
        EINA_LIST_FREE(cache, c)
          {
             evas_object_del(c);
          }
     }

   it->states = NULL;
   it->realized = EINA_FALSE;
   it->want_unrealize = EINA_FALSE;
}

static void
_item_temp_realize(Elm_Gen_Item *it, const int index)
{
   it->item->unfocusable = EINA_TRUE;
   _item_realize(it, index, EINA_TRUE);
   _elm_genlist_item_unrealize(it, EINA_TRUE);
   it->item->unfocusable = EINA_FALSE;
}

static Eina_Bool
_item_block_recalc(Item_Block *itb, const int blk_idx, Eina_Bool qadd)
{
   const Eina_List *l;
   Elm_Gen_Item *it;
   Evas_Coord minw = 0, minh = 0;
   Eina_Bool show_me = EINA_FALSE, changed = EINA_FALSE;
   Evas_Coord y = 0;
   Item_Size *size = NULL;
   int vis_count = 0;

   itb->num = blk_idx;
   EINA_LIST_FOREACH(itb->items, l, it)
     {
        show_me |= it->item->show_me;

        if (!_item_filtered_get(it, itb->sd))
          {
             if (it->realized) evas_object_hide(VIEW(it));
             continue;
          }
        if (!itb->realized)
          {
             if (itb->sd->homogeneous &&
                 ((!size) || it->itc != size->itc))
               size = eina_hash_find(itb->sd->size_caches, &(it->itc));
             if (qadd || (itb->sd->homogeneous && !size))
               {
                  if (!it->item->mincalcd) changed = EINA_TRUE;
                  if (changed)
                    {
                       if (!size || (it->item->expanded_depth != size->expanded_depth))
                         {
                            _item_temp_realize(it, blk_idx + vis_count);
                         }
                       else
                         {
                            it->item->w = it->item->minw = size->minw;
                            it->item->h = it->item->minh = size->minh;
                            it->item->mincalcd = EINA_TRUE;
                         }
                    }
               }
             else
               {
                  if ((itb->sd->homogeneous) && size &&
                      (it->item->expanded_depth == size->expanded_depth) &&
                      (itb->sd->mode == ELM_LIST_COMPRESS))
                    {
                       it->item->w = it->item->minw = size->minw;
                       it->item->h = it->item->minh = size->minh;
                       it->item->mincalcd = EINA_TRUE;
                    }
                  else
                    _item_temp_realize(it, blk_idx + vis_count);
               }
          }
        else
          {
             if (!it->item->mincalcd) changed = EINA_TRUE;
             _item_realize(it, blk_idx + vis_count, EINA_FALSE);
          }
        minh += it->item->minh;
        if (minw < it->item->minw) minw = it->item->minw;
        vis_count++;
        it->x = 0;
        it->y = y;
        y += it->item->h;
     }
   if (changed) itb->sd->pan_changed = changed;
   itb->minw = minw;
   itb->minh = minh;
   itb->changed = EINA_FALSE;
   itb->position_update = EINA_FALSE;
   itb->vis_count = vis_count;

   return show_me;
}

static void
_update_job(void *data)
{
   Eina_Bool position = EINA_FALSE, recalc = EINA_FALSE;
   ELM_GENLIST_DATA_GET(data, sd);
   Item_Block *itb;
   Eina_List *l2;
   int num, num0;
   Evas *e = evas_object_evas_get(sd->obj);;

   sd->update_job = NULL;
   num = 0;

   evas_event_freeze(e);
   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        Evas_Coord itminw, itminh;
        Elm_Gen_Item *it;

        if (!itb->updateme)
          {
             if (position)
               _item_block_position(itb, num);
             num += itb->vis_count;
             continue;
          }
        num0 = num;
        recalc = EINA_FALSE;
        EINA_LIST_FOREACH(itb->items, l2, it)
          {
             if (it->item->updateme)
               {
                  itminw = it->item->minw;
                  itminh = it->item->minh;

                  it->item->updateme = EINA_FALSE;
                  if (it->realized)
                    {
                       _elm_genlist_item_unrealize(it, EINA_FALSE);
                       _item_realize(it, num, EINA_FALSE);
                       position = EINA_TRUE;
                    }
                  else
                    {
                       _item_realize(it, num, EINA_TRUE);
                       _elm_genlist_item_unrealize(it, EINA_TRUE);
                    }
                  if ((it->item->minw != itminw) || (it->item->minh != itminh))
                    recalc = EINA_TRUE;
               }
             if (!it->hide) num++;
          }
        itb->updateme = EINA_FALSE;
        if (recalc)
          {
             position = EINA_TRUE;
             itb->changed = EINA_TRUE;
             _item_block_recalc(itb, num0, EINA_FALSE);
             _item_block_position(itb, num0);
          }
     }
   if (position)
     {
        efl_canvas_group_change(sd->pan_obj);
        efl_canvas_group_change(sd->obj);
     }
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static void
_scroll_animate_start_cb(Evas_Object *obj,
                         void *data EINA_UNUSED)
{
   evas_object_smart_callback_call(obj, "scroll,anim,start", NULL);
}

static void
_scroll_animate_stop_cb(Evas_Object *obj,
                        void *data EINA_UNUSED)
{
   evas_object_smart_callback_call(obj, "scroll,anim,stop", NULL);
}

static void
_scroll_drag_start_cb(Evas_Object *obj,
                      void *data EINA_UNUSED)
{
   evas_object_smart_callback_call(obj, "scroll,drag,start", NULL);
}

static void
_scroll_cb(Evas_Object *obj,
           void *data EINA_UNUSED)
{
   evas_object_smart_callback_call(obj, "scroll", NULL);
}

static void
_scroll_drag_stop_cb(Evas_Object *obj,
                     void *data EINA_UNUSED)
{
   evas_object_smart_callback_call(obj, "scroll,drag,stop", NULL);
}

static void
_edge_left_cb(Evas_Object *obj,
              void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_EDGE_LEFT, NULL);
}

static void
_edge_right_cb(Evas_Object *obj,
               void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_EDGE_RIGHT, NULL);
}

static void
_edge_top_cb(Evas_Object *obj,
             void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_EDGE_TOP, NULL);
}

static void
_edge_bottom_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_EDGE_BOTTOM, NULL);
}

static void
_vbar_drag_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_VBAR_DRAG, NULL);
}

static void
_vbar_press_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_VBAR_PRESS, NULL);
}

static void
_vbar_unpress_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_VBAR_UNPRESS, NULL);
}

static void
_hbar_drag_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_HBAR_DRAG, NULL);
}

static void
_hbar_press_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_HBAR_PRESS, NULL);
}

static void
_hbar_unpress_cb(Evas_Object *obj,
                void *data EINA_UNUSED)
{
   efl_event_callback_legacy_call(obj, ELM_GENLIST_EVENT_HBAR_UNPRESS, NULL);
}

static void
_decorate_item_realize(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Evas_Object *obj = sd->obj;
   char buf[1024];
   Evas *e;

   if (it->item->deco_it_view) return;

   e = evas_object_evas_get(obj);
   evas_event_freeze(e);
   it->item->deco_it_view = _view_create(it, it->itc->decorate_item_style);

   /* signal callback add */
   evas_object_event_callback_add
     (it->item->deco_it_view, EVAS_CALLBACK_MOUSE_DOWN, _item_mouse_down_cb,
     it);
   evas_object_event_callback_add
     (it->item->deco_it_view, EVAS_CALLBACK_MOUSE_UP, _item_mouse_up_cb, it);
   evas_object_event_callback_add
     (it->item->deco_it_view, EVAS_CALLBACK_MOUSE_MOVE, _item_mouse_move_cb,
     it);

   _view_inflate(it->item->deco_it_view, it, &it->item->deco_it_texts,
                 &it->item->deco_it_contents, EINA_FALSE);
   edje_object_part_swallow
     (it->item->deco_it_view,
     edje_object_data_get(it->item->deco_it_view, "mode_part"), VIEW(it));

   snprintf(buf, sizeof(buf), "elm,state,%s,active", sd->decorate_it_type);
   edje_object_signal_emit(it->item->deco_it_view, buf, "elm");
   edje_object_signal_emit(VIEW(it), buf, "elm");

   it->want_unrealize = EINA_FALSE;
   evas_event_thaw(e);
   evas_event_thaw_eval(e);
}

static void
_decorate_item_set(Elm_Gen_Item *it)
{
   Evas *e;
   if (!it) return;

   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->mode_item = it;
   it->item->nocache_once = EINA_TRUE;

   ELM_SAFE_FREE(sd->scr_hold_timer, ecore_timer_del);
   elm_interface_scrollable_hold_set(sd->obj, EINA_TRUE);
   sd->scr_hold_timer = ecore_timer_add(SCR_HOLD_TIME, _scroll_hold_timer_cb, sd->obj);

   e = evas_object_evas_get(sd->obj);
   evas_event_freeze(e);
   _decorate_item_realize(it);
   if (it->item->group_item)
     evas_object_stack_above(it->item->VIEW(group_item), sd->stack[1]);
   _item_position
     (it, it->item->deco_it_view, it->item->scrl_x, it->item->scrl_y);
   evas_event_thaw(e);
   evas_event_thaw_eval(e);

}

static void
_decorate_item_unset(Elm_Genlist_Data *sd)
{
   char buf[1024], buf2[1024];
   Elm_Gen_Item *it;

   if (!sd->mode_item) return;

   it = sd->mode_item;
   it->item->nocache_once = EINA_TRUE;
   it->decorate_it_set = EINA_FALSE;

   snprintf(buf, sizeof(buf), "elm,state,%s,passive", sd->decorate_it_type);
   snprintf(buf2, sizeof(buf2), "elm,state,%s,passive,finished",
            sd->decorate_it_type);
   edje_object_signal_emit(it->item->deco_it_view, buf, "elm");
   edje_object_signal_callback_add
     (it->item->deco_it_view, buf2, "elm", _decorate_item_finished_signal_cb,
     it);
   sd->mode_item = NULL;
}

static void
_elm_genlist_looping_up_cb(void *data,
                           Evas_Object *obj EINA_UNUSED,
                           const char *emission EINA_UNUSED,
                           const char *source EINA_UNUSED)
{
   Evas_Object *genlist = data;

   ELM_GENLIST_DATA_GET(genlist, sd);

   Elm_Object_Item *eo_it = elm_genlist_last_item_get(genlist);

   elm_genlist_item_show(eo_it, ELM_GENLIST_ITEM_SCROLLTO_IN);
   elm_layout_signal_emit(genlist, "elm,action,looping,up,end", "elm");
   sd->item_looping_on = EINA_FALSE;

   if (!_elm_config->item_select_on_focus_disable)
     elm_genlist_item_selected_set(eo_it, EINA_TRUE);
   else
     elm_object_item_focus_set(eo_it, EINA_TRUE);
}

static void
_elm_genlist_looping_down_cb(void *data,
                             Evas_Object *obj EINA_UNUSED,
                             const char *emission EINA_UNUSED,
                             const char *source EINA_UNUSED)
{
   Evas_Object *genlist = data;

   ELM_GENLIST_DATA_GET(genlist, sd);

   Elm_Object_Item *eo_it = elm_genlist_first_item_get(genlist);

   elm_genlist_item_show(eo_it, ELM_GENLIST_ITEM_SCROLLTO_IN);
   elm_layout_signal_emit(genlist, "elm,action,looping,down,end", "elm");
   sd->item_looping_on = EINA_FALSE;

   if (!_elm_config->item_select_on_focus_disable)
     elm_genlist_item_selected_set(eo_it, EINA_TRUE);
   else
     elm_object_item_focus_set(eo_it, EINA_TRUE);
}

static void
_evas_viewport_resize_cb(void *d, Evas *e EINA_UNUSED, void *ei EINA_UNUSED)
{
   Elm_Genlist_Data *priv = d;
   evas_object_smart_changed(priv->pan_obj);
}

EOLIAN static void
_elm_genlist_efl_canvas_group_group_add(Eo *obj, Elm_Genlist_Data *priv)
{
   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
   Elm_Genlist_Pan_Data *pan_data;
   Evas_Coord minw, minh;
   Evas *e = evas_object_evas_get(obj);
   int i;

   efl_canvas_group_add(efl_super(obj, MY_CLASS));

   priv->size_caches = eina_hash_pointer_new(_size_cache_free);
   priv->hit_rect = evas_object_rectangle_add(e);
   evas_object_smart_member_add(priv->hit_rect, obj);
   elm_widget_sub_object_add(obj, priv->hit_rect);

   /* common scroller hit rectangle setup */
   evas_object_color_set(priv->hit_rect, 0, 0, 0, 0);
   evas_object_show(priv->hit_rect);
   evas_object_repeat_events_set(priv->hit_rect, EINA_TRUE);

   elm_widget_can_focus_set(obj, EINA_TRUE);
   elm_widget_on_show_region_hook_set(obj, NULL, _show_region_hook, NULL);

   if (!elm_layout_theme_set
       (obj, "genlist", "base", elm_widget_style_get(obj)))
     CRI("Failed to set layout!");

   elm_interface_scrollable_objects_set(obj, wd->resize_obj, priv->hit_rect);

   elm_interface_scrollable_bounce_allow_set
         (obj, EINA_FALSE, _elm_config->thumbscroll_bounce_enable);
   priv->v_bounce = _elm_config->thumbscroll_bounce_enable;

   elm_interface_scrollable_animate_start_cb_set(obj, _scroll_animate_start_cb);
   elm_interface_scrollable_animate_stop_cb_set(obj, _scroll_animate_stop_cb);
   elm_interface_scrollable_scroll_cb_set(obj, _scroll_cb);
   elm_interface_scrollable_drag_start_cb_set(obj, _scroll_drag_start_cb);
   elm_interface_scrollable_drag_stop_cb_set(obj, _scroll_drag_stop_cb);
   elm_interface_scrollable_edge_left_cb_set(obj, _edge_left_cb);
   elm_interface_scrollable_edge_right_cb_set(obj, _edge_right_cb);
   elm_interface_scrollable_edge_top_cb_set(obj, _edge_top_cb);
   elm_interface_scrollable_edge_bottom_cb_set(obj, _edge_bottom_cb);
   elm_interface_scrollable_vbar_drag_cb_set(obj, _vbar_drag_cb);
   elm_interface_scrollable_vbar_press_cb_set(obj, _vbar_press_cb);
   elm_interface_scrollable_vbar_unpress_cb_set(obj, _vbar_unpress_cb);
   elm_interface_scrollable_hbar_drag_cb_set(obj, _hbar_drag_cb);
   elm_interface_scrollable_hbar_press_cb_set(obj, _hbar_press_cb);
   elm_interface_scrollable_hbar_unpress_cb_set(obj, _hbar_unpress_cb);
   elm_interface_scrollable_content_min_limit_cb_set(obj, _content_min_limit_cb);

   priv->mode = ELM_LIST_SCROLL;
   priv->max_items_per_block = MAX_ITEMS_PER_BLOCK;
   priv->item_cache_max = priv->max_items_per_block * 2;
   priv->longpress_timeout = _elm_config->longpress_timeout;
   priv->highlight = EINA_TRUE;
   priv->pin_item = NULL;
   priv->pin_item_top = EINA_FALSE;

   priv->pan_obj = efl_add(MY_PAN_CLASS, e);
   pan_data = efl_data_scope_get(priv->pan_obj, MY_PAN_CLASS);
   efl_data_ref(obj, MY_CLASS);
   pan_data->wobj = obj;
   pan_data->wsd = priv;

   for (i = 0; i < 2; i++)
     {
        priv->stack[i] = evas_object_rectangle_add(e);
        evas_object_smart_member_add(priv->stack[i], priv->pan_obj);
     }

   elm_interface_scrollable_extern_pan_set(obj, priv->pan_obj);

   edje_object_size_min_calc(wd->resize_obj, &minw, &minh);
   evas_object_size_hint_min_set(obj, minw, minh);

   _mirrored_set(obj, efl_ui_mirrored_get(obj));

   elm_layout_sizing_eval(obj);

   edje_object_signal_callback_add(wd->resize_obj, "elm,looping,up,done", "elm", _elm_genlist_looping_up_cb, obj);
   edje_object_signal_callback_add(wd->resize_obj, "elm,looping,down,done", "elm", _elm_genlist_looping_down_cb, obj);
   evas_event_callback_add(e,
                           EVAS_CALLBACK_CANVAS_VIEWPORT_RESIZE,
                           _evas_viewport_resize_cb, priv);
}

EOLIAN static void
_elm_genlist_efl_canvas_group_group_del(Eo *obj, Elm_Genlist_Data *sd)
{
   int i;

   elm_genlist_clear(obj);
   _item_cache_zero(sd);

   efl_canvas_group_del(efl_super(obj, MY_CLASS));

   for (i = 0; i < 2; i++)
     ELM_SAFE_FREE(sd->stack[i], evas_object_del);

   evas_event_callback_del_full(evas_object_evas_get(obj),
                                EVAS_CALLBACK_CANVAS_VIEWPORT_RESIZE,
                                _evas_viewport_resize_cb, sd);

   ELM_SAFE_FREE(sd->update_job, ecore_job_del);
   ELM_SAFE_FREE(sd->pan_obj, evas_object_del);
   ELM_SAFE_FREE(sd->queue_idle_enterer, ecore_idle_enterer_del);
   ELM_SAFE_FREE(sd->must_recalc_idler, ecore_idler_del);
   ELM_SAFE_FREE(sd->multi_timer, ecore_timer_del);
   ELM_SAFE_FREE(sd->size_caches, eina_hash_free);

   eina_stringshare_replace(&sd->decorate_it_type, NULL);

   eina_hash_free(sd->content_item_map);
   _elm_genlist_tree_effect_stop(sd);
}

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

   efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos);
   efl_gfx_entity_position_set(sd->hit_rect, pos);
}

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

   efl_gfx_entity_size_set(sd->hit_rect, sz);
   if ((sd->queue) && (!sd->queue_idle_enterer) && (sz.w > 0))
     _requeue_idle_enterer(sd);

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

EOLIAN static void
_elm_genlist_efl_canvas_group_group_member_add(Eo *obj, Elm_Genlist_Data *sd, Evas_Object *member)
{
   efl_canvas_group_member_add(efl_super(obj, MY_CLASS), member);

   if (sd->hit_rect)
     evas_object_raise(sd->hit_rect);
}

static void
_access_obj_process(Elm_Genlist_Data *sd, Eina_Bool is_access)
{
   Item_Block *itb;
   Eina_Bool done = EINA_FALSE;

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        if (itb->realized)
          {
             Eina_List *l;
             Elm_Gen_Item *it;

             done = EINA_TRUE;
             EINA_LIST_FOREACH(itb->items, l, it)
               {
                  if (!it->realized || it->hide) continue;
                  if (is_access) _access_widget_item_register(it);
                  else
                    _elm_access_widget_item_unregister(it->base);
               }
          }
        else if (done) break;
     }
}

EOLIAN static void
_elm_genlist_efl_ui_widget_on_access_update(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool acs)
{
   _elm_genlist_smart_focus_next_enable = acs;
   _access_obj_process(sd, _elm_genlist_smart_focus_next_enable);
}

EAPI Evas_Object *
elm_genlist_add(Evas_Object *parent)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
   return elm_legacy_add(MY_CLASS, parent);
}

static void
_genlist_element_focused(void *data, const Efl_Event *ev)
{
   ELM_GENLIST_DATA_GET(data, pd);
   Efl_Ui_Widget *focused = efl_ui_focus_manager_focus_get(ev->object);
   Elm_Widget_Item *item;

   if (!focused || focused == data) return;

   item = efl_ui_focus_parent_provider_gen_item_fetch(pd->provider, focused);

   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(item, ELM_GENLIST_ITEM_CLASS));
   elm_object_item_focus_set(item, EINA_TRUE);
}

EOLIAN static Eo *
_elm_genlist_efl_object_constructor(Eo *obj, Elm_Genlist_Data *sd)
{
   legacy_efl_ui_focus_manager_widget_legacy_signals(obj, obj);
   obj = efl_constructor(efl_super(obj, MY_CLASS));

   sd->content_item_map = eina_hash_pointer_new(NULL);
   sd->provider = efl_add(EFL_UI_FOCUS_PARENT_PROVIDER_GEN_CLASS, obj,
    efl_ui_focus_parent_provider_gen_container_set(efl_added, obj),
    efl_ui_focus_parent_provider_gen_content_item_map_set(efl_added, sd->content_item_map));

   sd->obj = obj;

   efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY);
   evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks);
   efl_access_object_role_set(obj, EFL_ACCESS_ROLE_LIST);

   efl_event_callback_add(obj, EFL_UI_FOCUS_MANAGER_EVENT_MANAGER_FOCUS_CHANGED, _genlist_element_focused, obj);

   return obj;
}

static void
_internal_elm_genlist_clear(Evas_Object *obj)
{
   ELM_GENLIST_DATA_GET(obj, sd);
   Elm_Gen_Item *it;
   Evas *e = evas_object_evas_get(sd->obj);

   _elm_genlist_item_unfocused(sd->focused_item);
   if (sd->mode_item) sd->mode_item = NULL;

   ELM_SAFE_FREE(sd->state, eina_inlist_sorted_state_free);

   sd->filter_data = NULL;
   if (sd->filter_queue)
     ELM_SAFE_FREE(sd->queue_filter_enterer, ecore_idle_enterer_del);
   ELM_SAFE_FREE(sd->filter_queue, eina_list_free);
   ELM_SAFE_FREE(sd->filtered_list, eina_list_free);

   evas_event_freeze(e);

   // Do not use EINA_INLIST_FOREACH or EINA_INLIST_FOREACH_SAFE
   // because sd->items can be modified inside elm_widget_item_del()
   while (sd->items)
     {
        it = EINA_INLIST_CONTAINER_GET(sd->items, Elm_Gen_Item);
        efl_del(EO_OBJ(it));
     }

   sd->pan_changed = EINA_TRUE;
   if (!sd->queue)
     {
        sd->anchor_item = NULL;
        ELM_SAFE_FREE(sd->queue_idle_enterer, ecore_idle_enterer_del);
        ELM_SAFE_FREE(sd->must_recalc_idler, ecore_idler_del);
        ELM_SAFE_FREE(sd->reorder_move_animator, ecore_animator_del);
        sd->reorder_old_pan_y = 0;
     }

   if (sd->selected) ELM_SAFE_FREE(sd->selected, eina_list_free);

   sd->show_item = NULL;

   sd->pan_x = 0;
   sd->pan_y = 0;
   sd->minw = 0;
   sd->minh = 0;

   if (sd->pan_obj)
     {
        evas_object_size_hint_min_set(sd->pan_obj, sd->minw, sd->minh);
        efl_event_callback_legacy_call
              (sd->pan_obj, ELM_PAN_EVENT_CHANGED, NULL);
     }
   elm_layout_sizing_eval(sd->obj);
   elm_interface_scrollable_content_region_show(obj, 0, 0, 0, 0);

   _elm_genlist_tree_effect_stop(sd);

   ELM_SAFE_FREE(sd->multi_timer, ecore_timer_del);
   ELM_SAFE_FREE(sd->update_job, ecore_job_del);
   ELM_SAFE_FREE(sd->queue_idle_enterer, ecore_idle_enterer_del);
   ELM_SAFE_FREE(sd->must_recalc_idler, ecore_idler_del);
   ELM_SAFE_FREE(sd->event_block_rect, evas_object_del);
   ELM_SAFE_FREE(sd->scr_hold_timer, ecore_timer_del);
   ELM_SAFE_FREE(sd->queue, eina_list_free);

   evas_event_thaw(e);
   evas_event_thaw_eval(e);

   _elm_widget_focus_highlight_start(obj);
}

/* Return EINA_TRUE if the item is deleted in this function */
static Eina_Bool
_item_select(Elm_Gen_Item *it)
{
   Evas_Object *obj = WIDGET(it);
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   Elm_Object_Item *eo_it = EO_OBJ(it);
   Eina_Bool r = EINA_FALSE;

   if (elm_wdg_item_disabled_get(eo_it)) return EINA_FALSE;
   if (_is_no_select(it) || (it->decorate_it_set)) return EINA_FALSE;
   if ((sd->select_mode != ELM_OBJECT_SELECT_MODE_ALWAYS) &&
       (it->select_mode != ELM_OBJECT_SELECT_MODE_ALWAYS) && it->selected)
     return EINA_FALSE;

   if (!sd->multi)
     {
        const Eina_List *l, *ll;
        Elm_Object_Item *eo_it2;
        EINA_LIST_FOREACH_SAFE(sd->selected, l, ll, eo_it2)
         {
            ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
            if (it2 != it) _item_unselect(it2);
         }
     }

   if (!it->selected)
     {
        it->selected = EINA_TRUE;
        sd->selected =
          eina_list_append(sd->selected, eo_it);
     }

   efl_ref(eo_it);

   elm_object_item_focus_set(eo_it, EINA_TRUE);
   if ((it->base)->on_deletion) goto item_deleted;
   _elm_genlist_item_content_focus_set(it, ELM_FOCUS_PREVIOUS);
   sd->last_selected_item = eo_it;
   _item_highlight(it);

   if (it->func.func) it->func.func((void *)it->func.data, WIDGET(it), eo_it);
   // delete item if it's requested deletion in the above callbacks.
   if ((it->base)->on_deletion) goto item_deleted;
   evas_object_smart_callback_call(WIDGET(it), "selected", eo_it);
   if (_elm_config->atspi_mode)
     efl_access_state_changed_signal_emit(eo_it, EFL_ACCESS_STATE_TYPE_SELECTED, EINA_TRUE);
   // delete item if it's requested deletion in the above callbacks.
   if ((it->base)->on_deletion)
     {
        r = EINA_TRUE;
        goto item_deleted;
     }

   if (!(sd->focus_on_selection_enabled || _elm_config->item_select_on_focus_disable))
     {
        efl_ui_focus_manager_focus_set(obj, it->base->eo_obj);
     }

 item_deleted:
   efl_unref(eo_it);

   return r;
}

EOLIAN static Evas_Object *
_elm_genlist_item_elm_widget_item_part_content_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it, const char * part)
{
   Evas_Object *ret = NULL;
   if (it->deco_all_view)
     ret = edje_object_part_swallow_get(it->deco_all_view, part);
   else if (it->decorate_it_set)
     ret = edje_object_part_swallow_get(it->item->deco_it_view, part);
   if (!ret)
     {
        if (part)
          ret = edje_object_part_swallow_get(VIEW(it), part);
        else
          ret = edje_object_part_swallow_get(VIEW(it), "elm.swallow.icon");
     }
   return ret;
}

EOLIAN static const char *
_elm_genlist_item_elm_widget_item_part_text_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it, const char * part)
{
   if (!it->itc->func.text_get) return NULL;
   const char *ret = NULL;
   if (it->deco_all_view)
     ret = edje_object_part_text_get(it->deco_all_view, part);
   else if (it->decorate_it_set)
     ret = edje_object_part_text_get(it->item->deco_it_view, part);
   if (!ret)
     {
        if (part)
          ret = edje_object_part_text_get(VIEW(it), part);
        else
          ret = edje_object_part_text_get(VIEW(it), "elm.text");
     }
   return ret;
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_disable(Eo *eo_it, Elm_Gen_Item *it)
{
   Eina_List *l;
   Evas_Object *obj;

   _item_unselect(it);
   if (eo_it == it->item->wsd->focused_item)
     _elm_genlist_item_unfocused(eo_it);
   ELM_SAFE_FREE(it->long_timer, ecore_timer_del);

   if (it->realized)
     {
        if (elm_wdg_item_disabled_get(EO_OBJ(it)))
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_DISABLED, "elm");
             if (it->deco_all_view)
               edje_object_signal_emit
                 (it->deco_all_view, SIGNAL_DISABLED, "elm");
          }
        else
          {
             edje_object_signal_emit(VIEW(it), SIGNAL_ENABLED, "elm");
             if (it->deco_all_view)
               edje_object_signal_emit
                 (it->deco_all_view, SIGNAL_ENABLED, "elm");
          }
        EINA_LIST_FOREACH(it->contents, l, obj)
          elm_widget_disabled_set(obj, elm_wdg_item_disabled_get(EO_OBJ(it)));
     }
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_del_pre(Eo *eo_it EINA_UNUSED,
                                          Elm_Gen_Item *it)
{
   /* This item is getting removed from a callback that triggered in the
      _item_select(). Just pend removing. Because this will be removed right
      after in the _item_select(). So pratically, this item won't be
      dangled. */
   if (_elm_config->atspi_mode)
     efl_access_children_changed_del_signal_emit(WIDGET(it),eo_it);

   _item_del(it);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_signal_emit(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it, const char *emission, const char *source)
{
   if (!it->realized)
     {
        WRN("Item is not realized yet");
        return;
     }
   edje_object_signal_emit(VIEW(it), emission, source);
   if (it->deco_all_view)
     edje_object_signal_emit(it->deco_all_view, emission, source);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_style_set(Eo *eo_it,
                                            Elm_Gen_Item *it,
                                            const char *style)
{
   if (it->itc && !strcmp(it->itc->item_style, style)) return;

   Elm_Genlist_Item_Class *itc = elm_genlist_item_class_new();

   itc->item_style = style;
   if (it->itc)
     {
        itc->func.text_get = it->itc->func.text_get;
        itc->func.content_get = it->itc->func.content_get;
        itc->func.state_get = it->itc->func.state_get;
        itc->func.filter_get = it->itc->func.filter_get;
        itc->func.reusable_content_get = it->itc->func.reusable_content_get;
        itc->decorate_item_style = it->itc->decorate_item_style;
        itc->decorate_all_item_style = it->itc->decorate_all_item_style;
     }

   elm_genlist_item_item_class_update(eo_it, itc);
   elm_genlist_item_class_free(itc);
}

EOLIAN static const char *
_elm_genlist_item_elm_widget_item_style_get(const Eo *eo_it EINA_UNUSED,
                                            Elm_Gen_Item *it)
{
   if (it->itc) return it->itc->item_style;
   else return NULL;
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_item_focus_set(Eo *eo_it, Elm_Gen_Item *it, Eina_Bool focused)
{
   Evas_Object *obj = WIDGET(it);
   ELM_GENLIST_DATA_GET(obj, sd);

   if (focused)
     {
        sd->last_focused_item = eo_it;
        if (!elm_object_focus_get(obj))
          elm_object_focus_set(obj, EINA_TRUE);

        if (!elm_object_focus_get(obj))
          return;

        if (eo_it != sd->focused_item)
          {
             if (sd->focused_item)
               _elm_genlist_item_unfocused(sd->focused_item);
             _elm_genlist_item_focused(eo_it);

             /* If item is not realized state, widget couldn't get focus_highlight data. */
             if (it->realized)
               {
                  _elm_widget_item_highlight_in_theme(obj, EO_OBJ(it));
                  _elm_widget_highlight_in_theme_update(obj);
                  _elm_widget_focus_highlight_start(obj);
                  efl_ui_focus_manager_focus_set(it->base->widget, eo_it);
                  sd->focus_on_realization = NULL;
               }
             else
               {
                  sd->focus_on_realization = it;
               }
          }
     }
   else
     {
        if (!efl_ui_focus_object_focus_get(obj))
          return;
        _elm_genlist_item_unfocused(eo_it);
     }
}

EOLIAN static Eina_Bool
_elm_genlist_item_elm_widget_item_item_focus_get(const Eo *eo_it, Elm_Gen_Item *it)
{
   Evas_Object *obj = WIDGET(it);
   ELM_GENLIST_DATA_GET(obj, sd);

   if (eo_it == sd->focused_item)
     return EINA_TRUE;

   return EINA_FALSE;
}

EOLIAN static Eo *
_elm_genlist_item_efl_object_constructor(Eo *eo_it, Elm_Gen_Item *it)
{
   eo_it = efl_constructor(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS));

   it->base = efl_data_scope_get(eo_it, ELM_WIDGET_ITEM_CLASS);
   efl_access_object_role_set(eo_it, EFL_ACCESS_ROLE_LIST_ITEM);

   return eo_it;
}

static Elm_Gen_Item *
_elm_genlist_item_new(Elm_Genlist_Data *sd,
                      const Elm_Genlist_Item_Class *itc,
                      const void *data,
                      Elm_Object_Item *eo_parent,
                      Elm_Genlist_Item_Type type,
                      Evas_Smart_Cb func,
                      const void *func_data)
{
   Elm_Gen_Item *it2;
   int depth = 0;

   if (!itc) return NULL;

   Eo *eo_it = efl_add(ELM_GENLIST_ITEM_CLASS, sd->obj);
   if (!eo_it) return NULL;
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);

   it->itc = itc;
   elm_genlist_item_class_ref((Elm_Genlist_Item_Class *)itc);

   ELM_GENLIST_ITEM_DATA_GET(eo_parent, parent);
   WIDGET_ITEM_DATA_SET(EO_OBJ(it), data);
   it->parent = parent;
   it->func.func = func;
   it->func.data = func_data;

   it->item = ELM_NEW(Elm_Gen_Item_Type);
   it->item->wsd = sd;
   it->item->type = type;
   it->item->order_num_in = -1;

   if (it->parent)
     {
        if (it->parent->item->type & ELM_GENLIST_ITEM_GROUP)
          it->item->group_item = parent;
        else if (it->parent->item->group_item)
          it->item->group_item = it->parent->item->group_item;
     }
   for (it2 = it, depth = 0; it2->parent; it2 = it2->parent)
     {
        if (!(it2->parent->item->type & ELM_GENLIST_ITEM_GROUP)) depth += 1;
     }
   it->item->expanded_depth = depth;
   sd->item_count++;

   return it;
}

static int
_elm_genlist_item_compare(const void *data,
                          const void *data1)
{
   const Elm_Gen_Item *it, *item1;

   it = ELM_GEN_ITEM_FROM_INLIST(data);
   item1 = ELM_GEN_ITEM_FROM_INLIST(data1);
   return it->item->wsd->item_compare_cb(EO_OBJ(it), EO_OBJ(item1));
}

static int
_elm_genlist_item_list_compare(const void *data,
                               const void *data1)
{
   const Elm_Gen_Item *it = data;
   const Elm_Gen_Item *item1 = data1;

   return it->item->wsd->item_compare_cb(EO_OBJ(it), EO_OBJ(item1));
}

static int
_elm_genlist_eo_item_list_compare(const void *data,
                               const void *data1)
{
   const Elm_Object_Item *eo_it = data;
   const Elm_Object_Item *eo_item1 = data1;
   ELM_GENLIST_ITEM_DATA_GET(eo_it, it);

   return it->item->wsd->item_compare_cb(eo_it, eo_item1);
}

EOLIAN static unsigned int
_elm_genlist_items_count(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->item_count;
}

static Eina_List *
_list_last_recursive(Eina_List *list)
{
   Eina_List *ll, *ll2;
   Elm_Object_Item *eo_it2;

   ll = eina_list_last(list);
   if (!ll) return NULL;

   eo_it2 = ll->data;
   ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);

   if (it2->item->items)
     {
        ll2 = _list_last_recursive(it2->item->items);
        if (ll2)
          {
             return ll2;
          }
     }

   return ll;
}

EOLIAN static Elm_Object_Item*
_elm_genlist_item_append(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, const Elm_Genlist_Item_Class *itc, const void *data, Elm_Object_Item *eo_parent, Elm_Genlist_Item_Type type, Evas_Smart_Cb func, const void *func_data)
{
   Elm_Gen_Item *it;

   if (eo_parent)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_parent, parent);
        ELM_GENLIST_ITEM_CHECK_OR_RETURN(parent, NULL);
        EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(parent)), NULL);
     }

   it = _elm_genlist_item_new
       (sd, itc, data, eo_parent, type, func, func_data);
   if (!it) return NULL;

   if (!it->parent)
     {
        if (it->item->type & ELM_GENLIST_ITEM_GROUP)
          sd->group_items = eina_list_append(sd->group_items, it);
        sd->items = eina_inlist_append(sd->items, EINA_INLIST_GET(it));
        it->item->rel = NULL;
     }
   else
     {
        Elm_Object_Item *eo_it2 = NULL;
        Eina_List *ll = _list_last_recursive(it->parent->item->items);

        if (ll) eo_it2 = ll->data;
        if (!it->parent->item->items)
          sd->top_level_parent_items++;
        it->parent->item->items =
          eina_list_append(it->parent->item->items, EO_OBJ(it));
        if (!eo_it2) eo_it2 = EO_OBJ(it->parent);
        ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
        sd->items = eina_inlist_append_relative
           (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(it2));
        it->item->rel = it2;
        it2->item->rel_revs = eina_list_append(it2->item->rel_revs, it);
        _item_expanded_set_noevent(it->parent, EINA_TRUE);
     }
   it->item->before = EINA_FALSE;
   _item_queue(sd, it, NULL);

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item*
_elm_genlist_item_prepend(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, const Elm_Genlist_Item_Class *itc, const void *data, Elm_Object_Item *eo_parent, Elm_Genlist_Item_Type type, Evas_Smart_Cb func, const void *func_data)
{
   Elm_Gen_Item *it;

   if (eo_parent)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_parent, parent);
        ELM_GENLIST_ITEM_CHECK_OR_RETURN(parent, NULL);
        EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(parent)), NULL);
        /* first sub-item should allways be appended */
        if (!parent->item->items)
          return _elm_genlist_item_append(obj, sd, itc, data, eo_parent, type, func, func_data);
     }

   it = _elm_genlist_item_new
       (sd, itc, data, eo_parent, type, func, func_data);
   if (!it) return NULL;

   if (!it->parent)
     {
        if (it->item->type & ELM_GENLIST_ITEM_GROUP)
          sd->group_items = eina_list_prepend(sd->group_items, it);
        sd->items = eina_inlist_prepend(sd->items, EINA_INLIST_GET(it));
        it->item->rel = NULL;
     }
   else
     {
        Elm_Object_Item *eo_it2 = NULL;
        Eina_List *ll = it->parent->item->items;

        if (ll) eo_it2 = ll->data;
        if (!it->parent->item->items)
          sd->top_level_parent_items++;
        it->parent->item->items =
          eina_list_prepend(it->parent->item->items, EO_OBJ(it));
        if (!eo_it2) eo_it2 = EO_OBJ(it->parent);
        ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
        sd->items = eina_inlist_prepend_relative
           (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(it2));
        it->item->rel = it2;
        it2->item->rel_revs = eina_list_append(it2->item->rel_revs, it);
        _item_expanded_set_noevent(it->parent, EINA_TRUE);
     }
   it->item->before = EINA_TRUE;
   _item_queue(sd, it, NULL);

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item*
_elm_genlist_item_insert_after(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, const Elm_Genlist_Item_Class *itc, const void *data, Elm_Object_Item *eo_parent, Elm_Object_Item *eo_after, Elm_Genlist_Item_Type type, Evas_Smart_Cb func, const void *func_data)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(eo_after, NULL);
   ELM_GENLIST_ITEM_DATA_GET(eo_after, after);
   Elm_Gen_Item *it;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(after, NULL);
   EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(after)), NULL);
   if (eo_parent)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_parent, parent);
        ELM_GENLIST_ITEM_CHECK_OR_RETURN(parent, NULL);
        EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(parent)), NULL);
     }

   /* It makes no sense to insert after in an empty list with after !=
    * NULL, something really bad is happening in your app. */
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd->items, NULL);

   it = _elm_genlist_item_new
       (sd, itc, data, eo_parent, type, func, func_data);
   if (!it) return NULL;

   if (!it->parent)
     {
        if ((it->item->type & ELM_GENLIST_ITEM_GROUP) &&
            (after->item->type & ELM_GENLIST_ITEM_GROUP))
          sd->group_items = eina_list_append_relative
             (sd->group_items, it, after);
        else if (it->item->type & ELM_GENLIST_ITEM_GROUP)
          CRI("GROUP ITEM INSERT FAILED: NON-GROUP ITEM PASSED AS 'after'!");
     }
   else
     {
        if (!it->parent->item->items)
          sd->top_level_parent_items++;
        it->parent->item->items =
          eina_list_append_relative(it->parent->item->items, EO_OBJ(it), eo_after);
        _item_expanded_set_noevent(it->parent, EINA_TRUE);
     }

   if (after->item->items)
     {
        Eina_List *ll = _list_last_recursive(after->item->items);
        if (ll)
          {
             eo_after = ll->data;
             after = efl_data_scope_get(eo_after, ELM_GENLIST_ITEM_CLASS);
          }
     }

   sd->items = eina_inlist_append_relative
       (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(after));

   it->item->rel = after;
   after->item->rel_revs = eina_list_append(after->item->rel_revs, it);
   it->item->before = EINA_FALSE;
   _item_queue(sd, it, NULL);

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item*
_elm_genlist_item_insert_before(Eo *obj, Elm_Genlist_Data *sd, const Elm_Genlist_Item_Class *itc, const void *data, Elm_Object_Item *eo_parent, Elm_Object_Item *eo_before, Elm_Genlist_Item_Type type, Evas_Smart_Cb func, const void *func_data)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(eo_before, NULL);
   ELM_GENLIST_ITEM_DATA_GET(eo_before, before);
   Elm_Gen_Item *it;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(before, NULL);
   EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(before)), NULL);
   if (eo_parent)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_parent, parent);
        ELM_GENLIST_ITEM_CHECK_OR_RETURN(parent, NULL);
        EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(parent)), NULL);
     }

   /* It makes no sense to insert before in an empty list with before
    * != NULL, something really bad is happening in your app. */
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd->items, NULL);

   it = _elm_genlist_item_new
       (sd, itc, data, eo_parent, type, func, func_data);
   if (!it) return NULL;

   if (!it->parent)
     {
        if ((it->item->type & ELM_GENLIST_ITEM_GROUP) &&
            (before->item->type & ELM_GENLIST_ITEM_GROUP))
          sd->group_items =
            eina_list_prepend_relative(sd->group_items, it, before);
        else if (it->item->type & ELM_GENLIST_ITEM_GROUP)
          CRI("GROUP ITEM INSERT FAILED: NON-GROUP ITEM PASSED AS 'after'!");
     }
   else
     {
        if (!it->parent->item->items)
          sd->top_level_parent_items++;
        it->parent->item->items =
          eina_list_prepend_relative(it->parent->item->items, EO_OBJ(it), eo_before);
        _item_expanded_set_noevent(it->parent, EINA_TRUE);
     }
   sd->items = eina_inlist_prepend_relative
       (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(before));

   it->item->rel = before;
   before->item->rel_revs = eina_list_append(before->item->rel_revs, it);
   it->item->before = EINA_TRUE;
   _item_queue(sd, it, NULL);

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item*
_elm_genlist_item_sorted_insert(Eo *obj, Elm_Genlist_Data *sd, const Elm_Genlist_Item_Class *itc, const void *data, Elm_Object_Item *eo_parent, Elm_Genlist_Item_Type type, Eina_Compare_Cb comp, Evas_Smart_Cb func, const void *func_data)
{
   Elm_Object_Item *eo_rel = NULL;
   Elm_Gen_Item *rel = NULL;
   Elm_Gen_Item *it;

   EINA_SAFETY_ON_NULL_RETURN_VAL(comp, NULL);
   if (eo_parent)
     {
        ELM_GENLIST_ITEM_DATA_GET(eo_parent, parent);
        ELM_GENLIST_ITEM_CHECK_OR_RETURN(parent, NULL);
        EINA_SAFETY_ON_FALSE_RETURN_VAL((obj == WIDGET(parent)), NULL);
     }

   it = _elm_genlist_item_new
       (sd, itc, data, eo_parent, type, func, func_data);
   if (!it) return NULL;
   Elm_Object_Item *eo_it = EO_OBJ(it);

   sd->item_compare_cb = comp;

   if (it->parent)
     {
        Eina_List *l;
        int cmp_result;

        l = eina_list_search_sorted_near_list
            (it->parent->item->items, _elm_genlist_eo_item_list_compare, eo_it,
            &cmp_result);

        if (l)
          {
             eo_rel = eina_list_data_get(l);
             rel = efl_data_scope_get(eo_rel, ELM_GENLIST_ITEM_CLASS);

             if (cmp_result >= 0)
               {
                  it->parent->item->items = eina_list_prepend_relative_list
                      (it->parent->item->items, eo_it, l);
                  sd->items = eina_inlist_prepend_relative
                      (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(rel));
                  it->item->before = EINA_TRUE;
               }
             else // if (cmp_result < 0)
               {
                  it->parent->item->items = eina_list_append_relative_list
                      (it->parent->item->items, eo_it, l);
                  if (rel->item->items)
                    {
                       Eina_List *ll = _list_last_recursive(rel->item->items);
                       if (ll)
                         {
                            eo_rel = ll->data;
                            rel = efl_data_scope_get(eo_rel, ELM_GENLIST_ITEM_CLASS);
                         }
                    }
                  sd->items = eina_inlist_append_relative
                      (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(rel));
                  it->item->before = EINA_FALSE;
               }
          }
        else
          {
             // parent had no child
             rel = it->parent;

             it->parent->item->items = eina_list_prepend
                   (it->parent->item->items, eo_it);
             sd->items = eina_inlist_append_relative
                   (sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(rel));
             it->item->before = EINA_FALSE;
             sd->top_level_parent_items++;
          }

        ELM_SAFE_FREE(sd->state, eina_inlist_sorted_state_free);

        // ensure expanded state is on
        _item_expanded_set_noevent(it->parent, EINA_TRUE);
     }
   else
     {
        if (!sd->state && !sd->top_level_parent_items)
          {
             sd->state = eina_inlist_sorted_state_new();
             eina_inlist_sorted_state_init(sd->state, sd->items);
             sd->requeued = EINA_FALSE;
          }

        if (it->item->type == ELM_GENLIST_ITEM_GROUP)
          sd->group_items = eina_list_append(sd->group_items, it);

        if (!sd->top_level_parent_items)
          {
             sd->items = eina_inlist_sorted_state_insert
                   (sd->items, EINA_INLIST_GET(it), _elm_genlist_item_compare,
                    sd->state);

             if (EINA_INLIST_GET(it)->next)
               {
                  rel = ELM_GEN_ITEM_NEXT(it);
                  it->item->before = EINA_TRUE;
               }
             else if (EINA_INLIST_GET(it)->prev)
               {
                  rel = ELM_GEN_ITEM_PREV(it);
                  it->item->before = EINA_FALSE;
               }
          }
        else
          {
             // Inlist is not sorted!
             Elm_Gen_Item *prev_rel = NULL;
             int cmp = 0;

             EINA_INLIST_FOREACH(sd->items, rel)
               {
                  cmp = comp(EO_OBJ(it), EO_OBJ(rel));
                  if (cmp < 0) break;
                  prev_rel = rel;
                  if (rel->item->items)
                    {
                       Eina_List *ll = _list_last_recursive(rel->item->items);
                       if (ll)
                         {
                            eo_rel = ll->data;
                            rel = efl_data_scope_get(eo_rel, ELM_GENLIST_ITEM_CLASS);
                         }
                    }
                  if (!EINA_INLIST_GET(rel)->next)
                    {
                       cmp = 1;
                       break;
                    }
               }

             if (!rel)
               {
                  sd->items = eina_inlist_prepend_relative(sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(prev_rel));
                  it->item->before = EINA_TRUE;
                  rel = prev_rel;
               }
             else if (cmp >= 0)
               {
                  sd->items = eina_inlist_append_relative(sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(rel));
                  it->item->before = EINA_FALSE;
               }
             else
               {
                  sd->items = eina_inlist_prepend_relative(sd->items, EINA_INLIST_GET(it), EINA_INLIST_GET(rel));
                  it->item->before = EINA_TRUE;
               }
          }
     }

   if (rel)
     {
        it->item->rel = rel;
        rel->item->rel_revs = eina_list_append(rel->item->rel_revs, it);
     }

   _item_queue(sd, it, _elm_genlist_item_list_compare);

   return eo_it;
}

EOLIAN static void
_elm_genlist_clear(Eo *obj, Elm_Genlist_Data *sd EINA_UNUSED)
{
   _internal_elm_genlist_clear(obj);
}

EOLIAN static void
_elm_genlist_multi_select_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool multi)
{
   sd->multi = !!multi;

   if (!sd->multi && sd->selected)
     {
        Eina_List *l, *ll;
        Elm_Object_Item *eo_it;
        Elm_Object_Item *last = sd->selected->data;
        EINA_LIST_FOREACH_SAFE(sd->selected, l, ll, eo_it)
          {
             if (last != eo_it)
               {
                  ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
                  _item_unselect(it);
               }
          }
     }
}

EOLIAN static Eina_Bool
_elm_genlist_multi_select_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->multi;
}

EOLIAN static void
_elm_genlist_multi_select_mode_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Elm_Object_Multi_Select_Mode mode)
{
   if (mode >= ELM_OBJECT_MULTI_SELECT_MODE_MAX)
     return;

   if (sd->multi_select_mode != mode)
     sd->multi_select_mode = mode;
}

EOLIAN static Elm_Object_Multi_Select_Mode
_elm_genlist_multi_select_mode_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->multi_select_mode;
}

EOLIAN static Elm_Object_Item*
_elm_genlist_selected_item_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   if (sd->selected)
      return sd->selected->data;
   else
      return NULL;
}

EOLIAN static const Eina_List*
_elm_genlist_selected_items_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->selected;
}

EOLIAN static Eina_List*
_elm_genlist_realized_items_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   Item_Block *itb;
   Eina_Bool done = EINA_FALSE;

   Eina_List *ret = NULL;

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        if (itb->realized)
          {
             Eina_List *l;
             Elm_Gen_Item *it;

             done = EINA_TRUE;
             EINA_LIST_FOREACH(itb->items, l, it)
               {
                  if (it->realized) ret = eina_list_append(ret, EO_OBJ(it));
               }
          }
        else
          {
             if (done) break;
          }
     }

   return ret;
}

EOLIAN static Elm_Object_Item*
_elm_genlist_at_xy_item_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Evas_Coord x, Evas_Coord y, int *posret)
{
   Evas_Coord ox, oy, ow, oh;
   Evas_Coord lasty;
   Item_Block *itb;

   evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);
   lasty = oy;
   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        Eina_List *l;
        Elm_Gen_Item *it;

        if (!ELM_RECTS_INTERSECT(ox + itb->x - itb->sd->pan_x,
                                 oy + itb->y - itb->sd->pan_y,
                                 itb->w, itb->h, x, y, 1, 1))
          continue;
        EINA_LIST_FOREACH(itb->items, l, it)
          {
             Evas_Coord itx, ity;

             itx = ox + itb->x + it->x - itb->sd->pan_x;
             ity = oy + itb->y + it->y - itb->sd->pan_y;
             if (ELM_RECTS_INTERSECT
                   (itx, ity, it->item->w, it->item->h, x, y, 1, 1))
               {
                  if (posret)
                    {
                       if (y <= (ity + (it->item->h / 4))) *posret = -1;
                       else if (y >= (ity + it->item->h - (it->item->h / 4)))
                         *posret = 1;
                       else *posret = 0;
                    }

                  return EO_OBJ(it);
               }
             lasty = ity + it->item->h;
          }
     }
   if (posret)
     {
        if (y > lasty) *posret = 1;
        else *posret = -1;
     }

   return NULL;
}

EOLIAN static Elm_Object_Item*
_elm_genlist_first_item_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   Elm_Gen_Item *it = ELM_GEN_ITEM_FROM_INLIST(sd->items);

   while (it && sd->filter && !_item_filtered_get(it, sd))
     it = ELM_GEN_ITEM_NEXT(it);

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item*
_elm_genlist_last_item_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   Elm_Gen_Item *it;

   if (!sd->items) return NULL;
   it = ELM_GEN_ITEM_FROM_INLIST(sd->items->last);

   while (it && sd->filter && !_item_filtered_get(it, sd))
     it = ELM_GEN_ITEM_PREV(it);

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item *
_elm_genlist_item_next_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   do it = ELM_GEN_ITEM_NEXT(it);
   while (it && sd->filter && !_item_filtered_get(it, sd));

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item *
_elm_genlist_item_prev_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   do it = ELM_GEN_ITEM_PREV(it);
   while (it && sd->filter && !_item_filtered_get(it, sd));

   return EO_OBJ(it);
}

EOLIAN static Elm_Object_Item *
_elm_genlist_item_parent_item_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, NULL);

   return EO_OBJ(it->parent);
}

EOLIAN static unsigned int
_elm_genlist_item_subitems_count(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *item)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(item, 0);

   return eina_list_count(item->item->items);
}

EOLIAN static const Eina_List *
_elm_genlist_item_subitems_get(const Eo *eo_item EINA_UNUSED, Elm_Gen_Item *item)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(item, NULL);

   return item->item->items;
}

EOLIAN static void
_elm_genlist_item_subitems_clear(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   ELM_GENLIST_DATA_GET(WIDGET(it), sd);

   if (!sd->tree_effect_enabled || !sd->move_effect_mode)
     _item_sub_items_clear(it);
   else
     {
        if (!_elm_genlist_tree_effect_setup(sd))
          _item_sub_items_clear(it);
     }
}

EOLIAN static void
_elm_genlist_item_selected_set(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it,
      Eina_Bool selected)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   if (elm_wdg_item_disabled_get(EO_OBJ(it))) return;

   selected = !!selected;
   if (it->selected == selected) return;

   if (selected) _item_select(it);
   else _item_unselect(it);
}

EOLIAN static Eina_Bool
_elm_genlist_item_selected_get(const Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, EINA_FALSE);

   return it->selected;
}

static Elm_Gen_Item *
_elm_genlist_expanded_next_item_get(Elm_Gen_Item *it)
{
   Elm_Object_Item *eo_it = EO_OBJ(it);
   Elm_Object_Item *eo_it2;

   if (it->item->expanded)
     {
        eo_it2 = elm_genlist_item_next_get(eo_it);
        ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
        return it2;
     }
   else
     {
        eo_it2 = elm_genlist_item_next_get(eo_it);
        while (eo_it2)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
             if (it->item->expanded_depth >= it2->item->expanded_depth) return it2;
             eo_it2 = elm_genlist_item_next_get(eo_it2);
          }
     }
   return efl_data_scope_get(eo_it2, ELM_GENLIST_ITEM_CLASS);
}

static void
_elm_genlist_move_items_set(Elm_Gen_Item *it, Eina_Bool expanded)
{
   Eina_List *l, *ll;
   Elm_Gen_Item *it2 = NULL;
   Evas_Coord ox, oy, ow, oh, dh = 0;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   sd->expanded_next_item =
     _elm_genlist_expanded_next_item_get(it);

   if (expanded)
     {
        Elm_Object_Item *eo_item;
        l = elm_genlist_realized_items_get((sd)->obj);
        EINA_LIST_FREE(l, eo_item)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_item, item);
             sd->move_items = eina_list_append(sd->move_items, item);
          }

        EINA_LIST_FOREACH_SAFE(sd->move_items, l, ll, it2)
          {
             if (it2 == sd->expanded_next_item) break;
             sd->move_items = eina_list_remove(sd->move_items, it2);
          }
     }
   else
     {
        Elm_Object_Item *eo_it2 = NULL;
        evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);
        if (sd->expanded_next_item) eo_it2 = EO_OBJ(sd->expanded_next_item);

        while (eo_it2 && (dh < oy + oh))
          {
             it2 = efl_data_scope_get(eo_it2, ELM_GENLIST_ITEM_CLASS);
             dh += it2->item->h;
             sd->move_items = eina_list_append(sd->move_items, it2);
             eo_it2 = elm_genlist_item_next_get(eo_it2);
          }
     }
}

static void
_event_block_rect_update(const Evas_Object *obj)
{
   Evas_Coord ox, oy, ow, oh;

   ELM_GENLIST_CHECK(obj);
   ELM_GENLIST_DATA_GET(obj, sd);

   if (!sd->event_block_rect)
     {
        sd->event_block_rect = evas_object_rectangle_add(
           evas_object_evas_get(sd->obj));
        evas_object_smart_member_add(sd->event_block_rect, sd->pan_obj);
        evas_object_color_set(sd->event_block_rect, 0, 0, 0, 0);
     }

   evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);
   efl_gfx_entity_geometry_set(sd->pan_obj, EINA_RECT(ox, oy, ow, oh));
}

static void
_item_expanded_set_noevent(Elm_Gen_Item *it, Eina_Bool expanded)
{
   EINA_SAFETY_ON_NULL_RETURN(it);

   if (it->item->expanded == expanded) return;
   it->item->expanded = expanded;
   if (expanded)
     {
        if (it->realized)
          edje_object_signal_emit(VIEW(it), SIGNAL_EXPANDED, "elm");
     }
   else
     {
        if (it->realized)
          edje_object_signal_emit(VIEW(it), SIGNAL_CONTRACTED, "elm");
     }
}

EOLIAN static void
_elm_genlist_item_expanded_set(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it, Eina_Bool expanded)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   expanded = !!expanded;
   if (it->item->expanded == expanded) return;
   if (it->item->type != ELM_GENLIST_ITEM_TREE) return;
   sd->expanded_item = it;
   _elm_genlist_move_items_set(it, expanded);

   if (sd->tree_effect_enabled)
     _event_block_rect_update(WIDGET(it));

   if (expanded)
     {
        sd->move_effect_mode = ELM_GENLIST_TREE_EFFECT_EXPAND;
        _item_expanded_set_noevent(it, EINA_TRUE);
        efl_event_callback_legacy_call
              (WIDGET(it), ELM_GENLIST_EVENT_EXPANDED, EO_OBJ(it));
        sd->auto_scroll_enabled = EINA_TRUE;
        if (_elm_config->atspi_mode)
          efl_access_state_changed_signal_emit(eo_item, EFL_ACCESS_STATE_TYPE_EXPANDED, EINA_TRUE);
     }
   else
     {
        sd->move_effect_mode = ELM_GENLIST_TREE_EFFECT_CONTRACT;
        _item_expanded_set_noevent(it, EINA_FALSE);
        efl_event_callback_legacy_call
              (WIDGET(it), ELM_GENLIST_EVENT_CONTRACTED, EO_OBJ(it));
        sd->auto_scroll_enabled = EINA_FALSE;
        if (_elm_config->atspi_mode)
          efl_access_state_changed_signal_emit(eo_item, EFL_ACCESS_STATE_TYPE_EXPANDED, EINA_FALSE);
     }
}

EOLIAN static Eina_Bool
_elm_genlist_item_expanded_get(const Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, EINA_FALSE);

   return it->item->expanded;
}

EOLIAN static int
_elm_genlist_item_expanded_depth_get(const Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, 0);

   return it->item->expanded_depth;
}

static Eina_Bool
_elm_genlist_item_coordinates_calc(Elm_Gen_Item *it,
                                   Elm_Genlist_Item_Scrollto_Type type,
                                   Eina_Bool bring_in,
                                   Evas_Coord *x,
                                   Evas_Coord *y,
                                   Evas_Coord *w,
                                   Evas_Coord *h)
{
   Evas_Coord gith = 0;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   Eina_Bool deferred_show = EINA_FALSE;

   switch (type)
     {
      case ELM_GENLIST_ITEM_SCROLLTO_IN:
      case ELM_GENLIST_ITEM_SCROLLTO_TOP:
      case ELM_GENLIST_ITEM_SCROLLTO_MIDDLE:
      case ELM_GENLIST_ITEM_SCROLLTO_BOTTOM:
         break;
      default: /* Filters unsupported type */
         return EINA_FALSE;
     }

   //Can't goto the item right now. Reserve it instead.
   if (sd->queue || !(sd->homogeneous && (sd->mode == ELM_LIST_COMPRESS)))
     {
        if ((it->item->queued) || (!it->item->mincalcd) || (sd->queue))
          deferred_show = EINA_TRUE;
     }
   if ((it->item->block) && (it->item->block->w < 1)) deferred_show = EINA_TRUE;

   evas_object_geometry_get(sd->pan_obj, NULL, NULL, w, h);
   if (*w < 1 || *h < 1) deferred_show = EINA_TRUE;

   if (deferred_show)
     {
        sd->check_scroll = EINA_TRUE;
        sd->show_item = it;
        sd->bring_in = bring_in;
        sd->scroll_to_type = type;
        it->item->show_me = EINA_TRUE;

        efl_canvas_group_change(sd->obj);

        return EINA_FALSE;
     }

   if (sd->show_item)
     {
        sd->show_item->item->show_me = EINA_FALSE;
        sd->show_item = NULL;
     }

   switch (type)
     {
      case ELM_GENLIST_ITEM_SCROLLTO_IN:
        if ((it->item->group_item) &&
            (sd->pan_y > (it->y + it->item->block->y)))
          gith = it->item->group_item->item->h;
        *h = it->item->h;
        *y = it->y + it->item->block->y - gith;
        break;

      case ELM_GENLIST_ITEM_SCROLLTO_TOP:
        if (it->item->group_item) gith = it->item->group_item->item->h;
        *y = it->y + it->item->block->y - gith;
        break;

      case ELM_GENLIST_ITEM_SCROLLTO_MIDDLE:
        *y = it->y + it->item->block->y - (*h / 2) + (it->item->h / 2);
        break;

      case ELM_GENLIST_ITEM_SCROLLTO_BOTTOM:
        *y = it->y + it->item->block->y - *h + it->item->h;
        break;

      default:
        return EINA_FALSE;
     }

   *x = it->x + it->item->block->x;
   *w = it->item->block->w;

   return EINA_TRUE;
}

EOLIAN static void
_elm_genlist_item_promote(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   Elm_Object_Item *eo_first_item = elm_genlist_first_item_get(WIDGET(it));
   ELM_GENLIST_ITEM_DATA_GET(eo_first_item, first_item);
   _item_move_before(it, first_item);
}

EOLIAN static void
_elm_genlist_item_demote(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   Elm_Object_Item *eo_last_item = elm_genlist_last_item_get(WIDGET(it));
   ELM_GENLIST_ITEM_DATA_GET(eo_last_item, last_item);
   _item_move_after(it, last_item);
}

EOLIAN static void
_elm_genlist_item_show(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *item, Elm_Genlist_Item_Scrollto_Type type)
{
   Evas_Coord x, y, w, h;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(item);

   if (_elm_genlist_item_coordinates_calc
         (item, type, EINA_FALSE, &x, &y, &w, &h))
      elm_interface_scrollable_content_region_show
            (WIDGET(item), x, y, w, h);
}

EOLIAN static void
_elm_genlist_item_bring_in(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *item, Elm_Genlist_Item_Scrollto_Type type)
{
   Evas_Coord x, y, w, h;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(item);

   if (_elm_genlist_item_coordinates_calc
         (item, type, EINA_TRUE, &x, &y, &w, &h))
      elm_interface_scrollable_region_bring_in(WIDGET(item), x, y, w, h);
}

EOLIAN static void
_elm_genlist_item_all_contents_unset(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it, Eina_List **l)
{
   Evas_Object *content;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   EINA_LIST_FREE(it->contents, content)
     {
        _elm_widget_sub_object_redirect_to_top(WIDGET(it), content);
        edje_object_part_unswallow(VIEW(it), content);
        evas_object_hide(content);
        if (l) *l = eina_list_append(*l, content);
     }
}

static void
_mark_item_update(Elm_Gen_Item *it)
{
   it->item->mincalcd = EINA_FALSE;
   it->item->updateme = EINA_TRUE;
   it->item->block->updateme = EINA_TRUE;
}

EOLIAN static void
_elm_genlist_item_update(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if (!it->item->block) return;
   _mark_item_update(it);
   ecore_job_del(sd->update_job);
   sd->update_job = ecore_job_add(_update_job, sd->obj);
}

EOLIAN static void
_elm_genlist_item_fields_update(Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it,
                               const char *parts,
                               Elm_Genlist_Item_Field_Type itf)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   if (!it->item->block) return;

   if (!it->realized)
     {
        if (!itf || (itf & ELM_GENLIST_ITEM_FIELD_CONTENT))
          elm_genlist_item_update(eo_item);
        return;
     }

   if ((!itf) || (itf & ELM_GENLIST_ITEM_FIELD_TEXT))
     {
        _item_text_realize(it, VIEW(it), &it->texts, parts);
     }
   if ((!itf) || (itf & ELM_GENLIST_ITEM_FIELD_CONTENT))
     {
        _item_content_realize(it, VIEW(it), &it->contents, "contents", parts, EINA_FALSE);
        if (it->flipped)
          {
             _item_content_realize(it, VIEW(it), &it->item->flip_contents,
                                   "flips", parts, EINA_FALSE);
          }
        if (it->item->deco_it_view)
          {
             _item_content_realize(it, it->item->deco_it_view,
                                   &it->item->deco_it_contents,
                                   "contents", parts, EINA_FALSE);
          }
        if (it->item->wsd->decorate_all_mode)
          {
             _item_content_realize(it, it->deco_all_view,
                                   &it->item->deco_all_contents,
                                   "contents", parts, EINA_FALSE);
          }
        if (it->has_contents != (!!it->contents))
          it->item->mincalcd = EINA_FALSE;
        it->has_contents = !!it->contents;
        if (it->item->type == ELM_GENLIST_ITEM_NONE)
          {
             Evas_Object* eobj;
             Eina_List* l;
             it->item_focus_chain = eina_list_free(it->item_focus_chain);
             EINA_LIST_FOREACH(it->contents, l, eobj)
               if (elm_widget_is(eobj) && elm_object_focus_allow_get(eobj))
                 it->item_focus_chain = eina_list_append(it->item_focus_chain, eobj);
          }
     }

   if ((!itf) || (itf & ELM_GENLIST_ITEM_FIELD_STATE))
     _item_state_realize(it, VIEW(it), parts);

   if (!it->item->mincalcd)
     elm_genlist_item_update(eo_item);
}

EOLIAN static void
_elm_genlist_item_item_class_update(Eo *eo_it, Elm_Gen_Item *it,
                                   const Elm_Genlist_Item_Class *itc)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   EINA_SAFETY_ON_NULL_RETURN(itc);

   /* Decrease the orignal item class refcount to prevent memory leak */
   if (it->itc != itc)
     {
        elm_genlist_item_class_unref((Elm_Genlist_Item_Class *)it->itc);
        it->itc = itc;
        elm_genlist_item_class_ref((Elm_Genlist_Item_Class *)it->itc);
     }

   if (!it->item->block) return;
   it->item->nocache_once = EINA_TRUE;

   ELM_SAFE_FREE(it->texts, elm_widget_stringlist_free);
   ELM_SAFE_FREE(it->item->deco_it_texts, elm_widget_stringlist_free);
   ELM_SAFE_FREE(it->item->deco_all_texts, elm_widget_stringlist_free);

   elm_genlist_item_update(eo_it);
}

EOLIAN static const Elm_Genlist_Item_Class *
_elm_genlist_item_item_class_get(const Eo *eo_item EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, NULL);

   return it->itc;
}

static Evas_Object *
_elm_genlist_item_label_create(void *data,
                               Evas_Object *obj EINA_UNUSED,
                               Evas_Object *tooltip,
                               void *it 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 void
_elm_genlist_item_label_del_cb(void *data,
                               Evas_Object *obj EINA_UNUSED,
                               void *event_info EINA_UNUSED)
{
   eina_stringshare_del(data);
}

EAPI void
elm_genlist_item_tooltip_text_set(Elm_Object_Item *it,
                                  const char *text)
{
   elm_wdg_item_tooltip_text_set(it, text);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_tooltip_text_set(Eo *eo_it, Elm_Gen_Item *it, const char *text)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   text = eina_stringshare_add(text);
   elm_genlist_item_tooltip_content_cb_set
     (eo_it, _elm_genlist_item_label_create, text,
     _elm_genlist_item_label_del_cb);
}

EAPI void
elm_genlist_item_tooltip_content_cb_set(Elm_Object_Item *item,
                                        Elm_Tooltip_Item_Content_Cb func,
                                        const void *data,
                                        Evas_Smart_Cb del_cb)
{
   elm_wdg_item_tooltip_content_cb_set(item, func, data, del_cb);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_tooltip_content_cb_set(Eo *eo_it, Elm_Gen_Item *it,
                                        Elm_Tooltip_Item_Content_Cb func,
                                        const void *data,
                                        Evas_Smart_Cb del_cb)
{
   ELM_GENLIST_ITEM_CHECK_OR_GOTO(it, error);

   if ((it->tooltip.content_cb != func) || (it->tooltip.data != data))
     {
        if (it->tooltip.del_cb)
           it->tooltip.del_cb((void *)it->tooltip.data, WIDGET(it), it);

        it->tooltip.content_cb = func;
        it->tooltip.data = data;
        it->tooltip.del_cb = del_cb;
     }

   if (VIEW(it))
     {
        elm_wdg_item_tooltip_content_cb_set
              (efl_super(eo_it, ELM_GENLIST_ITEM_CLASS), it->tooltip.content_cb, it->tooltip.data, NULL);
        elm_wdg_item_tooltip_style_set(eo_it, it->tooltip.style);
        elm_wdg_item_tooltip_window_mode_set(eo_it, it->tooltip.free_size);
     }

   return;

error:
   if (del_cb) del_cb((void *)data, NULL, NULL);
}

EAPI void
elm_genlist_item_tooltip_unset(Elm_Object_Item *item)
{
   elm_wdg_item_tooltip_unset(item);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_tooltip_unset(Eo *eo_it, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   if ((VIEW(it)) && (it->tooltip.content_cb))
     elm_wdg_item_tooltip_unset(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS));

   if (it->tooltip.del_cb)
     it->tooltip.del_cb((void *)it->tooltip.data, WIDGET(it), it);
   it->tooltip.del_cb = NULL;
   it->tooltip.content_cb = NULL;
   it->tooltip.data = NULL;
   it->tooltip.free_size = EINA_FALSE;
   if (it->tooltip.style)
     elm_wdg_item_tooltip_style_set(eo_it, NULL);
}

EAPI void
elm_genlist_item_tooltip_style_set(Elm_Object_Item *item,
                                   const char *style)
{
   elm_wdg_item_tooltip_style_set(item, style);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_tooltip_style_set(Eo *eo_it, Elm_Gen_Item *it,
                                   const char *style)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   eina_stringshare_replace(&it->tooltip.style, style);
   if (VIEW(it)) elm_wdg_item_tooltip_style_set(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS), style);
}

EAPI const char *
elm_genlist_item_tooltip_style_get(const Elm_Object_Item *it)
{
   return elm_wdg_item_tooltip_style_get(it);
}

EOLIAN static const char *
_elm_genlist_item_elm_widget_item_tooltip_style_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   return it->tooltip.style;
}

EAPI Eina_Bool
elm_genlist_item_tooltip_window_mode_set(Elm_Object_Item *item,
                                         Eina_Bool disable)
{
   return elm_wdg_item_tooltip_window_mode_set(item, disable);
}

EOLIAN static Eina_Bool
_elm_genlist_item_elm_widget_item_tooltip_window_mode_set(Eo *eo_it, Elm_Gen_Item *it,
                                   Eina_Bool disable)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, EINA_FALSE);

   it->tooltip.free_size = disable;
   if (VIEW(it))
     {
        Eina_Bool ret;
        ret = elm_wdg_item_tooltip_window_mode_set(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS), disable);
        return ret;
     }

   return disable;
}

EAPI Eina_Bool
elm_genlist_item_tooltip_window_mode_get(const Elm_Object_Item *eo_it)
{
   return elm_wdg_item_tooltip_window_mode_get(eo_it);
}

EOLIAN static Eina_Bool
_elm_genlist_item_elm_widget_item_tooltip_window_mode_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   return it->tooltip.free_size;
}

EAPI void
elm_genlist_item_cursor_set(Elm_Object_Item *item,
                            const char *cursor)
{
   elm_wdg_item_cursor_set(item, cursor);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_cursor_set(Eo *eo_it, Elm_Gen_Item *it,
                            const char *cursor)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   eina_stringshare_replace(&it->mouse_cursor, cursor);
   if (VIEW(it)) elm_wdg_item_cursor_set(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS), cursor);
}

EAPI const char *
elm_genlist_item_cursor_get(const Elm_Object_Item *eo_it)
{
   return elm_wdg_item_cursor_get(eo_it);
}

EAPI void
elm_genlist_item_cursor_unset(Elm_Object_Item *item)
{
   elm_wdg_item_cursor_unset(item);
}

EOLIAN static void
_elm_genlist_item_elm_widget_item_cursor_unset(Eo *eo_it, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   if (!it->mouse_cursor) return;

   if (VIEW(it)) elm_wdg_item_cursor_unset(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS));

   ELM_SAFE_FREE(it->mouse_cursor, eina_stringshare_del);
}

EAPI void
elm_genlist_item_cursor_style_set(Elm_Object_Item *eo_it,
                                  const char *style)
{
   elm_wdg_item_cursor_style_set(eo_it, style);
}

EAPI const char *
elm_genlist_item_cursor_style_get(const Elm_Object_Item *eo_it)
{
   return elm_wdg_item_cursor_style_get(eo_it);
}

EAPI void
elm_genlist_item_cursor_engine_only_set(Elm_Object_Item *eo_it,
                                        Eina_Bool engine_only)
{
   elm_wdg_item_cursor_engine_only_set(eo_it, engine_only);
}

EAPI Eina_Bool
elm_genlist_item_cursor_engine_only_get(const Elm_Object_Item *eo_it)
{
   return elm_wdg_item_cursor_engine_only_get(eo_it);
}

EOLIAN static int
_elm_genlist_item_index_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   int cnt = 1;
   Elm_Gen_Item *tmp;
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, -1);

   EINA_INLIST_FOREACH(it->item->wsd->items, tmp)
    {
       if (tmp == it) break;
       cnt++;
    }
   return cnt;
}

EOLIAN static void
_elm_genlist_mode_set(Eo *obj, Elm_Genlist_Data *sd, Elm_List_Mode mode)
{
   if (sd->mode == mode) return;
   sd->mode = mode;

   if (sd->mode == ELM_LIST_LIMIT)
     {
        sd->scr_minw = EINA_TRUE;
        sd->scr_minh = EINA_FALSE;
     }
   else if (sd->mode == ELM_LIST_EXPAND)
     {
        sd->scr_minw = EINA_TRUE;
        sd->scr_minh = EINA_TRUE;
     }
   else
     {
        sd->scr_minw = EINA_FALSE;
        sd->scr_minh = EINA_FALSE;
     }

   elm_layout_sizing_eval(obj);
}

EOLIAN static Elm_List_Mode
_elm_genlist_mode_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->mode;
}

EAPI void
elm_genlist_bounce_set(Evas_Object *obj,
                       Eina_Bool h_bounce,
                       Eina_Bool v_bounce)
{
   ELM_GENLIST_CHECK(obj);
   elm_interface_scrollable_bounce_allow_set(obj, h_bounce, v_bounce);
}

EOLIAN static void
_elm_genlist_elm_interface_scrollable_bounce_allow_set(Eo *obj, Elm_Genlist_Data *sd, Eina_Bool h_bounce, Eina_Bool v_bounce)
{
   sd->h_bounce = !!h_bounce;
   sd->v_bounce = !!v_bounce;
   elm_interface_scrollable_bounce_allow_set
         (efl_super(obj, MY_CLASS), sd->h_bounce, sd->v_bounce);
}

EAPI void
elm_genlist_bounce_get(const Evas_Object *obj,
                       Eina_Bool *h_bounce,
                       Eina_Bool *v_bounce)
{
   ELM_GENLIST_CHECK(obj);
   elm_interface_scrollable_bounce_allow_get
         (obj, h_bounce, v_bounce);
}

EOLIAN static void
_elm_genlist_elm_interface_scrollable_bounce_allow_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
{
   if (h_bounce) *h_bounce = sd->h_bounce;
   if (v_bounce) *v_bounce = sd->v_bounce;
}

EOLIAN static void
_elm_genlist_homogeneous_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool homogeneous)
{
   sd->homogeneous = !!homogeneous;
}

EOLIAN static Eina_Bool
_elm_genlist_homogeneous_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->homogeneous;
}

EOLIAN static void
_elm_genlist_block_count_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, int count)
{
   EINA_SAFETY_ON_TRUE_RETURN(count < 1);

   sd->max_items_per_block = count;
   sd->item_cache_max = sd->max_items_per_block * 2;
   _item_cache_clean(sd);
}

EOLIAN static int
_elm_genlist_block_count_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->max_items_per_block;
}

static void
_filter_item_internal(Elm_Gen_Item *it)
{
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);
   if (sd->filter_data)
     {
        if ((it->parent && !_item_filtered_get(it->parent, sd)) ||
            (it->itc->func.filter_get &&
             !it->itc->func.filter_get(
                (void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)),
                WIDGET(it), sd->filter_data)))
          {
             it->hide = EINA_TRUE;
             if (it->item->block)
               it->item->block->changed = EINA_TRUE;
          }
        else
          sd->filtered_count++;
     }
   it->filtered = EINA_TRUE;
   sd->processed_count++;
}

// Returns true if the item is not filtered out, but remains visible.
static Eina_Bool
_item_filtered_get(Elm_Gen_Item *it, Elm_Genlist_Data *sd)
{
   Eina_List *l;
   /* no filter exists: item will always be visible */
   if (!sd->filter_data) return EINA_TRUE;
   if (!it->filtered)
     {
        l = eina_list_data_find_list(sd->filter_queue, it);
        if (l)
          sd->filter_queue = eina_list_remove_list(sd->filter_queue, l);
        if (it->item->queued)
          l = eina_list_data_find_list(sd->queue, it);
        else
          l = NULL;
        if (l)
          {
             sd->queue = eina_list_remove_list(sd->queue, l);
             it->item->queued = EINA_FALSE;
             _item_process(sd, it);
             _item_process_post(sd, it);
          }

        _filter_item_internal(it);
        if (it->item->block)
          it->item->block->changed = EINA_TRUE;
        efl_canvas_group_change(sd->obj);
        sd->need_calc = EINA_TRUE;
   }
   return !it->hide;
}

static int
_filter_queue_process(Elm_Genlist_Data *sd)
{
   int n;
   Elm_Gen_Item *it;
   double t0;

   t0 = ecore_time_get();
   for (n = 0; ((sd->filter_queue) && (sd->processed_count < ITEM_QUEUE_MAX)); n++)
     {
        it = eina_list_data_get(sd->filter_queue);
        if (!it) break;
        //FIXME: This is added as a fail safe code for items not yet processed.
        if (it->item->queued)
          {
             sd->filter_queue = eina_list_remove_list
                              (sd->filter_queue, sd->filter_queue);
             sd->filter_queue = eina_list_append(sd->filter_queue, it);
             it = eina_list_data_get(sd->filter_queue);
          }
        sd->filter_queue = eina_list_remove_list(sd->filter_queue, sd->filter_queue);
        _filter_item_internal(it);
        it->item->block->changed = EINA_TRUE;
        if ((ecore_time_get() - t0) > (ecore_animator_frametime_get()))
          {
             //At least 1 item is filtered by this time, so return n+1 for first loop
             n++;
             break;
          }
     }
   return n;
}

static Eina_Bool
_filter_process(void *data,
              Eina_Bool *wakeup)
{
   Elm_Genlist_Data *sd = data;

   if (_filter_queue_process(sd) > 0) *wakeup = EINA_TRUE;
   if (!sd->filter_queue) return ECORE_CALLBACK_CANCEL;
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_item_filter_enterer(void *data)
{
   Eina_Bool wakeup = EINA_FALSE;
   ELM_GENLIST_DATA_GET(data, sd);
   Eina_Bool ok = _filter_process(sd, &wakeup);
   if (wakeup)
     {
        // wake up mainloop
        efl_canvas_group_change(sd->obj);
     }
   if (ok == ECORE_CALLBACK_CANCEL)
     {
        sd->queue_filter_enterer = NULL;
        efl_event_callback_legacy_call(sd->obj, ELM_GENLIST_EVENT_FILTER_DONE, NULL);
     }

   return ok;
}

EOLIAN void
_elm_genlist_filter_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, void *filter_data)
{
   Item_Block *itb;
   Eina_List *l;
   Elm_Gen_Item *it;

   if (sd->filter_queue)
     ELM_SAFE_FREE(sd->queue_filter_enterer, ecore_idle_enterer_del);
   ELM_SAFE_FREE(sd->filter_queue, eina_list_free);
   ELM_SAFE_FREE(sd->filtered_list, eina_list_free);
   sd->filtered_count = 0;
   sd->processed_count = 0;
   sd->filter = EINA_TRUE;
   sd->filter_data = filter_data;

   EINA_INLIST_FOREACH(sd->blocks, itb)
     {
        if (itb->realized)
          {
             EINA_LIST_FOREACH(itb->items, l, it)
               {
                  it->filtered = EINA_FALSE;
                  it->hide = EINA_FALSE;
                  if (it->realized)
                    _filter_item_internal(it);
                  else
                    sd->filter_queue = eina_list_append(sd->filter_queue, it);
               }
            itb->changed = EINA_TRUE;
            evas_object_smart_changed(obj);
            sd->need_calc = EINA_TRUE;
         }
       else
         {
            EINA_LIST_FOREACH(itb->items, l, it)
              {
                 it->filtered = EINA_FALSE;
                 it->hide = EINA_FALSE;
                 sd->filter_queue = eina_list_append(sd->filter_queue, it);
              }
         }
     }
   if (!sd->filter_queue)
     efl_event_callback_legacy_call(sd->obj, ELM_GENLIST_EVENT_FILTER_DONE, NULL);
   else
     sd->queue_filter_enterer = ecore_idle_enterer_add(_item_filter_enterer,
                                                       sd->obj);
}

static Eina_Bool
_filter_iterator_next(Elm_Genlist_Filter *iter, void **data)
{
   if (!iter->current) return EINA_FALSE;
   Elm_Gen_Item *item;
   while (iter->current)
     {
        item = ELM_GENLIST_FILTER_ITERATOR_ITEM_GET(iter->current, Elm_Gen_Item);
        iter->current = iter->current->next;
        if (_item_filtered_get(item, item->item->block->sd))
          {
             if (data)
               *data = EO_OBJ(item);
             return EINA_TRUE;
          }
     }

   return EINA_FALSE;
}

static void
_filter_iterator_free(Elm_Genlist_Filter *iter)
{
   free(iter);
}

static Evas_Object *
_filter_iterator_get_container(Elm_Genlist_Filter *iter)
{
   if (!iter) return NULL;
   return iter->obj;
}

EOLIAN Eina_Iterator *
_elm_genlist_filter_iterator_new(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   Elm_Genlist_Filter *iter;
   iter = calloc(1, sizeof (Elm_Genlist_Filter));
   if (!iter) return NULL;

   iter->head = sd->items;
   iter->current = sd->items;
   iter->obj = sd->obj;

   iter->iterator.version = EINA_ITERATOR_VERSION;
   iter->iterator.next = FUNC_ITERATOR_NEXT(_filter_iterator_next);
   iter->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
                                    _filter_iterator_get_container);
   iter->iterator.free = FUNC_ITERATOR_FREE(_filter_iterator_free);

   EINA_MAGIC_SET(&iter->iterator, EINA_MAGIC_ITERATOR);

   return &iter->iterator;
}

EOLIAN static unsigned int
_elm_genlist_filtered_items_count(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->filtered_count;
}

EOLIAN static void
_elm_genlist_longpress_timeout_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, double timeout)
{
   sd->longpress_timeout = timeout;
}

EOLIAN static double
_elm_genlist_longpress_timeout_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->longpress_timeout;
}

EAPI void
elm_genlist_scroller_policy_set(Evas_Object *obj,
                                Elm_Scroller_Policy policy_h,
                                Elm_Scroller_Policy policy_v)
{
   ELM_GENLIST_CHECK(obj);
   elm_interface_scrollable_policy_set(obj, policy_h, policy_v);
}

EOLIAN static void
_elm_genlist_elm_interface_scrollable_policy_set(Eo *obj, Elm_Genlist_Data *sd EINA_UNUSED, Elm_Scroller_Policy policy_h, Elm_Scroller_Policy policy_v)
{
   if ((policy_h >= ELM_SCROLLER_POLICY_LAST) ||
       (policy_v >= ELM_SCROLLER_POLICY_LAST))
     return;

   elm_interface_scrollable_policy_set(efl_super(obj, MY_CLASS), policy_h, policy_v);
}

EAPI void
elm_genlist_scroller_policy_get(const Evas_Object *obj,
                                Elm_Scroller_Policy *policy_h,
                                Elm_Scroller_Policy *policy_v)
{
   ELM_GENLIST_CHECK(obj);
   elm_interface_scrollable_policy_get(obj, policy_h, policy_v);
}

EOLIAN static void
_elm_genlist_elm_interface_scrollable_policy_get(const Eo *obj, Elm_Genlist_Data *sd EINA_UNUSED, Elm_Scroller_Policy *policy_h, Elm_Scroller_Policy *policy_v)
{
   Elm_Scroller_Policy s_policy_h, s_policy_v;

   elm_interface_scrollable_policy_get
         (efl_super(obj, MY_CLASS), &s_policy_h, &s_policy_v);
   if (policy_h) *policy_h = (Elm_Scroller_Policy)s_policy_h;
   if (policy_v) *policy_v = (Elm_Scroller_Policy)s_policy_v;
}

EOLIAN static void
_elm_genlist_realized_items_update(Eo *obj, Elm_Genlist_Data *_pd)
{
   Eina_List *list;
   Elm_Object_Item *it;

   list = elm_genlist_realized_items_get(obj);
   EINA_LIST_FREE(list, it)
     {
        ELM_GENLIST_ITEM_DATA_GET(it, it2);
        if (!it2->item->block) continue;
        _mark_item_update(it2);
     }
   ecore_job_del(_pd->update_job);
   _pd->update_job = ecore_job_add(_update_job, obj);
}

EOLIAN static void
_elm_genlist_item_decorate_mode_set(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it,
                                   const char *decorate_it_type,
                                   Eina_Bool decorate_it_set)
{
   Elm_Genlist_Data *sd;
   Eina_List *l;
   Elm_Object_Item *eo_it2 = NULL;

   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   sd = it->item->wsd;

   if (!decorate_it_type) return;
   if (elm_wdg_item_disabled_get(eo_it)) return;
   if (sd->decorate_all_mode) return;

   if ((sd->mode_item == it) &&
       (!strcmp(decorate_it_type, sd->decorate_it_type)) &&
       (decorate_it_set))
     return;
   if (!it->itc->decorate_item_style) return;
   it->decorate_it_set = decorate_it_set;

   if (sd->multi)
     {
        EINA_LIST_FOREACH(sd->selected, l, eo_it2)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it2, it2);
             if (it2->realized)
                elm_genlist_item_selected_set(eo_it2, EINA_FALSE);
          }
     }
   else
     {
        Elm_Gen_Item *it2 = NULL;
        eo_it2 = elm_genlist_selected_item_get(sd->obj);
        if (eo_it2) it2 = efl_data_scope_get(eo_it2, ELM_GENLIST_ITEM_CLASS);
        if (it2 && it2->realized)
          elm_genlist_item_selected_set(eo_it2, EINA_FALSE);
     }

   if (!eina_streq(sd->decorate_it_type, decorate_it_type) ||
       decorate_it_set || (it == sd->mode_item))
     _decorate_item_unset(sd);

   eina_stringshare_replace(&sd->decorate_it_type, decorate_it_type);
   if (decorate_it_set) _decorate_item_set(it);
}

EOLIAN static const char *
_elm_genlist_item_decorate_mode_get(const Eo *eo_i EINA_UNUSED, Elm_Gen_Item *i)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(i, NULL);
   return i->item->wsd->decorate_it_type;
}

EOLIAN static Elm_Object_Item *
_elm_genlist_decorated_item_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return EO_OBJ(sd->mode_item);
}

EOLIAN static Eina_Bool
_elm_genlist_decorate_mode_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->decorate_all_mode;
}

EOLIAN static void
_elm_genlist_decorate_mode_set(Eo *obj, Elm_Genlist_Data *sd, Eina_Bool decorated)
{
   Elm_Object_Item *eo_it;
   Eina_List *list;

   decorated = !!decorated;
   if (sd->decorate_all_mode == decorated) return;
   // decorate_all_mode should be set first
   // because content_get func. will be called after this
   // and user can check whether decorate_all_mode_ is enabled.
   sd->decorate_all_mode = decorated;

   _elm_genlist_tree_effect_stop(sd);
   sd->move_effect_mode = ELM_GENLIST_TREE_EFFECT_NONE;

   list = elm_genlist_realized_items_get(obj);
   if (!sd->decorate_all_mode)
     {
        EINA_LIST_FREE(list, eo_it)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
             if (it->item->type != ELM_GENLIST_ITEM_GROUP)
               _decorate_all_item_unrealize(it);
          }
        _item_cache_zero(sd);
     }
   else
     {
        // unset decorated item
        Elm_Object_Item *deco_eo_it = elm_genlist_decorated_item_get(obj);
        ELM_GENLIST_ITEM_DATA_GET(deco_eo_it, deco_it);
        if (deco_it)
          {
             elm_genlist_item_decorate_mode_set
               (deco_eo_it, elm_genlist_item_decorate_mode_get
                 (deco_eo_it), EINA_FALSE);
             _decorate_item_finished_signal_cb(deco_it, obj, NULL, NULL);
          }

        EINA_LIST_FREE(list, eo_it)
          {
             ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
             if (it->item->type != ELM_GENLIST_ITEM_GROUP)
               {
                  if (it->itc->decorate_all_item_style)
                    _decorate_all_item_realize(it, EINA_TRUE);
               }
          }
     }

   efl_canvas_group_change(sd->obj);
}

EOLIAN static void
_elm_genlist_reorder_mode_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool reorder_mode)
{
   Eina_List *realized;
   Elm_Object_Item *eo_it;

   if (sd->reorder_mode == !!reorder_mode) return;
   if (sd->pin_item) elm_genlist_item_pin_set(EO_OBJ(sd->pin_item), EINA_FALSE);
   sd->reorder_mode = !!reorder_mode;
   realized = elm_genlist_realized_items_get(obj);
   EINA_LIST_FREE(realized, eo_it)
    {
       ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
       if (sd->reorder_mode)
         {
            edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_MODE_SET, "elm");
            if (it->deco_all_view)
              {
                 edje_object_signal_emit(it->deco_all_view,
                                         SIGNAL_REORDER_MODE_SET, "elm");
              }
         }
       else
         {
            edje_object_signal_emit(VIEW(it), SIGNAL_REORDER_MODE_UNSET, "elm");
            if (it->deco_all_view)
              {
                 edje_object_signal_emit(it->deco_all_view,
                                         SIGNAL_REORDER_MODE_UNSET, "elm");
              }
         }
    }
}

EOLIAN static Eina_Bool
_elm_genlist_reorder_mode_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->reorder_mode;
}

EOLIAN static Elm_Genlist_Item_Type
_elm_genlist_item_type_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, ELM_GENLIST_ITEM_MAX);

   return it->item->type;
}

EOLIAN static void
_elm_genlist_item_pin_set(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it, Eina_Bool pin)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   ELM_GENLIST_DATA_GET(WIDGET(it), sd);
   if (sd->reorder_mode) return;
   if (it->item->type & ELM_GENLIST_ITEM_GROUP) return;

   if (pin ^ (sd->pin_item == it))
     {
        if (sd->pin_item)
          {
             if (sd->pin_item->item->block)
                sd->pin_item->item->block->realized = EINA_TRUE;
             evas_object_smart_changed(sd->pan_obj);
          }
        if (pin)
          sd->pin_item = it;
        else
          sd->pin_item = NULL;
     }
}

EOLIAN static Eina_Bool
_elm_genlist_item_pin_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, EINA_FALSE);
   ELM_GENLIST_DATA_GET(WIDGET(it), sd);

   if (sd->pin_item == it)
     return EINA_TRUE;
   else
     return EINA_FALSE;
}

EAPI Elm_Genlist_Item_Class *
elm_genlist_item_class_new(void)
{
   Elm_Genlist_Item_Class *itc = ELM_NEW(Elm_Genlist_Item_Class);
   EINA_SAFETY_ON_NULL_RETURN_VAL(itc, NULL);

   itc->version = CLASS_ALLOCATED;
   itc->refcount = 1;
   itc->delete_me = EINA_FALSE;

   return itc;
}

EAPI void
elm_genlist_item_class_free(Elm_Genlist_Item_Class *itc)
{
   if (itc && (itc->version == CLASS_ALLOCATED))
     {
        if (!itc->delete_me) itc->delete_me = EINA_TRUE;
        if (itc->refcount > 0) elm_genlist_item_class_unref(itc);
        else
          {
             itc->version = 0;
             free(itc);
          }
     }
}

EAPI void
elm_genlist_item_class_ref(Elm_Genlist_Item_Class *itc)
{
   if (itc && (itc->version == CLASS_ALLOCATED))
     {
        itc->refcount++;
        if (itc->refcount == 0) itc->refcount--;
     }
}

EAPI void
elm_genlist_item_class_unref(Elm_Genlist_Item_Class *itc)
{
   if (itc && (itc->version == CLASS_ALLOCATED))
     {
        if (itc->refcount > 0) itc->refcount--;
        if (itc->delete_me && (!itc->refcount))
          elm_genlist_item_class_free(itc);
     }
}

static void
_flip_job(void *data)
{
   Elm_Gen_Item *it = (Elm_Gen_Item *)data;
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   _item_unselect(it);
   _elm_genlist_item_unrealize(it, EINA_FALSE);

   it->flipped = EINA_TRUE;
   it->item->nocache = EINA_TRUE;
   efl_canvas_group_change(sd->obj);
}

EOLIAN static void
_elm_genlist_item_flip_set(Eo *eo_it, Elm_Gen_Item *it, Eina_Bool flip)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);

   flip = !!flip;
   if (it->flipped == flip) return;

   if (flip)
     {
        ecore_job_add(_flip_job, it);
     }
   else
     {
        edje_object_signal_emit(VIEW(it), SIGNAL_FLIP_DISABLED, "elm");
        if (it->item->wsd->decorate_all_mode)
          edje_object_signal_emit(it->deco_all_view, SIGNAL_FLIP_DISABLED,
                                  "elm");

        it->flipped = flip;
        _item_cache_zero(it->item->wsd);
        elm_genlist_item_update(eo_it);
        it->item->nocache = EINA_FALSE;
     }
}

EOLIAN static Eina_Bool
_elm_genlist_item_flip_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, EINA_FALSE);

   return it->flipped;
}

EOLIAN static void
_elm_genlist_select_mode_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Elm_Object_Select_Mode mode)
{
   if ((mode >= ELM_OBJECT_SELECT_MODE_MAX) || (sd->select_mode == mode))
     return;

   sd->select_mode = mode;

   if ((sd->select_mode == ELM_OBJECT_SELECT_MODE_NONE) ||
       (sd->select_mode == ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY))
     {
        Eina_List *l, *ll;
        Elm_Object_Item *eo_it;
        EINA_LIST_FOREACH_SAFE(sd->selected, l, ll, eo_it)
        {
           ELM_GENLIST_ITEM_DATA_GET(eo_it, it);
           _item_unselect(it);
        }
     }
}

EOLIAN static Elm_Object_Select_Mode
_elm_genlist_select_mode_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->select_mode;
}

EOLIAN static void
_elm_genlist_highlight_mode_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool highlight)
{
   sd->highlight = !!highlight;
}

EOLIAN static Eina_Bool
_elm_genlist_highlight_mode_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->highlight;
}

EOLIAN static void
_elm_genlist_item_select_mode_set(Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it,
                                 Elm_Object_Select_Mode mode)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it);
   ELM_GENLIST_DATA_GET_FROM_ITEM(it, sd);

   if ((mode >= ELM_OBJECT_SELECT_MODE_MAX) || (it->select_mode == mode))
     return;

   it->select_mode = mode;

   if ((it->select_mode == ELM_OBJECT_SELECT_MODE_NONE) ||
       (it->select_mode == ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY))
     _item_unselect(it);

   if (it->select_mode == ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY)
     {
        it->item->mincalcd = EINA_FALSE;
        it->item->updateme = EINA_TRUE;
        if (it->item->block) it->item->block->updateme = EINA_TRUE;
        ecore_job_del(sd->update_job);
        sd->update_job = ecore_job_add(_update_job, sd->obj);

        // reset homogeneous item size
        if (sd->homogeneous)
          {
             Item_Size *size =
                eina_hash_find(sd->size_caches, &(it->itc));
             if (size)
               eina_hash_del_by_key(sd->size_caches, it->itc);
          }
     }
}

EOLIAN static Elm_Object_Select_Mode
_elm_genlist_item_select_mode_get(const Eo *eo_it EINA_UNUSED, Elm_Gen_Item *it)
{
   ELM_GENLIST_ITEM_CHECK_OR_RETURN(it, ELM_OBJECT_SELECT_MODE_MAX);

   return it->select_mode;
}

EOLIAN Efl_Access_State_Set
_elm_genlist_item_efl_access_object_state_set_get(const Eo *eo_it, Elm_Gen_Item *it EINA_UNUSED)
{
   Efl_Access_State_Set ret;
   Eina_Bool sel;

   ret = efl_access_object_state_set_get(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS));

   sel = elm_obj_genlist_item_selected_get(eo_it);

   STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_SELECTABLE);

   if (sel)
      STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_SELECTED);

   if (elm_genlist_item_type_get(eo_it) == ELM_GENLIST_ITEM_TREE)
     {
        STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_EXPANDABLE);
        if (elm_genlist_item_expanded_get(eo_it))
           STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_EXPANDED);
     }

   return ret;
}

EOLIAN const char*
_elm_genlist_item_efl_access_object_i18n_name_get(const Eo *eo_it, Elm_Gen_Item *it)
{
   const char *ret;
   Eina_Strbuf *buf;
   char *accessible_name;

   ret = efl_access_object_i18n_name_get(efl_super(eo_it, ELM_GENLIST_ITEM_CLASS));
   if (ret) return ret;

   buf = eina_strbuf_new();

   if (it->itc->func.text_get)
     {
        Eina_List *texts;
        const char *key;

        texts =
           elm_widget_stringlist_get(edje_object_data_get(VIEW(it), "texts"));

        EINA_LIST_FREE(texts, key)
          {
             char *str_markup = it->itc->func.text_get
                ((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), key);
             char *str_utf8 = _elm_util_mkup_to_text(str_markup);

             free(str_markup);

             if (str_utf8)
               {
                  if (eina_strbuf_length_get(buf) > 0) eina_strbuf_append(buf, ", ");
                  eina_strbuf_append(buf, str_utf8);
                  free(str_utf8);
               }
          }
     }

   accessible_name = eina_strbuf_string_steal(buf);
   eina_strbuf_free(buf);

   eina_stringshare_del(it->base->accessible_name);
   it->base->accessible_name = eina_stringshare_add(accessible_name);
   free(accessible_name);
   return it->base->accessible_name;
}

EOLIAN static void
_elm_genlist_tree_effect_enabled_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool enabled)
{
   sd->tree_effect_enabled = !!enabled;
}

EOLIAN static Eina_Bool
_elm_genlist_tree_effect_enabled_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->tree_effect_enabled;
}

EOLIAN static void
_elm_genlist_focus_on_selection_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool enabled)
{
   sd->focus_on_selection_enabled = !!enabled;
}

EOLIAN static Eina_Bool
_elm_genlist_focus_on_selection_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->focus_on_selection_enabled;
}

EAPI Elm_Object_Item *
elm_genlist_nth_item_get(const Evas_Object *obj, unsigned int nth)
{
   Elm_Gen_Item *it = NULL;
   Eina_Accessor *a;
   void *data;

   ELM_GENLIST_CHECK(obj) NULL;
   ELM_GENLIST_DATA_GET(obj, sd);

   if (!sd->items) return NULL;

   a = eina_inlist_accessor_new(sd->items);
   if (!a) return NULL;
   if (eina_accessor_data_get(a, nth, &data))
     it = ELM_GEN_ITEM_FROM_INLIST(data);
   eina_accessor_free(a);
   return EO_OBJ(it);
}

EOLIAN static Eina_Rect
_elm_genlist_efl_ui_widget_focus_highlight_geometry_get(const Eo *obj, Elm_Genlist_Data *sd)
{
   Evas_Coord ox, oy, oh, ow, item_x = 0, item_y = 0, item_w = 0, item_h = 0;
   Eina_Rect r = {};

   evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);

   if (sd->focused_item)
     {
        ELM_GENLIST_ITEM_DATA_GET(sd->focused_item, focus_it);
        evas_object_geometry_get(VIEW(focus_it), &item_x, &item_y, &item_w, &item_h);
        elm_widget_focus_highlight_focus_part_geometry_get(VIEW(focus_it), &item_x, &item_y, &item_w, &item_h);
     }
   else
     {
        evas_object_geometry_get(obj, &r.x, &r.y, &r.w, &r.h);
        return r;
     }

   r.x = item_x;
   r.y = item_y;
   r.w = item_w;
   r.h = item_h;

   if (item_y < oy)
     {
        r.y = oy;
     }
   if (item_y > (oy + oh - item_h))
     {
        r.y = oy + oh - item_h;
     }

   if ((item_x + item_w) > (ox + ow))
     {
        r.w = ow;
     }
   if (item_x < ox)
     {
        r.x = ox;
     }

   return r;
}

EOLIAN static Elm_Object_Item *
_elm_genlist_search_by_text_item_get(Eo *obj EINA_UNUSED,
                                     Elm_Genlist_Data *sd,
                                     Elm_Object_Item *item_to_search_from,
                                     const char *part_name,
                                     const char *pattern,
                                     Elm_Glob_Match_Flags flags)
{
   Elm_Gen_Item *it = NULL;
   char *str = NULL;
   Eina_Inlist *start = NULL;
   int fnflags = 0;

   if (!pattern) return NULL;
   if (!sd->items) return NULL;

   if (flags & ELM_GLOB_MATCH_NO_ESCAPE) fnflags |= FNM_NOESCAPE;
   if (flags & ELM_GLOB_MATCH_PATH) fnflags |= FNM_PATHNAME;
   if (flags & ELM_GLOB_MATCH_PERIOD) fnflags |= FNM_PERIOD;
#ifdef FNM_CASEFOLD
   if (flags & ELM_GLOB_MATCH_NOCASE) fnflags |= FNM_CASEFOLD;
#endif

   start = (item_to_search_from) ?
   EINA_INLIST_GET((Elm_Gen_Item *)efl_data_scope_get(item_to_search_from, ELM_GENLIST_ITEM_CLASS)) :
   sd->items;
   EINA_INLIST_FOREACH(start, it)
     {
        if (!it->itc->func.text_get) continue;
        str = it->itc->func.text_get((void *)WIDGET_ITEM_DATA_GET(EO_OBJ(it)), WIDGET(it), part_name);
        if (!str) continue;
        if (!fnmatch(pattern, str, fnflags))
          {
             free(str);
             return EO_OBJ(it);
          }
        free(str);
     }
   return NULL;
}

EOLIAN static Elm_Object_Item*
_elm_genlist_elm_widget_item_container_focused_item_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->focused_item;
}

EOLIAN static void
_elm_genlist_elm_interface_scrollable_item_loop_enabled_set(Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd, Eina_Bool enable)
{
   sd->item_loop_enable = !!enable;
}

EOLIAN static Eina_Bool
_elm_genlist_elm_interface_scrollable_item_loop_enabled_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *sd)
{
   return sd->item_loop_enable;
}

EOLIAN static void
_elm_genlist_class_constructor(Efl_Class *klass)
{
   if (_elm_config->access_mode)
      _elm_genlist_smart_focus_next_enable = EINA_TRUE;

   evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
}

EOLIAN const Efl_Access_Action_Data *
_elm_genlist_efl_access_widget_action_elm_actions_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd EINA_UNUSED)
{
   static Efl_Access_Action_Data atspi_actions[] = {
          { "move,prior", "move", "prior", _key_action_move},
          { "move,next", "move", "next", _key_action_move},
          { "move,left", "move", "left", _key_action_move},
          { "move,right", "move", "right", _key_action_move},
          { "move,up", "move", "up", _key_action_move},
          { "move,up,multi", "move", "up_multi", _key_action_move},
          { "move,down", "move", "down", _key_action_move},
          { "move,down,multi", "move", "down_multi", _key_action_move},
          { "move,first", "move", "first", _key_action_move},
          { "move,last", "move", "last", _key_action_move},
          { "select", "select", NULL, _key_action_select},
          { "select,multi", "select", "multi", _key_action_select},
          { "escape", "escape", NULL, _key_action_escape},
          { NULL, NULL, NULL, NULL }
   };
   return &atspi_actions[0];
}

EOLIAN Eina_List*
_elm_genlist_efl_access_object_access_children_get(const Eo *obj, Elm_Genlist_Data *sd)
{
   Eina_List *ret = NULL, *ret2 = NULL;
   Elm_Gen_Item *it;

   EINA_INLIST_FOREACH(sd->items, it)
      ret = eina_list_append(ret, EO_OBJ(it));

   ret2 = efl_access_object_access_children_get(efl_super(obj, ELM_GENLIST_CLASS));

   return eina_list_merge(ret, ret2);
}

EOLIAN Efl_Access_State_Set
_elm_genlist_efl_access_object_state_set_get(const Eo *obj, Elm_Genlist_Data *sd EINA_UNUSED)
{
   Efl_Access_State_Set ret;

   ret = efl_access_object_state_set_get(efl_super(obj, ELM_GENLIST_CLASS));

   STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_MANAGES_DESCENDANTS);

   if (elm_genlist_multi_select_get(obj))
     STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_MULTISELECTABLE);

   if (elm_genlist_reorder_mode_get(obj))
     STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_ANIMATED);

   return ret;
}

EOLIAN int
_elm_genlist_efl_access_selection_selected_children_count_get(const Eo *objm EINA_UNUSED, Elm_Genlist_Data *pd)
{
   return eina_list_count(pd->selected);
}

EOLIAN Eo*
_elm_genlist_efl_access_selection_selected_child_get(const Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd, int child_idx)
{
   return eina_list_nth(pd->selected, child_idx);
}

EOLIAN Eina_Bool
_elm_genlist_efl_access_selection_child_select(Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd, int child_index)
{
   Elm_Gen_Item *item;
   if (pd->select_mode != ELM_OBJECT_SELECT_MODE_NONE)
     {
        EINA_INLIST_FOREACH(pd->items, item)
          {
             if (child_index-- == 0)
               {
                  elm_genlist_item_selected_set(EO_OBJ(item), EINA_TRUE);
                  return EINA_TRUE;
               }
          }
     }
   return EINA_FALSE;
}

EOLIAN Eina_Bool
_elm_genlist_efl_access_selection_selected_child_deselect(Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd, int child_index)
{
   Eo *item;
   Eina_List *l;

   EINA_LIST_FOREACH(pd->selected, l, item)
     {
        if (child_index-- == 0)
          {
             elm_genlist_item_selected_set(item, EINA_FALSE);
             return EINA_TRUE;
          }
     }
   return EINA_FALSE;
}

EOLIAN Eina_Bool
_elm_genlist_efl_access_selection_is_child_selected(Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd, int child_index)
{
   Elm_Gen_Item *item;

   EINA_INLIST_FOREACH(pd->items, item)
     {
        if (child_index-- == 0)
          {
             return elm_genlist_item_selected_get(EO_OBJ(item));
          }
     }
   return EINA_FALSE;
}

EOLIAN Eina_Bool
_elm_genlist_efl_access_selection_all_children_select(Eo *obj, Elm_Genlist_Data *pd)
{
   Elm_Gen_Item *item;

   if (!elm_genlist_multi_select_get(obj))
     return EINA_FALSE;

   EINA_INLIST_FOREACH(pd->items, item)
      elm_genlist_item_selected_set(EO_OBJ(item), EINA_TRUE);

   return EINA_TRUE;
}

EOLIAN Eina_Bool
_elm_genlist_efl_access_selection_access_selection_clear(Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd)
{
   return _all_items_deselect(pd);
}

EOLIAN Eina_Bool
_elm_genlist_efl_access_selection_child_deselect(Eo *obj EINA_UNUSED, Elm_Genlist_Data *pd, int child_index)
{
   Elm_Gen_Item *item;
   if (pd->select_mode != ELM_OBJECT_SELECT_MODE_NONE)
     {
        EINA_INLIST_FOREACH(pd->items, item)
          {
             if (child_index-- == 0)
               {
                  elm_genlist_item_selected_set(EO_OBJ(item), EINA_FALSE);
                  return EINA_TRUE;
               }
          }
     }
   return EINA_FALSE;
}

EOLIAN static Efl_Object*
_elm_genlist_efl_object_provider_find(const Eo *obj, Elm_Genlist_Data *pd, const Efl_Object *klass)
{
   if (klass == EFL_UI_FOCUS_PARENT_PROVIDER_INTERFACE)
     return pd->provider;
   return efl_provider_find(efl_super(obj, ELM_GENLIST_CLASS), klass);
}

EOLIAN static void
_elm_genlist_item_efl_ui_focus_object_setup_order_non_recursive(Eo *obj, Elm_Gen_Item *pd)
{
   Eina_List *n;
   Efl_Ui_Widget *wid;

   EINA_LIST_FOREACH(pd->contents, n, wid)
     {
        if (efl_isa(wid, EFL_UI_WIDGET_CLASS))
          _elm_widget_full_eval(wid);
     }

   if (pd->realized)
     efl_ui_focus_object_setup_order_non_recursive(efl_super(obj, ELM_GENLIST_ITEM_CLASS));
}

EOLIAN static Eina_Bool
_elm_genlist_efl_ui_widget_focus_state_apply(Eo *obj, Elm_Genlist_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect EINA_UNUSED)
{
   return efl_ui_widget_focus_state_apply(efl_super(obj, MY_CLASS), current_state, configured_state, obj);
}

EOLIAN static Efl_Ui_Focus_Object*
_elm_genlist_item_efl_ui_focus_object_focus_parent_get(const Eo *obj EINA_UNUSED, Elm_Gen_Item *pd)
{
   if (!pd->item->block) return NULL;
   return pd->item->block->adapter;
}

/* Standard widget overrides */

ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(elm_genlist, Elm_Genlist_Data)

/* Internal EO APIs and hidden overrides */

#define ELM_GENLIST_EXTRA_OPS \
   EFL_CANVAS_GROUP_CALC_OPS(elm_genlist), \
   EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_genlist)

#define ELM_GENLIST_PAN_EXTRA_OPS \
   EFL_CANVAS_GROUP_DEL_OPS(elm_genlist_pan)

#include "elm_genlist_eo.c"
#include "elm_genlist_pan_eo.c"
#include "elm_genlist_item_eo.c"
