// util funcs for the efm view
static inline int
_xtov(char x)
{
   if ((x >= '0') && (x <= '9')) return x - '0';
   if ((x >= 'a') && (x <= 'f')) return 10 + (x - 'a');
   if ((x >= 'A') && (x <= 'F')) return 10 + (x - 'A');
   return 0;
}

static unsigned int
_xtoi(const char *str)
{
   unsigned int v = 0;
   const char *s;

   for (s = str; *s; s++)
     {
        v <<= 4;
        v += _xtov(*s);
     }
   return v;
}

static char *
_escape_parse(const char *str)
{
   char *dest = malloc(strlen(str) + 1);
   char *d;
   const char *s;

   for (d = dest, s = str; *s; d++)
     {
        if ((s[0] == '%') && (!isspace(s[1])))
          {
             if ((s[1]) && (s[2]))
               {
                  *d = (_xtov(s[1]) << 4) | (_xtov(s[2]));
                  s += 3;
               }
             else s++;
          }
        else
          {
             *d = s[0];
             s++;
          }
     }
   *d = 0;
   return dest;
}

static void
_strbuf_escape_append(Eina_Strbuf *strbuf, const char *str)
{
   const char hex[] = "0123456789abcdef";
   const char *s;

   for (s = str; *s; s++)
     {
        if ((*s <- ',') ||
            (*s == '%') ||
            ((*s >= ':') && (*s <= '@')) ||
            ((*s >= '[') && (*s <= ' ')) ||
            (*s >= '{'))
          {
             eina_strbuf_append_char(strbuf, '%');
             eina_strbuf_append_char(strbuf, hex[(*s >> 4) & 0xf]);
             eina_strbuf_append_char(strbuf, hex[*s & 0xf]);
          }
        else
          eina_strbuf_append_char(strbuf, *s);
     }
}

static double
_scale_get(Smart_Data *sd)
{
   double s1 = elm_object_scale_get(sd->o_scroller);
   double s2 = elm_config_scale_get();
   return s1 * s2;
}

static Eina_Bool
_selected_icons_uri_strbuf_append(Smart_Data *sd, Eina_Strbuf *strbuf)
{
   Eina_List *bl, *il;
   Block *block;
   Icon *icon;
   Eina_Bool added = EINA_FALSE;

   // walk all files and add the selected ones to the uri list buf
   EINA_LIST_FOREACH(sd->blocks, bl, block)
     {
        if (block->selected_num == 0) continue; // skip blocks with 0 sel
        EINA_LIST_FOREACH(block->icons, il, icon)
          {
             if (!icon->selected) continue; // skip icons not selected
             eina_strbuf_append(strbuf, "file://");
             _strbuf_escape_append(strbuf, icon->sd->path);
             eina_strbuf_append_char(strbuf, '/');
             _strbuf_escape_append(strbuf, icon->info.file);
             eina_strbuf_append_char(strbuf, '\n');
             added = EINA_TRUE;
          }
     }
   return added;
}

static void
_detail_realized_items_resize(Smart_Data *sd)
{
   Eina_List *bl, *il;
   Block *block;
   Icon *icon;
   Evas_Object *o;
   int i;
   char buf[128];

   if (sd->config.view_mode != EFM_VIEW_MODE_LIST_DETAILED) return;
   EINA_LIST_FOREACH(sd->blocks, bl, block)
     {
        if (block->realized_num == 0) continue; // skip non-realized
        EINA_LIST_FOREACH(block->icons, il, icon)
          {
             if (!icon->realized) continue; // skip non-realized
             for (i = 0; i < 6; i++)
               {
                  snprintf(buf, sizeof(buf), "e.swallow.detail%i", i + 1);
                  o = icon->o_list_detail_swallow[i];
                  evas_object_size_hint_min_set(o, sd->detail_min_w[i] * _scale_get(sd), 0);
                  edje_object_part_swallow(icon->o_base, buf, o);
               }
          }
     }
}

static Icon *
_icon_find_closest(Eina_List *list, Evas_Coord x, Evas_Coord y, Icon *not_icon, Efm_Focus_Dir dir)
{
   Eina_List *l;
   Icon *icon, *closest = NULL;
   Evas_Coord closest_dist = 0x7fffffff, ix, iy, dx, dy, dist;

   EINA_LIST_FOREACH(list, l, icon)
     {
        if (icon == not_icon) continue;
        ix = icon->geom.x + (icon->geom.w / 2);
        iy = icon->geom.y + (icon->geom.h / 2);
        switch (dir)
          {
           case EFM_FOCUS_DIR_UP:
           case EFM_FOCUS_DIR_PGUP:
             if (iy >= y) continue;
             break;
           case EFM_FOCUS_DIR_DOWN:
           case EFM_FOCUS_DIR_PGDN:
             if (iy <= y) continue;
             break;
           case EFM_FOCUS_DIR_LEFT:
             if (ix >= x) continue;
             break;
           case EFM_FOCUS_DIR_RIGHT:
             if (ix <= x) continue;
             break;
           default:
             break;
          }
        dx = x - ix;
        dy = y - iy;
        dist = (dx * dx) + (dy * dy);
        if (dist < closest_dist)
          {
             closest = icon;
             closest_dist = dist;
          }
     }
   return closest;
}

static void
_efm_focus_position(Smart_Data *sd)
{
   Evas_Coord ix, iy, iw, ih, ix2, iy2, iw2, ih2;

   if (!sd->last_focused) return;
   ix2 = ix = sd->last_focused->geom.x;
   iy2 = iy = sd->last_focused->geom.y;
   iw2 = iw = sd->last_focused->geom.w;
   ih2 = ih = sd->last_focused->geom.h;
   if (sd->last_focused_before)
     {
        ix = sd->last_focused_before->geom.x;
        iy = sd->last_focused_before->geom.y;
        iw = sd->last_focused_before->geom.w;
        ih = sd->last_focused_before->geom.h;
     }
   if (sd->focus_pos < 1.0)
     {
        ix2 = ((ix * (1.0 - sd->focus_pos)) + (ix2 * sd->focus_pos));
        iy2 = ((iy * (1.0 - sd->focus_pos)) + (iy2 * sd->focus_pos));
        iw2 = ((iw * (1.0 - sd->focus_pos)) + (iw2 * sd->focus_pos));
        ih2 = ((ih * (1.0 - sd->focus_pos)) + (ih2 * sd->focus_pos));
     }
   evas_object_geometry_set(sd->o_focus,
                            sd->geom.x + ix2, sd->geom.y + iy2,
                            iw2, ih2);
}

static Eina_Rectangle
_efm_sel_rect_get(Smart_Data *sd)
{
   Evas_Coord x1, y1, x2, y2;
   Eina_Rectangle rect;

   if (sd->sel_x1 < sd->sel_x2)
     {
        x1 = sd->sel_x1;
        x2 = sd->sel_x2;
     }
   else
     {
        x1 = sd->sel_x2;
        x2 = sd->sel_x1;
     }
   if (sd->sel_y1 < sd->sel_y2)
     {
        y1 = sd->sel_y1;
        y2 = sd->sel_y2;
     }
   else
     {
        y1 = sd->sel_y2;
        y2 = sd->sel_y1;
     }
   rect.x = x1;
   rect.y = y1;
   rect.w = x2 - x1 + 1;
   rect.h = y2 - y1 + 1;
   return rect;
}

static void
_efm_sel_position(Smart_Data *sd)
{
   Eina_Rectangle rect;

   if (!sd->sel_show) return;
   rect = _efm_sel_rect_get(sd);
   evas_object_geometry_set(sd->o_sel,
                            sd->geom.x + rect.x, sd->geom.y + rect.y,
                            rect.w, rect.h);
}

static Eina_Bool
_cb_focus_anim(void *data)
{
   Smart_Data *sd = data;

   sd->focus_pos = (ecore_loop_time_get() - sd->focus_start_time) / FOCUS_ANIM_TIME;
   if (sd->focus_pos < 0.0) sd->focus_pos = 0.0;
   _efm_focus_position(sd);
   if (sd->focus_pos >= 1.0)
     {
        sd->focus_animator = NULL;
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static void
_icon_focus(Smart_Data *sd)
{
   Evas_Coord ix, iy, iw, ih;

   if ((!sd->last_focused) && (!sd->last_focused_before)) return;
   if (!sd->focus_show)
     { // just show the focus now
        if (sd->last_focused)
          {
             sd->focus_show = EINA_TRUE;
             ix = sd->last_focused->geom.x;
             iy = sd->last_focused->geom.y;
             iw = sd->last_focused->geom.w;
             ih = sd->last_focused->geom.h;
             evas_object_geometry_set(sd->o_focus,
                                      sd->geom.x + ix, sd->geom.y + iy, iw, ih);
             edje_object_signal_emit(sd->o_focus, "elm,action,focus,show", "elm");
             evas_object_raise(sd->o_focus);
             evas_object_show(sd->o_focus);
             sd->last_focused_before = sd->last_focused;
          }
     }
   else
     { // transition focus to new place
        if (sd->last_focused_before)
          {
             ix = sd->last_focused_before->geom.x;
             iy = sd->last_focused_before->geom.y;
             iw = sd->last_focused_before->geom.w;
             ih = sd->last_focused_before->geom.h;
          }
        else if (sd->last_focused)
          {
             ix = sd->last_focused->geom.x;
             iy = sd->last_focused->geom.y;
             iw = sd->last_focused->geom.w;
             ih = sd->last_focused->geom.h;
          }
        evas_object_geometry_set(sd->o_focus,
                                 sd->geom.x + ix, sd->geom.y + iy, iw, ih);
        edje_object_signal_emit(sd->o_focus, "elm,action,focus,show", "elm");
        evas_object_raise(sd->o_focus);
        evas_object_show(sd->o_focus);
        if (!sd->focus_animator)
          sd->focus_animator = ecore_animator_add(_cb_focus_anim, sd);
     }
   if (sd->o_scroller)
     {
        if (sd->last_focused)
          {
             ix = sd->last_focused->geom.x;
             iy = sd->last_focused->geom.y;
             iw = sd->last_focused->geom.w;
             ih = sd->last_focused->geom.h;
             elm_scroller_region_bring_in(sd->o_scroller, ix, iy, iw, ih);
          }
     }
}

static Eina_Bool
_icon_focus_dir(Smart_Data *sd, Efm_Focus_Dir dir)
{
   Eina_List *bl, *il, *realized = NULL;
   Icon *icon;
   Block *block;
   Evas_Coord ix, iy, iw, ih, vh;

   if (!sd->icons) return EINA_FALSE;
   if (!sd->last_focused)
     {
        EINA_LIST_FOREACH(sd->blocks, bl, block)
          {
             if (block->realized_num > 0)
               {
                  EINA_LIST_FOREACH(block->icons, il, icon)
                    {
                       if (icon->realized)
                         realized = eina_list_append(realized, icon);
                    }
               }
          }
        icon = NULL;

        switch (dir)
          {
           case EFM_FOCUS_DIR_UP:
           case EFM_FOCUS_DIR_PGUP: // bottom right
             icon = _icon_find_closest(realized, sd->geom.w, sd->geom.h, NULL, dir);
             break;
           case EFM_FOCUS_DIR_DOWN:
           case EFM_FOCUS_DIR_RIGHT:
           case EFM_FOCUS_DIR_PGDN: // top left
             icon = _icon_find_closest(realized, 0, 0, NULL, dir);
             break;
           case EFM_FOCUS_DIR_LEFT: // top right
             icon = _icon_find_closest(realized, sd->geom.w, 0, NULL, dir);
             break;
           default:
             break;
          }
        eina_list_free(realized);
        if (!icon) return EINA_FALSE;
        printf("focus %s\n", icon->info.file);
        sd->last_focused_before = NULL;
        sd->last_focused = icon;
        sd->focus_pos = 1.0;
     }
   else
     {
        ix = sd->last_focused->geom.x;
        iy = sd->last_focused->geom.y;
        iw = sd->last_focused->geom.w;
        ih = sd->last_focused->geom.h;
        icon = NULL;
        switch (dir)
          {
           case EFM_FOCUS_DIR_UP:
             if (!((sd->last_focused) &&
                   (sd->last_focused == sd->icons->data))) // first item
               {
                  icon = _icon_find_closest(sd->icons, ix + (iw / 2), iy, sd->last_focused, dir);
               }
             break;
           case EFM_FOCUS_DIR_DOWN:
             if (!((sd->last_focused) &&
                   (sd->last_focused == eina_list_last(sd->icons)->data))) // last item
               {
                  icon = _icon_find_closest(sd->icons, ix + (iw / 2), iy + ih, sd->last_focused, dir);
               }
             break;
           case EFM_FOCUS_DIR_LEFT:
             if (!((sd->last_focused) &&
                   (sd->last_focused == sd->icons->data))) // first item
               {
                  icon = _icon_find_closest(sd->icons, ix, iy + (ih / 2), sd->last_focused, dir);
                  if (!icon)
                    {
                       EINA_LIST_FOREACH(sd->icons, il, icon)
                         {
                            if (icon == sd->last_focused)
                              {
                                 if (il->prev) icon = il->prev->data;
                                 break;
                              }
                         }
                    }
               }
             break;
           case EFM_FOCUS_DIR_RIGHT:
             if (!((sd->last_focused) &&
                   (sd->last_focused == eina_list_last(sd->icons)->data))) // last item
               {
                  icon = _icon_find_closest(sd->icons, ix + iw, iy + (ih / 2), sd->last_focused, dir);
                  if (!icon)
                    {
                       EINA_LIST_FOREACH(sd->icons, il, icon)
                         {
                            if (icon == sd->last_focused)
                              {
                                 if (il->next) icon = il->next->data;
                                 break;
                              }
                         }
                    }
               }
             break;
           case EFM_FOCUS_DIR_PGUP:
             if (!((sd->last_focused) &&
                   (sd->last_focused == sd->icons->data))) // first item
               {
                  vh = 0;
                  if (sd->o_scroller)
                    evas_object_geometry_get(sd->o_scroller, NULL, NULL, NULL, &vh);
                  icon = _icon_find_closest(sd->icons, ix + (iw / 2), iy - vh, sd->last_focused, dir);
                  if (!icon) icon = sd->icons->data;
                  printf("pu %p\n", icon);
               }
             break;
           case EFM_FOCUS_DIR_PGDN:
             if (!((sd->last_focused) &&
                   (sd->last_focused == eina_list_last(sd->icons)->data))) // last item
               {
                  vh = 0;
                  if (sd->o_scroller)
                    evas_object_geometry_get(sd->o_scroller, NULL, NULL, NULL, &vh);
                  icon = _icon_find_closest(sd->icons, ix + (iw / 2), iy + ih + vh, sd->last_focused, dir);
                  if (!icon) icon = eina_list_last(sd->icons)->data;
                  printf("pd %p + %i\n", icon, vh);
               }
             break;
           default:
             break;
          }
        if (!icon) return EINA_FALSE;
        printf("focus %s\n", icon->info.file);
        sd->last_focused_before = sd->last_focused;
        sd->last_focused = icon;
        sd->focus_pos = 0.0;
        sd->focus_start_time = ecore_loop_time_get();
     }

   _icon_focus(sd);
   return EINA_TRUE;
}

static void
_icon_focus_show(Smart_Data *sd)
{
   if (sd->focus_show) return;
   sd->focus_show = EINA_TRUE;
   evas_object_show(sd->o_focus);
}

static void
_icon_focus_hide(Smart_Data *sd)
{
   if (!sd->focus_show) return;
   sd->focus_show = EINA_FALSE;
   evas_object_hide(sd->o_focus);
}

static void
_icon_object_clear(Icon *icon)
{
   int i;

   // XXX: have a cache for base and icon objects to avoid re-creating them
   for (i = 0; i < 6; i++)
     {
        if (icon->o_list_detail_swallow[i])
          {
             evas_object_del(icon->o_list_detail_swallow[i]);
             icon->o_list_detail_swallow[i] = NULL;
          }
        if (icon->o_list_detail_swallow2[i])
          {
             evas_object_del(icon->o_list_detail_swallow2[i]);
             icon->o_list_detail_swallow2[i] = NULL;
          }
     }
   if (icon->o_icon)
     {
        evas_object_del(icon->o_icon);
        icon->o_icon = NULL;
     }
   if (icon->o_entry)
     {
        evas_object_del(icon->o_entry);
        icon->o_entry = NULL;
     }
   if (icon->o_base)
     {
        evas_object_del(icon->o_base);
        icon->o_base = NULL;
     }
   if ((icon->over) && (icon->sd))
     evas_object_hide(icon->sd->o_over);
}

static void
_icon_text_update(Icon *icon)
{ // update the label text to be the file or correct label string
   const char *str = NULL;
   char *txt = NULL;

   if (!icon->o_base) return;
   if (icon->down)
     {
        if (icon->info.label_clicked) str = icon->info.label_clicked;
     }
   if ((icon->selected) && (!str))
     {
        if (icon->info.label_selected) str = icon->info.label_selected;
     }
   if (!str)
     {
        if (icon->info.label) str = icon->info.label;
        else if (icon->info.file) str = icon->info.file;
     }
   if (!str) str = "???";
   if (icon->sd->config.view_mode >= EFM_VIEW_MODE_LIST) txt = strdup(str);
   else txt = elm_entry_utf8_to_markup(str);
   edje_object_part_text_set(icon->o_base, "e.text.label", txt);
   free(txt);
}

static void
_icon_rename_end(Icon *icon)
{
   if (icon->sd->rename_icon != icon) return;
   icon->sd->rename_icon = NULL;
   printf("end renaming icon %s\n", icon->info.file);
   icon->renaming = EINA_FALSE;
   evas_object_del(icon->o_entry);
   icon->o_entry = NULL;
   elm_object_focus_set(icon->sd->o_scroller, EINA_TRUE);
}

static void
_cb_entry_aborted(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Icon *icon = data;

   _icon_rename_end(icon);
}

static void
_cb_entry_activated(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Icon *icon = data;
   const char *txt;
   char *utf;

   txt = elm_object_text_get(icon->o_entry);
   if (txt)
     {
        utf = elm_entry_markup_to_utf8(txt);
        if (utf)
          {
             printf("XXX: rename to [%s]\n", utf);
             free(utf);
          }
     }
   _icon_rename_end(icon);
}

static void
_icon_rename_begin(Icon *icon)
{
   Evas_Object *o;
   char *mkup;

   if (icon->sd->rename_icon) _icon_rename_end(icon->sd->rename_icon);
   if (icon->sd->drag) return;
   icon->sd->rename_icon = icon;
   icon->renaming = EINA_TRUE;
   icon->o_entry = o = elm_entry_add(icon->sd->o_scroller);
   elm_entry_single_line_set(o, EINA_TRUE);
   elm_entry_scrollable_set(o, EINA_TRUE);
   elm_scroller_policy_set(o, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
   evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   // s = elm_entry_markup_to_utf8(s);
   mkup = elm_entry_utf8_to_markup(icon->info.file);
   elm_object_text_set(o, mkup);
   free(mkup);
   evas_object_smart_callback_add(o, "aborted", _cb_entry_aborted, icon);
   evas_object_smart_callback_add(o, "activated", _cb_entry_activated, icon);
   edje_object_part_swallow(icon->o_base, "e.swallow.entry", o);
   evas_object_show(o);
   elm_object_focus_set(o, EINA_TRUE);
}

static Eina_Bool
_cb_icon_longpress_timer(void *data)
{
   Icon *icon = data;

   icon->longpress_timer = NULL;
   printf("XXX: Long press [%s]\n", icon->info.file);
   return EINA_FALSE;
}

static void
_icon_file_set(Icon *icon, const char *file)
{
   const char *ext;

   ext = strrchr(file, '.');
   if (ext && ((!strcasecmp(ext, ".webm")) ||
               (!strcasecmp(ext, ".mp4")) ||
               (!strcasecmp(ext, ".m4v"))))
     {
        efm_icon_video_set(icon->o_icon, file);
     }
   else
     {
        efm_icon_file_set(icon->o_icon, file);
     }
}

static void
_cb_icon_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Down *ev = event_info;
   Icon *icon = data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if ((icon->sd->rename_icon) && (icon->sd->rename_icon != icon))
     _icon_rename_end(icon->sd->rename_icon);
   elm_object_focus_set(icon->sd->o_scroller, EINA_TRUE);
   icon->sd->key_control = EINA_FALSE;
   icon->sd->last_focused = NULL;
   icon->sd->last_focused_before = NULL;
   icon->sd->just_dragged = EINA_FALSE;
   _icon_focus_hide(icon->sd);
   if (ev->button == 1)
     {
        if (icon->longpress_timer) ecore_timer_del(icon->longpress_timer);
        icon->longpress_timer = ecore_timer_add(ICON_LONGPRESS_TIMER,
                                                _cb_icon_longpress_timer,
                                                icon);
        icon->down = EINA_TRUE;
        if (ev->flags == EVAS_BUTTON_NONE)
          { // a click - not double-clicked. regular click
             icon->down_x = ev->canvas.x;
             icon->down_y = ev->canvas.y;
             if (!icon->info.thumb)
               {
                  if (icon->edje)
                    edje_object_signal_emit(icon->o_icon, "e,state,clicked", "e");
                  else if (icon->info.icon_clicked)
                    _icon_file_set(icon, icon->info.icon_clicked);
               }
             _icon_text_update(icon);
             // XXX; start longpress timer
          }
        else if (ev->flags == EVAS_BUTTON_DOUBLE_CLICK)
          { // double clicked
             // XXX:
             printf("XXX: DBL\n");
          }
     }
}

static void
_icon_select_update(Icon *icon)
{
   int i;

   if (!icon->o_base) return;
   if (icon->selected)
     {
        edje_object_signal_emit(icon->o_base, "e,state,selected", "e");
        if (icon->edje)
          edje_object_signal_emit(icon->o_icon, "e,state,selected", "e");
        else if (icon->info.icon_selected)
          _icon_file_set(icon, icon->info.icon_selected);
        for (i = 0; i < 6; i++)
          {
             if (icon->o_list_detail_swallow2[i])
               edje_object_signal_emit(icon->o_list_detail_swallow2[i],
                                       "e,state,selected", "e");
          }
     }
   else
     {
        edje_object_signal_emit(icon->o_base, "e,state,unselected", "e");
        if (icon->edje)
          edje_object_signal_emit(icon->o_icon, "e,state,normal", "e");
        else if ((icon->info.pre_lookup_icon) &&
                 (icon->info.pre_lookup_icon[0] == '/'))
          _icon_file_set(icon, icon->info.pre_lookup_icon);
        for (i = 0; i < 6; i++)
          {
             if (icon->o_list_detail_swallow2[i])
               edje_object_signal_emit(icon->o_list_detail_swallow2[i],
                                       "e,state,unselected", "e");
          }
     }
   _icon_text_update(icon);
}

static Eina_Bool
_cb_dnd_over_open_timer(void *data)

{
   Smart_Data *sd = data;

   sd->dnd_over_open_timer = NULL;
   if (sd->drop_over)
     {
        printf("XXX: open hover dir [%s]\n", sd->drop_over->info.file);
        // XXX: open dir smart callback
     }
   return EINA_FALSE;
}

static void
_icon_over_off(Icon *icon)
{
   icon->over = EINA_FALSE;
   edje_object_signal_emit(icon->sd->o_over, "e,state,unselected", "e");
   evas_object_hide(icon->sd->o_over);
   if (icon->sd->dnd_over_open_timer)
     {
        ecore_timer_del(icon->sd->dnd_over_open_timer);
        icon->sd->dnd_over_open_timer = NULL;
     }
}

static void
_icon_over_on(Icon *icon)
{
   if (icon->over) return;
   if (icon->sd->over_icon)
     {
        _icon_over_off(icon->sd->over_icon);
        icon->sd->over_icon = NULL;
     }
   if (!icon->info.dir) return;
   if ((icon->sd->drag_icon) && (icon->info.file) &&
       (icon->sd->drag_icon->info.file) &&
       (!strcmp(icon->sd->drag_icon->info.file, icon->info.file)))
     return;
   if (icon->sd->dnd_over_open_timer)
     ecore_timer_del(icon->sd->dnd_over_open_timer);
   icon->sd->dnd_over_open_timer = ecore_timer_add(DND_OVER_OPEN_TIMER,
                                                   _cb_dnd_over_open_timer,
                                                   icon->sd);
   icon->sd->over_icon = icon;
   icon->over = EINA_TRUE;
   edje_object_signal_emit(icon->sd->o_over, "e,state,selected", "e");
   evas_object_raise(icon->sd->o_over);
   evas_object_show(icon->sd->o_over);
   evas_object_geometry_set(icon->sd->o_over,
                            icon->sd->geom.x + icon->geom.x,
                            icon->sd->geom.y + icon->geom.y,
                            icon->geom.w,
                            icon->geom.h);
}

static void
_icon_select(Icon *icon)
{
   if (icon->selected) return;
   icon->selected = EINA_TRUE;
   icon->block->selected_num++;
   icon->sd->last_selected = icon;
   _icon_select_update(icon);
}

static void
_icon_unselect(Icon *icon)
{
   if (!icon->selected) return;
   if (icon->sd->last_selected == icon) icon->sd->last_selected = NULL;
   icon->selected = EINA_FALSE;
   icon->block->selected_num--;
   _icon_select_update(icon);
}

static Eina_Bool
_unselect_all(Smart_Data *sd)
{
   Eina_List *bl, *il;
   Icon *icon;
   Block *block;
   Eina_Bool had_selected = EINA_FALSE;

   EINA_LIST_FOREACH(sd->blocks, bl, block)
     {
        if (block->selected_num > 0)
          {
             had_selected = EINA_TRUE;
             EINA_LIST_FOREACH(block->icons, il, icon)
               {
                  _icon_unselect(icon);
               }
          }
     }
   return had_selected;
}

static void
_select_range(Icon *icon_from, Icon *icon_to)
{
   Eina_List *l;
   Icon *icon;
   Eina_Bool sel = EINA_FALSE;

   EINA_LIST_FOREACH(icon_from->sd->icons, l, icon)
     {
        if (!sel)
          {
             if ((icon_from == icon) || (icon_to == icon))
               {
                  _icon_select(icon);
                  sel = EINA_TRUE;
               }
          }
        else
          {
             _icon_select(icon);
             if ((icon_from == icon) || (icon_to == icon)) return;
          }
     }
}

static void
_cb_icon_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Up *ev = event_info;
   Icon *icon = data;
   Evas_Coord dx, dy;
   Eina_Bool dragged = EINA_FALSE;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) goto done;
   if ((ev->button == 1) && (icon->down))
     { // click + release
        // XXX: kill longpress timer
        dx = ev->canvas.x - icon->down_x;
        dy = ev->canvas.y - icon->down_y;
        if (((dx * dx) + (dy * dy)) > (5 * 5)) dragged = EINA_TRUE;
        if (icon->longpress_timer)
          {
             ecore_timer_del(icon->longpress_timer);
             icon->longpress_timer = NULL;
          }
        if (!dragged)
          {
             printf("XXX: mouse clicked\n");
             if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
               { // range select
                  if (icon->sd->last_selected)
                    _select_range(icon->sd->last_selected, icon);
                  else
                    _icon_select(icon);
               }
             else if (evas_key_modifier_is_set(ev->modifiers, "Control"))
               { // multi-single select toggle
                  if (!icon->selected) _icon_select(icon);
                  else _icon_unselect(icon);
               }
             else
               { // select just one file so unselect previous files
                  _unselect_all(icon->sd);
                  if (!icon->selected) _icon_select(icon);
                  else _icon_unselect(icon);
               }
          }
        else _icon_select_update(icon);
        icon->sd->last_focused = icon;
     }
   else if (ev->button == 3)
     { // right mouse click
        // XXX: handle right mouse click on an icon
        printf("XXX: right mouse\n");
     }
   _icon_focus_hide(icon->sd);
done:
   icon->down = EINA_FALSE;
}

static Icon *
_icon_dup(Icon *icon)
{
   Icon *icon2;

   icon2 = calloc(1, sizeof(Icon));
   if (!icon2) return NULL;
   icon2->geom = icon->geom;
   icon2->sd = icon->sd;
   icon2->down_x = icon->down_x;
   icon2->down_y = icon->down_y;
   icon2->selected = icon->selected;
   icon2->down = icon->down;
   icon2->edje = icon->edje;
   icon2->info = icon->info;
#define DUPSTRSHARE(X) if (icon->info.X) icon2->info.X = eina_stringshare_add(icon->info.X)
   DUPSTRSHARE(file);
   DUPSTRSHARE(label);
   DUPSTRSHARE(label_selected);
   DUPSTRSHARE(label_clicked);
   DUPSTRSHARE(mime);
   DUPSTRSHARE(icon);
   DUPSTRSHARE(icon_selected);
   DUPSTRSHARE(icon_clicked);
   DUPSTRSHARE(mime_icon);
   DUPSTRSHARE(pre_lookup_icon);
   DUPSTRSHARE(thumb);
#undef DUPSTRSHARE
   icon2->cmd = cmd_dup(icon->cmd);
   return icon2;
}

static void
_cb_icon_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Move *ev = event_info;
   Icon *icon = data;
   Evas_Coord dx, dy;
   Eina_Bool dragged = EINA_FALSE;

   if (!icon->down) return;
   // XXX: kill longpress timer
   dx = ev->cur.canvas.x - icon->down_x;
   dy = ev->cur.canvas.y - icon->down_y;
   if (((dx * dx) + (dy * dy)) > (5 * 5)) dragged = EINA_TRUE;
   if (!dragged) return;
   if (icon->sd->drag) return;
   if (icon->longpress_timer)
     {
        ecore_timer_del(icon->longpress_timer);
        icon->longpress_timer = NULL;
     }
   // mouse has been dragged
   if (!icon->selected)
     {
        if ((!evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
            (!evas_key_modifier_is_set(ev->modifiers, "Control")))
        _unselect_all(icon->sd);
        _icon_select(icon);
     }
   icon->down = EINA_FALSE;
   _icon_select_update(icon);
   if (icon->renaming) return;
   _drag_start(icon);
}

static void
_icon_resized(Icon *icon)
{
   int w, h;

   // get the icon size and if it has alpha
   efm_icon_size_get(icon->o_icon, &w, &h);
   // set the aspect ratio hint based on the above and re-swallow
   evas_object_size_hint_aspect_set(icon->o_icon, EVAS_ASPECT_CONTROL_BOTH, w, h);
   if ((efm_icon_mono_get(icon->o_icon)) &&
       (edje_object_part_exists(icon->o_base, "e.swallow.icon_mono")))
     edje_object_part_swallow(icon->o_base, "e.swallow.icon_mono", icon->o_icon);
   else
     edje_object_part_swallow(icon->o_base, "e.swallow.icon", icon->o_icon);
}

static void
_cb_icon_resized(void *data, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{ // the icon image/thumb has been loaded so update the icon base
   Icon *icon = data;

   _icon_resized(icon);
}

static void
_cb_icon_loaded(void *data, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{ // the icon image/thumb has been loaded so update the icon base
   Icon *icon = data;
   Eina_Bool alpha;

   alpha = efm_icon_alpha_get(icon->o_icon);
   _icon_resized(icon);
   if (icon->info.thumb)
     { // if it's a thumbnail - let's emit to show it
        if (alpha)
          edje_object_signal_emit(icon->o_base, "e,action,thumb,gen,alpha", "e");
        else
          edje_object_signal_emit(icon->o_base, "e,action,thumb,gen", "e");
     }
}

static void
_cb_icon_label_longpress(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
{
   Icon *icon = data;

   if ((icon->sd->drag) || (icon->sd->just_dragged)) return;
   _icon_rename_begin(icon);
}

static void
_icon_object_add(Icon *icon, Smart_Data *sd, Evas *e, const char *theme_edj_file, Eina_Bool clip_set, int num)
{ // either image or theme element
   Evas_Object *o, *o2;
   const char *icon_file = NULL, *icon_group = NULL, *icon_thumb = NULL;
   const char *ic;
   char buf[PATH_MAX], buf2[128], *tmps;
   int i;

   if (icon->o_base) return;
   icon_thumb = icon->info.thumb;
   if ((!icon->info.thumb) && (icon->info.pre_lookup_icon))
     { // we're being asked for a std theme icon
        ic = icon->info.pre_lookup_icon;
        if (icon->down)
          {
             if (icon->info.icon_clicked) ic = icon->info.icon_clicked;
          }
        else if (icon->selected)
          {
             if (icon->info.icon_selected) ic = icon->info.icon_selected;
          }
        if (ic[0] == '/')
        icon_file = ic;
        else
          {
             snprintf(buf, sizeof(buf), "e/icons/%s", ic);
             icon_file = elm_theme_group_path_find(NULL, buf);
             if (icon_file) icon_group = buf;
          }
     }
   if ((!icon_file) && (!icon_thumb))
     { // theme icon doesn't exit or other reason
        // use this icon file
        if (icon->info.icon) icon_file = icon->info.icon;
        else
          { // try a mime type std icon
             snprintf(buf, sizeof(buf), "e/icons/fileman/mime/%s",
                      icon->info.mime);
             icon_file = elm_theme_group_path_find(NULL, buf);
             // mime type icon exists
             if (icon_file) icon_group = buf;
             else
               { // use an out-of-theme std mime icon
                  if (icon->info.mime_icon) icon_file = icon->info.mime_icon;
               }
          }
     }
   if ((!icon_group) && (!icon_file) && (!icon_thumb))
     { // fallback to plain empty file when no icon found
        snprintf(buf, sizeof(buf), "e/icons/fileman/mime/inode/file");
        icon_file = elm_theme_group_path_find(NULL, buf);
        icon_group = buf;
     }
   // about to realize if it hasn't been
   // XXX: have a cache for base and icon objects to avoid re-creating them
   icon->o_base = o = edje_object_add(e);
   if (sd->config.view_mode == EFM_VIEW_MODE_ICONS)
     edje_object_file_set(o, theme_edj_file, "e/fileman/default/icon/fixed");
   else if (sd->config.view_mode == EFM_VIEW_MODE_LIST)
     {
        if (num & 0x1)
          edje_object_file_set(o, theme_edj_file,
                               "e/fileman/default/list_odd/fixed");
        else
          edje_object_file_set(o, theme_edj_file,
                               "e/fileman/default/list/fixed");
     }
   else if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
     {
        const char *s;

        if (num & 0x1)
          edje_object_file_set(o, theme_edj_file,
                               "e/fileman/default/list_odd/detailed");
        else
          edje_object_file_set(o, theme_edj_file,
                               "e/fileman/default/list/detailed");
        s = cmd_key_find(icon->cmd, "size");
        if (s)
          {
             unsigned long long size = atoll(s);

             icon->o_list_detail_swallow[0] = o2 =
               elm_grid_add(sd->o_scroller);
             elm_grid_size_set(o2, 1, 1);
             evas_object_size_hint_min_set
               (o2, sd->detail_min_w[0] * _scale_get(sd), 0);
             edje_object_part_swallow(icon->o_base, "e.swallow.detail1", o2);
             icon->o_list_detail_swallow2[0] = o2 =
               edje_object_add(e);
             edje_object_file_set(o2, theme_edj_file,
                                  "e/fileman/default/filesize");
             if (icon->selected)
               edje_object_signal_emit(o2, "e,state,selected", "e");
             else
               edje_object_signal_emit(o2, "e,state,unselected", "e");
             elm_grid_pack(icon->o_list_detail_swallow[0], o2, 0, 0, 1, 1);
             evas_object_show(o2);

             if (sd->file_max > 0)
               _size_message(o2, (double)size / (double)sd->file_max);
             else
               _size_message(o2, 0.0);

             if (size < 1024)
               {
                  edje_object_part_text_set(o2, "e.text.unit", "b");
               }
             else if (size < (1024LL * 1024LL))
               {
                  size = ((size + ((1024LL) - 1)) >> 10);
                  edje_object_part_text_set(o2, "e.text.unit", "K");
               }
             else if (size < (1024LL * 1024LL * 1024LL))
               {
                  size = ((size + ((1024LL * 1024LL) - 1)) >> 20);
                  edje_object_part_text_set(o2, "e.text.unit", "M");
               }
             else if (size < (1024LL * 1024LL * 1024LL * 1024LL))
               {
                  size = ((size + ((1024LL * 1024LL * 1024LL) - 1)) >> 30);
                  edje_object_part_text_set(o2, "e.text.unit", "G");
               }
             else if (size < (1024LL * 1024LL * 1024LL * 1024LL * 1024LL))
               {
                  size = ((size + ((1024LL * 1024LL * 1024LL * 1024LL) - 1)) >> 40);
                  edje_object_part_text_set(o2, "e.text.unit", "T");
               }
             snprintf(buf2, sizeof(buf2), "%i", (int)size);
             edje_object_part_text_set(o2, "e.text.label", buf2);
             edje_object_message_signal_process(o2);
          }
        s = cmd_key_find(icon->cmd, "mtime");
        if (s)
          {
             time_t tmpt = atoll(s);
             struct tm *info;

             info = localtime(&tmpt);

             icon->o_list_detail_swallow[1] = o2 =
               elm_grid_add(sd->o_scroller);
             elm_grid_size_set(o2, 1, 1);
             evas_object_size_hint_min_set
               (o2, sd->detail_min_w[1] * _scale_get(sd), 0);
             edje_object_part_swallow(icon->o_base, "e.swallow.detail2", o2);
             icon->o_list_detail_swallow2[1] = o2 =
               edje_object_add(e);
             edje_object_file_set(o2, theme_edj_file,
                                  "e/fileman/default/filedate");
             if (icon->selected)
               edje_object_signal_emit(o2, "e,state,selected", "e");
             else
               edje_object_signal_emit(o2, "e,state,unselected", "e");
             elm_grid_pack(icon->o_list_detail_swallow[1], o2, 0, 0, 1, 1);
             evas_object_show(o2);

             tmps = eina_strftime("%y", info);
             edje_object_part_text_set(o2, "e.text.year", tmps);
             free(tmps);
             tmps = eina_strftime("%b", info);
             edje_object_part_text_set(o2, "e.text.month", tmps);
             free(tmps);
             tmps = eina_strftime("%d", info);
             edje_object_part_text_set(o2, "e.text.day", tmps);
             free(tmps);
             tmps = eina_strftime("%H:%M:%S", info);
             edje_object_part_text_set(o2, "e.text.time", tmps);
             free(tmps);
//             tmps = eina_strftime("%b %d %y %H:%M:%S", info);
//             edje_object_part_text_set(icon->o_base, "e.text.detail2", tmps);
//             free(tmps);
          }
        s = icon->info.mime;
        if (!s) s = "";
        edje_object_part_text_set(icon->o_base, "e.text.detail3", s);
        s = cmd_key_find(icon->cmd, "user");
        if (s) edje_object_part_text_set(icon->o_base, "e.text.detail4", s);
        s = cmd_key_find(icon->cmd, "group");
        if (s) edje_object_part_text_set(icon->o_base, "e.text.detail5", s);
        s = cmd_key_find(icon->cmd, "mode");
        if (s)
          {
             int mode = _xtoi(s);

             icon->o_list_detail_swallow[5] = o2 =
               elm_grid_add(sd->o_scroller);
             elm_grid_size_set(o2, 1, 1);
             evas_object_size_hint_min_set
               (o2, sd->detail_min_w[5] * _scale_get(sd), 0);
             edje_object_part_swallow(icon->o_base, "e.swallow.detail6", o2);
             icon->o_list_detail_swallow2[5] = o2 =
               edje_object_add(e);
             edje_object_file_set(o2, theme_edj_file,
                                  "e/fileman/default/fileperms");
             if (icon->selected)
               edje_object_signal_emit(o2, "e,state,selected", "e");
             else
               edje_object_signal_emit(o2, "e,state,unselected", "e");
             elm_grid_pack(icon->o_list_detail_swallow[5], o2, 0, 0, 1, 1);
             evas_object_show(o2);
             edje_object_signal_emit(o2, "e,type,reset", "e");
             if      (S_ISREG(mode))  edje_object_signal_emit(o2, "e,type,none", "e");
             else if (S_ISDIR(mode))  edje_object_signal_emit(o2, "e,type,dir", "e");
             else if (S_ISLNK(mode))  edje_object_signal_emit(o2, "e,type,link", "e");
             else if (S_ISFIFO(mode)) edje_object_signal_emit(o2, "e,type,pipe", "e");
             else if (S_ISSOCK(mode)) edje_object_signal_emit(o2, "e,type,socket", "e");
             else if (S_ISBLK(mode))  edje_object_signal_emit(o2, "e,type,block", "e");
             else if (S_ISCHR(mode))  edje_object_signal_emit(o2, "e,type,char", "e");
             edje_object_signal_emit(o2, "e,perm,reset", "e");
             if      ((mode & S_ISUID))  edje_object_signal_emit(o2, "e,perm,user,setuid", "e");
             if      ((mode & S_IRUSR))  edje_object_signal_emit(o2, "e,perm,user,read", "e");
             if      ((mode & S_IWUSR))  edje_object_signal_emit(o2, "e,perm,user,write", "e");
             if      ((mode & S_IXUSR))  edje_object_signal_emit(o2, "e,perm,user,execute", "e");
             if      ((mode & S_ISGID))  edje_object_signal_emit(o2, "e,perm,group,setuid", "e");
             if      ((mode & S_IRGRP))  edje_object_signal_emit(o2, "e,perm,group,read", "e");
             if      ((mode & S_IWGRP))  edje_object_signal_emit(o2, "e,perm,group,write", "e");
             if      ((mode & S_IXGRP))  edje_object_signal_emit(o2, "e,perm,group,execute", "e");
             if      ((mode & S_IROTH))  edje_object_signal_emit(o2, "e,perm,other,read", "e");
             if      ((mode & S_IWOTH))  edje_object_signal_emit(o2, "e,perm,other,write", "e");
             if      ((mode & S_IXOTH))  edje_object_signal_emit(o2, "e,perm,other,execute", "e");
             edje_object_message_signal_process(o2);
          }
        if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
          {
             for (i = 2; i < 5; i++)
               {
                  icon->o_list_detail_swallow[i] = o2 =
                    evas_object_rectangle_add(e);
                  evas_object_color_set(o2, 0, 0, 0, 0);
                  evas_object_size_hint_min_set
                    (o2, sd->detail_min_w[i] * _scale_get(sd), 0);
                  snprintf(buf2, sizeof(buf2), "e.swallow.detail%i", i + 1);
                  edje_object_part_swallow(icon->o_base, buf2, o2);
               }
          }
     }
   edje_object_preload(o, EINA_FALSE);
   edje_object_signal_callback_add(o, "e,action,label,click", "e",
                                   _cb_icon_label_longpress, icon);
   icon->edje = EINA_FALSE;
   if ((!icon_group) && (icon_file))
     { // image file
        icon->o_icon = o = efm_icon_add(o);
        evas_object_smart_callback_add(o, "resized", _cb_icon_resized, icon);
        evas_object_smart_callback_add(o, "loaded", _cb_icon_loaded, icon);
        _icon_file_set(icon, icon_file);
     }
   else if ((icon_group) && (icon_file))
     { // theme element
        icon->o_icon = o = edje_object_add(e);
        edje_object_file_set(o, icon_file, icon_group);
        edje_object_preload(o, EINA_FALSE);
        icon->edje = EINA_TRUE;
     }
   else if (icon_thumb)
     { // thumbnail file
        icon->o_icon = o = efm_icon_add(o);
        evas_object_smart_callback_add(o, "resized", _cb_icon_resized, icon);
        evas_object_smart_callback_add(o, "loaded", _cb_icon_loaded, icon);
        efm_icon_thumb_set(o, icon_thumb);
     }
   _icon_text_update(icon);
   edje_object_part_swallow(icon->o_base, "e.swallow.icon", o);
   evas_object_show(icon->o_icon);
   if (clip_set)
     {
        evas_object_smart_member_add(icon->o_base, sd->o_smart);
        evas_object_clip_set(icon->o_base, sd->o_clip);
     }

   if (icon->selected)
     edje_object_signal_emit(icon->o_base, "e,state,selected", "e");
   else
     edje_object_signal_emit(icon->o_base, "e,state,unselected", "e");
   if (icon->info.broken)
     edje_object_signal_emit(icon->o_base, "e,state,broken", "e");
   else if (icon->info.link)
     edje_object_signal_emit(icon->o_base, "e,state,link", "e");
   if (icon->info.special)
     edje_object_signal_emit(icon->o_base, "e,type,special", "e");
   if (icon->info.thumb)
     {
        if (efm_icon_mono_get(icon->o_icon))
          {
             edje_object_signal_emit(icon->o_base, "e,type,thumb,mono", "e");
             if (edje_object_part_exists(icon->o_base, "e.swallow.icon_mono"))
               edje_object_part_swallow(icon->o_base, "e.swallow.icon_mono", o);
             evas_object_show(icon->o_icon);
          }
        else
          edje_object_signal_emit(icon->o_base, "e,type,thumb", "e");
     }

   evas_object_event_callback_add(icon->o_base, EVAS_CALLBACK_MOUSE_DOWN,
                                  _cb_icon_mouse_down, icon);
   evas_object_event_callback_add(icon->o_base, EVAS_CALLBACK_MOUSE_UP,
                                  _cb_icon_mouse_up, icon);
   evas_object_event_callback_add(icon->o_base, EVAS_CALLBACK_MOUSE_MOVE,
                                  _cb_icon_mouse_move, icon);

   evas_object_show(icon->o_base);
   icon->realized = EINA_TRUE;
   if (clip_set)
     {
        evas_object_raise(sd->o_focus);
        evas_object_raise(sd->o_sel);
     }
   if (icon->renaming)
     {
        icon->sd->rename_icon = NULL;
        _icon_rename_begin(icon);
     }
}

static void
_icon_free(Icon *icon)
{ // free an icon - not needed anymore
   if (icon->sd)
     {
        if (icon->sd->last_selected == icon) icon->sd->last_selected = NULL;
        if (icon->sd->last_focused == icon) icon->sd->last_focused = NULL;
        if (icon->sd->over_icon == icon) _icon_over_off(icon);
        if (icon->sd->drop_over == icon) icon->sd->drop_over = NULL;
        if (icon->sd->rename_icon == icon) _icon_rename_end(icon);
     }
   if (icon->block)
     {
        if (icon->selected) icon->block->selected_num--;
        if (icon->realized) icon->block->realized_num--;
        icon->block->icons = eina_list_remove(icon->block->icons, icon);
     }
   if (icon->longpress_timer)
     {
        ecore_timer_del(icon->longpress_timer);
        icon->longpress_timer = NULL;
     }
   _icon_object_clear(icon);
   eina_stringshare_replace(&icon->info.file, NULL);
   eina_stringshare_replace(&icon->info.label, NULL);
   eina_stringshare_replace(&icon->info.label_selected, NULL);
   eina_stringshare_replace(&icon->info.label_clicked, NULL);
   eina_stringshare_replace(&icon->info.mime, NULL);
   eina_stringshare_replace(&icon->info.icon, NULL);
   eina_stringshare_replace(&icon->info.icon_selected, NULL);
   eina_stringshare_replace(&icon->info.icon_clicked, NULL);
   eina_stringshare_replace(&icon->info.mime_icon, NULL);
   eina_stringshare_replace(&icon->info.pre_lookup_icon, NULL);
   eina_stringshare_replace(&icon->info.thumb, NULL);
   cmd_free(icon->cmd);
   icon->cmd = NULL;
   free(icon);
}

static void
_block_free_final(Block *block)
{ // remove our block from storage
   block->icons = eina_list_free(block->icons);
   free(block);
}

static void
_block_free(Block *block)
{ // free a block when we're not done with it - not used when reblocking
   Eina_List *il;
   Icon *icon;

   if (block->realized_num > 0)
     { // we have some realized icons - so clear them
        EINA_LIST_FOREACH(block->icons, il, icon)
          {
             icon->realized = EINA_FALSE;
             icon->block = NULL;
             _icon_object_clear(icon);
          }
     }
   else
     {
        EINA_LIST_FOREACH(block->icons, il, icon)
          {
             icon->block = NULL;
          }
     }
   _block_free_final(block);
}

static void
_cb_reblock(void *data)
{  // re-do all our blocks of icons that we divide into to avoid looking at
   // batches of icons as a group/block when recvalculating when that whole
   // group is already known to not be visible as a whole, so skip it as
   // a while thus keeping the recalculations to a reasonably small set
   // instead of everything
   Smart_Data *sd = data;
   Eina_List *l;
   Icon *icon;
   Block *block;

   sd->reblock_job = NULL;
   // special - remove our blocks but leave icons realized as we rebuild
   // the block list next
   EINA_LIST_FREE(sd->blocks, block) _block_free_final(block);
   // walk all icons and figure out which blocks they belong to
   block = NULL;
   EINA_LIST_FOREACH(sd->icons, l, icon)
     {
        // if our block is full move onto making a new block
        if ((block) && (eina_list_count(block->icons) >= BLOCK_MAX))
          block = NULL;
        // we don't have a block to add this icon to - so make one
        if (!block)
          {
             block = calloc(1, sizeof(Block));
             if (!block) abort();
             // add the block to our block list
             sd->blocks = eina_list_append(sd->blocks, block);
             block->sd = sd;
          }
        // add icon to block icons
        block->icons = eina_list_append(block->icons, icon);
        icon->block = block;
        icon->block_list = eina_list_last(block->icons);
        // adjust block reslize num if icon is realized
        if (icon->realized) block->realized_num++;
        if (icon->selected) block->selected_num++;
     }
   // flag the obj to have had our blocks re-done so we also then re-calc
   // which blocks are visible or not and unrealize/realize icons as
   // needed etc.
   sd->reblocked = EINA_TRUE;
   evas_object_smart_changed(sd->o_smart);
}
