// the efm view - intended to be put in an elm scroller - though could
// work without and display a set of file icons in some view mode and
// allow them to update, sort, drag and drop, rename, delete, copy/paste,
// etc. etc.
// 
// maximum # of files in a dir that is sane: 10,000
// maximum number of files in a dir on a real system to be fast with: 3,000
#include "efm.h"
#include "efm_icon.h"
#include "cmd.h"
#include "sort.h"

// maximum number of icons in a block
#define BLOCK_MAX 64
#define SCROLL_SEL_TIMER 0.2
#define SCROLL_DND_TIMER 0.2
#define FOCUS_ANIM_TIME 0.2
#define DND_OVER_OPEN_TIMER 1.0
#define ICON_LONGPRESS_TIMER 1.0

#include "efm_structs.h"

int _log_dom = -1;

static Eina_List        *_pending_exe_dels = NULL;
static Evas_Smart       *_smart = NULL;
static Evas_Smart_Class  _sc = EVAS_SMART_CLASS_INIT_NULL;
static Evas_Smart_Class  _sc_parent = EVAS_SMART_CLASS_INIT_NULL;

#define ENTRY Smart_Data *sd = evas_object_smart_data_get(obj); if (!sd) return

#include "efm_private.h"
#include "efm_back_end.c"
#include "efm_util.c"
#include "efm_dnd.c"

static void _cb_canvas_resize(void *data, Evas *e, void *event_info EINA_UNUSED);
static void _reposition_detail_header_items(Smart_Data *sd);

static void
_cb_lost_selection(void *data, Elm_Sel_Type selection EINA_UNUSED)
{
   Smart_Data *sd = evas_object_smart_data_get(data);

   if (sd->cnp_have)
     {
        sd->cnp_have = EINA_FALSE;
        sd->cnp_cut = EINA_FALSE;
        printf("XXX: lost select\n");
     }
}

static void
_cnp_copy_files(Smart_Data *sd)
{
   Eina_Strbuf *strbuf;
   const char *str = NULL;

   strbuf = eina_strbuf_new();
   if (!strbuf) return;
   if (_selected_icons_uri_strbuf_append(sd, strbuf))
     {
        str = eina_strbuf_string_get(strbuf);
        if (str)
          {
             elm_cnp_selection_set(sd->o_scroller,
                                   ELM_SEL_TYPE_CLIPBOARD,
                                   ELM_SEL_FORMAT_URILIST,
                                   str, strlen(str));
             elm_cnp_selection_loss_callback_set(sd->o_scroller,
                                                 ELM_SEL_TYPE_CLIPBOARD,
                                                 _cb_lost_selection,
                                                 sd->o_smart);
          }
     }
   if (str) printf("XXX: COPY: [%s]\n", str);
   else printf("XXX: COPY: no str\n");
   eina_strbuf_free(strbuf);
}

static Eina_Bool
_cb_sel_get(void *data,
            Evas_Object *_obj EINA_UNUSED,
            Elm_Selection_Data *ev)
{
   Smart_Data *sd = evas_object_smart_data_get(data);

   if (!sd) return EINA_TRUE;
   printf("XXX: GET SEL %i\n", ev->format);
   if (ev->format & ELM_SEL_FORMAT_URILIST)
     {
        char **plist, **p, *esc, *tmp;

        tmp = malloc(ev->len + 1);
        if (tmp)
          {
             memcpy(tmp, ev->data, ev->len);
             tmp[ev->len] = 0;
             plist = eina_str_split(tmp, "\n", -1);
             for (p = plist; *p != NULL; ++p)
               {
                  if (**p)
                    {
                       esc = _escape_parse(*p);
                       if (!esc) continue;
                       printf("XXX: PASTE FILE: [%s]\n", esc);
                    }
               }
             free(*plist);
             free(plist);
             free(tmp);
          }
     }
   return EINA_TRUE;
}

static void
_cnp_paste_files(Smart_Data *sd)
{
   elm_cnp_selection_get(sd->o_scroller, ELM_SEL_TYPE_CLIPBOARD,
                         ELM_SEL_FORMAT_URILIST,
                         _cb_sel_get, sd->o_smart);
}

static void
_cb_key_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Smart_Data *sd = data;
   Evas_Event_Key_Down *ev = event_info;
   Eina_Bool handled = EINA_FALSE;

   printf("key: [%s]\n", ev->key);
   // these keys we use/steal for navigation, so want them for sure
   if      (!strcmp(ev->key, "Up"))    handled = _icon_focus_dir(sd, EFM_FOCUS_DIR_UP);
   else if (!strcmp(ev->key, "Down"))  handled = _icon_focus_dir(sd, EFM_FOCUS_DIR_DOWN);
   else if (!strcmp(ev->key, "Left"))  handled = _icon_focus_dir(sd, EFM_FOCUS_DIR_LEFT);
   else if (!strcmp(ev->key, "Right")) handled = _icon_focus_dir(sd, EFM_FOCUS_DIR_RIGHT);
   else if (!strcmp(ev->key, "Prior")) handled = _icon_focus_dir(sd, EFM_FOCUS_DIR_PGUP);
   else if (!strcmp(ev->key, "Next"))  handled = _icon_focus_dir(sd, EFM_FOCUS_DIR_PGDN);
   else if ((!strcmp(ev->key, "Return")) ||
            (!strcmp(ev->key, "KP_Enter")))
     {
        if (sd->last_focused)
          {
             Icon *icon = sd->last_focused;

             if (!icon->selected) _icon_select(icon);
             // XXX: handle "open" of all selected files/icons (double-click)
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "space"))
     {
        if (sd->last_focused)
          {
             Icon *icon = sd->last_focused;

             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);
               }
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "Escape"))
     {
        handled = _unselect_all(sd);
     }
// hmm - should we handle this?
//   else if (!strcmp(ev->key, "Backspace"))
//     {
//     }
   else if (!strcmp(ev->key, "Delete"))
     {
        // XXX: delete file
        handled = EINA_TRUE;
     }
   else if (!strcmp(ev->key, "Home"))
     {
        if (sd->icons)
          {
             sd->last_focused_before = sd->last_focused;
             sd->last_focused = sd->icons->data;
             _icon_focus(sd);
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "End"))
     {
        if (sd->icons)
          {
             sd->last_focused_before = sd->last_focused;
             sd->last_focused = eina_list_last(sd->icons)->data;
             _icon_focus(sd);
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "c"))
     {
        if (evas_key_modifier_is_set(ev->modifiers, "Control"))
          {
             sd->cnp_have = EINA_TRUE;
             sd->cnp_cut = EINA_FALSE;
             _cnp_copy_files(sd);
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "x"))
     {
        if (evas_key_modifier_is_set(ev->modifiers, "Control"))
          {
             sd->cnp_have = EINA_TRUE;
             sd->cnp_cut = EINA_TRUE;
             _cnp_copy_files(sd);
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "v"))
     {
        if (evas_key_modifier_is_set(ev->modifiers, "Control"))
          {
             _cnp_paste_files(sd);
             handled = EINA_TRUE;
          }
     }
   else if (!strcmp(ev->key, "Insert"))
     {
        if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
          {
             _cnp_paste_files(sd);
             handled = EINA_TRUE;
          }
     }
   // flag on hold if we use the  event - otherwise pass it on
   if (handled)
     {
        ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
        sd->key_control = EINA_TRUE;
     }
   printf("KEY: [%c] [%s]\n", handled ? '#' : ' ', ev->key);
}

static void
_cb_refocus(void *data)
{
   Smart_Data *sd = data;

   sd->refocus_job = NULL;
   if (sd->rename_icon) return;
   printf("REFOCUS\n");
   if (sd->focused)
     {
        printf("  focus widget\n");
        evas_object_focus_set(sd->o_clip, EINA_TRUE);
        if (sd->key_control) _icon_focus_show(sd);
     }
   else
     {
        printf("  unfocused widget\n");
        _icon_focus_hide(sd);
        // XXX: exit rename mode if on...
     }
}

static void
_cb_focus_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Smart_Data *sd = data;

   printf("FOCUS OUT CLIP\n");
   if (sd->refocus_job) ecore_job_del(sd->refocus_job);
   sd->refocus_job = ecore_job_add(_cb_refocus, sd);
}

static void
_cb_back_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Smart_Data *sd = data;
   Evas_Event_Mouse_Down *ev = event_info;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   // XXX: exit rename mode if on...
   elm_object_focus_set(sd->o_scroller, EINA_TRUE);
   if (ev->button == 1)
     {
        sd->back_down = EINA_TRUE;
        sd->back_down_x = ev->canvas.x;
        sd->back_down_y = ev->canvas.y;
        sd->sel_x1 = sd->back_down_x - sd->geom.x;
        sd->sel_y1 = sd->back_down_y - sd->geom.y;
     }
   if (sd->rename_icon) _icon_rename_end(sd->rename_icon);
   _icon_focus_hide(sd);
}

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

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (ev->button == 1)
     {
        // XXX: kill longpress timer
        sd->back_down = EINA_FALSE;
        _icon_focus_hide(sd);
        if (!sd->sel_show)
          {
             dx = ev->canvas.x - sd->back_down_x;
             dy = ev->canvas.y - sd->back_down_y;
             if (((dx * dx) + (dy * dy)) > (5 * 5)) dragged = EINA_TRUE;
             if (!dragged)
               {
                  if ((!evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
                      (!evas_key_modifier_is_set(ev->modifiers, "Control")))
                    _unselect_all(sd);
               }
          }
        else
          {
             Eina_List *bl, *il;
             Icon *icon;
             Block *block;
             Eina_Rectangle r = _efm_sel_rect_get(sd);

             EINA_LIST_FOREACH(sd->blocks, bl, block)
               {
                  if (eina_rectangles_intersect(&r, &(block->bounds)))
                    {
                       EINA_LIST_FOREACH(block->icons, il, icon)
                         {
                            if (eina_rectangles_intersect(&r, &(icon->geom)))
                              _icon_select(icon);
                         }
                    }
               }
             sd->sel_show = EINA_FALSE;
             evas_object_hide(sd->o_sel);
             if (sd->scroll_timer)
               {
                  ecore_timer_del(sd->scroll_timer);
                  sd->scroll_timer = NULL;
               }
          }
     }
   else if (ev->button == 3)
     { // right mouse click
        // XXX: handle ctxt menu for window
        printf("XXX: right mouse back\n");
     }
}

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

   if (sd->o_scroller)
     {
        Evas_Coord sx, sy, sw, sh;

        evas_object_geometry_get(sd->o_scroller, &sx, &sy, &sw, &sh);
        if ((sd->back_x < sx) || (sd->back_y < sy) ||
            (sd->back_x >= (sx +  sw)) || (sd->back_y >= (sy +  sh)))
          {
             elm_scroller_region_bring_in(sd->o_scroller,
                                          sd->sel_x2, sd->sel_y2, 1, 1);
          }
        else
          {
             sd->scroll_timer = NULL;
             return EINA_FALSE;
          }
     }
   return EINA_TRUE;
}

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

   if (!sd->back_down) return;
   // XXX: kill longpress timer
   dx = ev->cur.canvas.x - sd->back_down_x;
   dy = ev->cur.canvas.y - sd->back_down_y;
   if (((dx * dx) + (dy * dy)) > (5 * 5)) dragged = EINA_TRUE;
   if (!dragged) return;
   if ((!evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
       (!evas_key_modifier_is_set(ev->modifiers, "Control")))
     _unselect_all(sd);
   if (!sd->sel_show)
     {
        _icon_focus_hide(sd);
        sd->sel_show = EINA_TRUE;
        evas_object_show(sd->o_sel);
        evas_object_raise(sd->o_sel);
     }
   sd->back_x = ev->cur.canvas.x;
   sd->back_y = ev->cur.canvas.y;
   sd->sel_x2 = ev->cur.canvas.x - sd->geom.x;
   sd->sel_y2 = ev->cur.canvas.y - sd->geom.y;
   _efm_sel_position(sd);
   if (!sd->scroll_timer)
     sd->scroll_timer = ecore_timer_add
       (SCROLL_SEL_TIMER, _cb_sel_bounds_scroll_timer, sd);
}

static void
_reposition_detail_bars(Smart_Data *sd)
{
   int i;
   Evas_Coord x, w;

   for (i = 0; i < 6; i++)
     {
        evas_object_geometry_get(sd->o_list_detail_swallow[i],
                                 &x, NULL, &w, NULL);
        x -= sd->geom.x;
        elm_grid_pack(sd->o_overlay_grid, sd->o_list_detail[i],
                      x, 0, w, 100);
     }
   _reposition_detail_header_items(sd);
}

static void
_cb_overlay_detail_swallow_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   _reposition_detail_bars(data);
}

static void
_cb_overlay_detail_swallow_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   _reposition_detail_bars(data);
}

static void
_cb_overlay_grid_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;
   Evas_Coord w;

   evas_object_geometry_get(obj, NULL, NULL, &w, NULL);
   elm_grid_size_set(sd->o_overlay_grid, w, 100);
}

static void
_cb_overlay_detail_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *info)
{
   Smart_Data *sd = data;
   Evas_Event_Mouse_Down *ev = info;
   int i;

   if (ev->button == 1)
     {
        sd->detail_down = EINA_TRUE;
        sd->detail_down_x = ev->canvas.x;
        sd->detail_down_y = ev->canvas.y;
        for (i = 0; i < 6; i++)
          {
             if (obj == sd->o_list_detail[i])
               {
                  sd->detail_down_start_min_w =
                    sd->detail_min_w[i] * _scale_get(sd);
                  break;
               }
          }
     }
}

static void
_cb_overlay_detail_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info)
{
   Smart_Data *sd = data;
   Evas_Event_Mouse_Up *ev = info;

   if (ev->button == 1)
     {
        if (sd->detail_down)
          {
             sd->detail_down = EINA_FALSE;
          }
     }
}

static void
_cb_overlay_detail_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *info)
{
   Smart_Data *sd = data;
   Evas_Event_Mouse_Move *ev = info;
   Evas_Object *o;
   char buf[128];
   int i;

   if (sd->detail_down)
     {
        for (i = 0; i < 6; i++)
          {
             if (obj == sd->o_list_detail[i])
               {
                  o = sd->o_list_detail_swallow[i];
                  sd->detail_min_w[i] =
                    (sd->detail_down_start_min_w -
                     (ev->cur.canvas.x - sd->detail_down_x)) / _scale_get(sd);
                  if (sd->detail_min_w[i] < 0) sd->detail_min_w[i] = 0;
                  snprintf(buf, sizeof(buf), "e.swallow.detail%i", i + 1);
                  evas_object_size_hint_min_set(o, sd->detail_min_w[i] * _scale_get(sd), 0);
                  edje_object_part_swallow(sd->o_list_detailed_dummy, buf, o);
                  _reposition_detail_bars(sd);
                  _detail_realized_items_resize(sd);
                  break;
               }
          }
     }
}

static void
_add_overlay_objects(Smart_Data *sd)
{
   Evas_Object *o;
   Evas *e;
   const char *theme_edj_file, *grp;
   char buf[128];
   int i;

   e = evas_object_evas_get(sd->o_scroller);

   for (i = 0; i < 6; i++)
     {
        if (sd->o_list_detail_swallow[i])
          {
             evas_object_del(sd->o_list_detail_swallow[i]);
             sd->o_list_detail_swallow[i] = NULL;
          }
     }
   if (sd->o_overlay_info)
     {
        evas_object_del(sd->o_overlay_info);
        sd->o_overlay_info = NULL;
     }
   if (sd->o_overlay_grid)
     {
        evas_object_del(sd->o_overlay_grid);
        sd->o_overlay_grid = NULL;
     }
   if (sd->o_overlay_grid_fill)
     {
        evas_object_del(sd->o_overlay_grid_fill);
        sd->o_overlay_grid_fill = NULL;
     }
   for (i = 0; i < 6; i++)
     {
        sd->o_list_detail[i] = NULL;
     }

   sd->o_overlay_grid_fill = o = elm_grid_add(sd->o_scroller);
   elm_grid_size_set(o, 1, 1);
   elm_object_part_content_set(sd->o_scroller, "elm.swallow.overlay", o);
   evas_object_show(o);

   sd->o_overlay_grid = o = elm_grid_add(sd->o_scroller);
   evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE,
                                  _cb_overlay_grid_resize, sd);
   elm_grid_size_set(o, 100, 100);
   elm_grid_pack(sd->o_overlay_grid_fill, o, 0, 0, 1, 1);
   evas_object_show(o);

   sd->o_overlay_info = o = edje_object_add(e);
   grp = "e/fileman/default/overlay";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);
   elm_grid_pack(sd->o_overlay_grid_fill, o, 0, 0, 1, 1);
   evas_object_show(o);

   for (i = 0; i < 6; i++)
     {
        sd->o_list_detail[i] = o = edje_object_add(e);
        grp = "e/fileman/default/detail-sizer";
        theme_edj_file = elm_theme_group_path_find(NULL, grp);
        edje_object_file_set(o, theme_edj_file, grp);
        elm_grid_pack(sd->o_overlay_grid, o, 100 - i - 10, 0, 1, 100);
        if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
          evas_object_show(o);
        evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
                                       _cb_overlay_detail_mouse_down, sd);
        evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP,
                                       _cb_overlay_detail_mouse_up, sd);
        evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE,
                                       _cb_overlay_detail_mouse_move, sd);

        sd->o_list_detail_swallow[i] = o = evas_object_rectangle_add(e);
        snprintf(buf, sizeof(buf), "e.swallow.detail%i", i + 1);
        evas_object_size_hint_min_set(o, sd->detail_min_w[i] * _scale_get(sd), 0);
        evas_object_event_callback_add(o, EVAS_CALLBACK_MOVE,
                                       _cb_overlay_detail_swallow_move, sd);
        evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE,
                                       _cb_overlay_detail_swallow_resize, sd);
        edje_object_part_swallow(sd->o_list_detailed_dummy, buf, o);
        evas_object_show(o);
     }
}

// gui code
static void
_smart_add(Evas_Object *obj)
{ // create a new efm view
   Smart_Data *sd;
   Evas_Object *o;
   Evas *e;
   const char *theme_edj_file, *grp;

   if (_log_dom < 0)
     _log_dom = eina_log_domain_register("efm", EINA_COLOR_WHITE);
   sd = calloc(1, sizeof(Smart_Data));
   if (!sd) return;
   evas_object_smart_data_set(obj, sd);

   _sc_parent.add(obj);

   e = evas_object_evas_get(obj);

   evas_event_callback_add(e, EVAS_CALLBACK_CANVAS_VIEWPORT_RESIZE,
                           _cb_canvas_resize, sd);

   sd->config.view_mode = EFM_VIEW_MODE_ICONS;
   sd->config.sort_mode = EFM_SORT_MODE_NAME |
                          EFM_SORT_MODE_LABEL_NOT_PATH |
                          EFM_SORT_MODE_DIRS_FIRST;
   sd->config.icon_size = 40;
   sd->detail_min_w[0] =  50;
   sd->detail_min_w[1] = 120;
   sd->detail_min_w[2] = 130;
   sd->detail_min_w[3] =  60;
   sd->detail_min_w[4] =  60;
   sd->detail_min_w[5] = 120;

   evas_object_size_hint_min_set(obj, 1, 1);

   sd->o_smart = obj;

   sd->o_clip = o = evas_object_rectangle_add(e);
   evas_object_smart_member_add(o, obj);
   evas_object_color_set(o, 255, 255, 255, 255);
   evas_object_show(o);

   sd->o_back = o = evas_object_rectangle_add(e);
   evas_object_color_set(o, 0, 0, 0, 0);
   evas_object_smart_member_add(o, obj);
   evas_object_clip_set(o, sd->o_clip);
   evas_object_show(o);
   evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
                                  _cb_back_mouse_down, sd);
   evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP,
                                  _cb_back_mouse_up, sd);
   evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE,
                                  _cb_back_mouse_move, sd);

   sd->o_sel = o = edje_object_add(e);
   evas_object_pass_events_set(o, EINA_TRUE);
   evas_object_smart_member_add(o, obj);
   evas_object_clip_set(o, sd->o_clip);
   grp = "e/fileman/default/rubberband";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);

   sd->o_focus = o = edje_object_add(e);
   evas_object_pass_events_set(o, EINA_TRUE);
   evas_object_smart_member_add(o, obj);
   evas_object_clip_set(o, sd->o_clip);
   grp = "elm/focus_highlight/top/default";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);

   sd->o_over = o = edje_object_add(e);
   evas_object_pass_events_set(o, EINA_TRUE);
   evas_object_smart_member_add(o, obj);
   evas_object_clip_set(o, sd->o_clip);
   grp = "e/fileman/default/list/drop_in";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);

   sd->handler_exe_del  = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
                                                  _cb_exe_del, sd);
   sd->handler_exe_data = ecore_event_handler_add(ECORE_EXE_EVENT_DATA,
                                                  _cb_exe_data, sd);

   sd->thread_data = calloc(1, sizeof(Smart_Data_Thread));
   sd->thread_data->sd = sd;
   sd->thread_data->thq = eina_thread_queue_new();
   sd->thread = ecore_thread_feedback_run(_cb_thread_main, _cb_thread_notify,
                                          _cb_thread_done, _cb_thread_done,
                                          sd->thread_data, EINA_TRUE);
   evas_object_event_callback_priority_add
     (sd->o_clip, EVAS_CALLBACK_KEY_DOWN, -100, _cb_key_down, sd);
   evas_object_event_callback_add
     (sd->o_clip, EVAS_CALLBACK_FOCUS_OUT, _cb_focus_out, sd);
}

static void
_smart_del(Evas_Object *obj)
{ // delete/free efm view
   Block *block;
   Icon *icon;
   Evas *e;
   int i;
   ENTRY;

   e = evas_object_evas_get(obj);
   evas_event_callback_del_full(e, EVAS_CALLBACK_CANVAS_VIEWPORT_RESIZE,
                                _cb_canvas_resize, sd);
   if (sd->size_bars_update_job)
     {
        ecore_job_del(sd->size_bars_update_job);
        sd->size_bars_update_job = NULL;
     }
   if (sd->size_max_update_job)
     {
        ecore_job_del(sd->size_max_update_job);
        sd->size_max_update_job = NULL;
     }
   if (sd->drag_icon)
     {
        if (sd->o_scroller) elm_drag_cancel(sd->o_scroller);
        sd->drag_icon->sd = NULL;
        sd->drag_icon = NULL;
     }
   if (sd->refocus_job)
     {
        ecore_job_del(sd->refocus_job);
        sd->refocus_job = NULL;
     }
   if (sd->thread)
     {
        if (sd->thread_data) sd->thread_data->sd = NULL;
        ecore_thread_cancel(sd->thread);
        sd->thread = NULL;
     }
   eina_stringshare_replace(&(sd->path), NULL);
   if (sd->exe_open)
     {
        Pending_Exe_Del *pend = calloc(1, sizeof(Pending_Exe_Del));

        if (pend)
          {
             pend->exe = sd->exe_open;
             pend->timer = ecore_timer_add(5.0, _cb_exe_pending_timer, pend);
             _pending_exe_dels = eina_list_append(_pending_exe_dels, pend);
          }
        ecore_exe_interrupt(sd->exe_open);
        sd->exe_open = NULL;
     }
   if (sd->handler_exe_del)
     {
        ecore_event_handler_del(sd->handler_exe_del);
        sd->handler_exe_del = NULL;
     }
   if (sd->handler_exe_data)
     {
        ecore_event_handler_del(sd->handler_exe_data);
        sd->handler_exe_data = NULL;
     }
   EINA_LIST_FREE(sd->blocks, block) _block_free(block);
   EINA_LIST_FREE(sd->icons, icon) _icon_free(icon);
   for (i = 0; i < 6; i++)
     {
        if (sd->o_list_detail_swallow[i])
          {
             evas_object_del(sd->o_list_detail_swallow[i]);
             sd->o_list_detail_swallow[i] = NULL;
          }
     }
   if (sd->o_detail_header)
     {
        evas_object_del(sd->o_detail_header);
        sd->o_detail_header = NULL;
     }
   if (sd->o_clip)
     {
        evas_object_del(sd->o_clip);
        sd->o_clip = NULL;
     }
   if (sd->o_focus)
     {
        evas_object_del(sd->o_focus);
        sd->o_focus = NULL;
     }
   if (sd->o_back)
     {
        evas_object_del(sd->o_back);
        sd->o_back = NULL;
     }
   if (sd->o_sel)
     {
        evas_object_del(sd->o_sel);
        sd->o_sel = NULL;
     }
   if (sd->o_over)
     {
        evas_object_del(sd->o_over);
        sd->o_over = NULL;
     }
   if (sd->o_list_detailed_dummy)
     {
        evas_object_del(sd->o_list_detailed_dummy);
        sd->o_list_detailed_dummy = NULL;
     }
   if (sd->o_overlay_grid)
     {
        evas_object_del(sd->o_overlay_grid);
        sd->o_overlay_grid = NULL;
     }
   if (sd->o_overlay_grid_fill)
     {
        evas_object_del(sd->o_overlay_grid_fill);
        sd->o_overlay_grid_fill = NULL;
     }
   if (sd->reblock_job)
     {
        ecore_job_del(sd->reblock_job);
        sd->reblock_job = NULL;
     }
   if (sd->focus_animator)
     {
        ecore_animator_del(sd->focus_animator);
        sd->focus_animator = NULL;
     }
   if (sd->scroll_timer)
     {
        ecore_timer_del(sd->scroll_timer);
        sd->scroll_timer = NULL;
     }
   if (sd->dnd_over_open_timer)
     {
        ecore_timer_del(sd->dnd_over_open_timer);
        sd->dnd_over_open_timer = NULL;
     }
   if (sd->dnd_scroll_timer)
     {
        ecore_timer_del(sd->dnd_scroll_timer);
        sd->dnd_scroll_timer = NULL;
     }

   // XXX: anything special with scroller?
   sd->o_scroller = NULL;
   sd->o_smart = NULL;

   _sc_parent.del(obj);
   evas_object_smart_data_set(obj, NULL);
}

static void
_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
{ // efm view object moved
   ENTRY;

   if ((sd->geom.x == x) && (sd->geom.y == y)) return;
   sd->geom.x = x;
   sd->geom.y = y;
   evas_object_smart_changed(obj);
   evas_object_move(sd->o_clip, x, y);
   evas_object_move(sd->o_list_detailed_dummy, x, y);
   if (sd->sel_show)
     {
        sd->sel_x2 = sd->back_x - sd->geom.x;
        sd->sel_y2 = sd->back_y - sd->geom.y;
        _efm_sel_position(sd);
        if (!sd->scroll_timer)
          sd->scroll_timer = ecore_timer_add
          (SCROLL_SEL_TIMER, _cb_sel_bounds_scroll_timer, sd);
     }
}

static void
_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
{ // efm veiw object resized
   Eina_Bool width_change = EINA_FALSE;
   ENTRY;

   if ((sd->geom.w == w) && (sd->geom.h == h)) return;
   if (w != sd->geom.w) width_change = EINA_TRUE;
   sd->geom.w = w;
   sd->geom.h = h;
   evas_object_smart_changed(obj);
   evas_object_resize(sd->o_clip, w, h);
   // if width changed we have to re-flow (relayout) icons
   if (width_change) sd->relayout = EINA_TRUE;
   if (width_change)
     {
        evas_object_resize(sd->o_list_detailed_dummy,
                           w, sd->list_detailed_min_h);
        printf("RSZ: %p %i\n", sd->o_list_detailed_dummy, w);
     }
}

static void
_cb_canvas_resize(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED)
{ // canvas resized and so handle so new visible icons are visible etc.
   Smart_Data *sd = data;
   Evas_Object *obj = sd->o_smart;

   evas_object_smart_changed(obj);
}

static void
_relayout_icons(Smart_Data *sd)
{ // wall all blocks and icons in blocks and assign them geometry
   Eina_List *bl, *il;
   Block *block;
   Icon *icon;
   Evas_Coord x, y, minw, minh;

   // this is a row by row top-left to bottom-right layout
   x = y = 0;
   minw = minh = 0;
   EINA_LIST_FOREACH(sd->blocks, bl, block)
     {
        EINA_LIST_FOREACH(block->icons, il, icon)
          { // icon is at urrent x,y
             icon->geom.x = x;
             icon->geom.y = y;
             icon->geom.w = sd->icon_min_w;
             icon->geom.h = sd->icon_min_h;
             x += icon->geom.w;
             if ((x + icon->geom.w) > sd->geom.w)
               { // if next icon over end of row, start a new row
                  x = 0;
                  y += icon->geom.h;
               }
             if (block->icons == il)
               { // first icon in block - block bounds == icon geom
                  block->bounds.x = icon->geom.x;
                  block->bounds.y = icon->geom.y;
                  block->bounds.w = icon->geom.w;
                  block->bounds.h = icon->geom.h;
               }
             else
               { // expand block bounds based on icon geom if it needs to
                  if (icon->geom.x < block->bounds.x)
                    block->bounds.x = icon->geom.x;
                  if (icon->geom.y < block->bounds.y)
                    block->bounds.y = icon->geom.y;
                  if ((icon->geom.x + icon->geom.w) >
                      (block->bounds.x + block->bounds.w))
                    block->bounds.w = icon->geom.x +
                    icon->geom.w - block->bounds.x;
                  if ((icon->geom.y + icon->geom.h) >
                      (block->bounds.y + block->bounds.h))
                    block->bounds.h = icon->geom.y +
                    icon->geom.h - block->bounds.y;
               }
          }
        // adjust view minw/h if block expands it
        if ((block->bounds.x + block->bounds.w) > minw)
          minw = block->bounds.x + block->bounds.w;
        if ((block->bounds.y + block->bounds.h) > minh)
          minh = block->bounds.y + block->bounds.h;
     }
   // set min size for scroller to know content size
   evas_object_size_hint_min_set(sd->o_smart, sd->icon_min_w, minh);
}

static void
_relayout_list(Smart_Data *sd)
{ // wall all blocks and icons in blocks and assign them geometry
   Eina_List *bl, *il;
   Block *block;
   Icon *icon;
   Evas_Coord x, y, minw, minh;

   // this is a row by row top-left to bottom-right layout
   x = y = 0;
   minw = minh = 0;
   EINA_LIST_FOREACH(sd->blocks, bl, block)
     {
        EINA_LIST_FOREACH(block->icons, il, icon)
          { // icon is at urrent x,y
             icon->geom.x = x;
             icon->geom.y = y;
             icon->geom.w = sd->geom.w;
             if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
               icon->geom.h = sd->list_detailed_min_h;
             else
               icon->geom.h = sd->list_min_h;
             y += icon->geom.h;
             if (block->icons == il)
               { // first icon in block - block bounds == icon geom
                  block->bounds.x = icon->geom.x;
                  block->bounds.y = icon->geom.y;
                  block->bounds.w = icon->geom.w;
                  block->bounds.h = icon->geom.h;
               }
             else
               { // expand block bounds based on icon geom if it needs to
                  if (icon->geom.x < block->bounds.x)
                    block->bounds.x = icon->geom.x;
                  if (icon->geom.y < block->bounds.y)
                    block->bounds.y = icon->geom.y;
                  if ((icon->geom.x + icon->geom.w) >
                      (block->bounds.x + block->bounds.w))
                    block->bounds.w = icon->geom.x +
                    icon->geom.w - block->bounds.x;
                  if ((icon->geom.y + icon->geom.h) >
                      (block->bounds.y + block->bounds.h))
                    block->bounds.h = icon->geom.y +
                    icon->geom.h - block->bounds.y;
               }
          }
        // adjust view minw/h if block expands it
        if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
          {
             if ((sd->list_detailed_min_w) > minw)
               minw = block->bounds.x + sd->list_detailed_min_w;
          }
        else
          {
             if ((sd->list_min_w) > minw)
               minw = block->bounds.x + sd->list_min_w;
          }
        if ((block->bounds.y + block->bounds.h) > minh)
          minh = block->bounds.y + block->bounds.h;
     }
   // set min size for scroller to know content size
   evas_object_size_hint_min_set(sd->o_smart, minw, minh);
}

static void
_relayout(Smart_Data *sd)
{ // wall all blocks and icons in blocks and assign them geometry
//   printf("XXXXX relayout...\n");
   // XXX: add other layouts like:
   // XXX: fixed position per icon layout (just calc block bounds)
   // XXX: column layout (top-left down then next col along)
   // XXX: list layout
   if (sd->config.view_mode == EFM_VIEW_MODE_ICONS)
     _relayout_icons(sd);
   else if (sd->config.view_mode == EFM_VIEW_MODE_LIST)
     _relayout_list(sd);
   else if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
     _relayout_list(sd);
}

static void
_recalc(Smart_Data *sd)
{ // recalc position of icons and which may or may not be visible now
   Eina_List *bl, *il;
   Block *block;
   Icon *icon;
   Eina_Rectangle viewport, rect;
   Evas *e;
   int num = 0;
   const char *theme_edj_file;

   e = evas_object_evas_get(sd->o_smart);
   evas_output_viewport_get(e,
                            &(viewport.x), &(viewport.y),
                            &(viewport.w), &(viewport.h));
   theme_edj_file = elm_theme_group_path_find
     (NULL, "e/fileman/default/icon/fixed");
   EINA_LIST_FOREACH(sd->blocks, bl, block)
     { // walk all our blocks and per clock ... is that block visible
        rect = block->bounds;
        rect.x += sd->geom.x;
        rect.y += sd->geom.y;
        if (eina_rectangles_intersect(&rect, &viewport))
          { // if the block intersects the viewport, then look into it
             // check all the icon to see if each is visible
             EINA_LIST_FOREACH(block->icons, il, icon)
               { // check each icon...
                  rect = icon->geom;
                  rect.x += sd->geom.x;
                  rect.y += sd->geom.y;
                  // if the icon is within the viewport ...
                  if (eina_rectangles_intersect(&rect, &viewport))
                    { // we are within vierwport - so could be visible
                       if (icon->changed)
                         { // icon changed - let's redo it, so del the old
                            _icon_object_clear(icon);
                         }
                       icon->changed = EINA_FALSE;
                       // no icon yet ... it needs to be created
                       if (!icon->o_base)
                         { // no object - let's create one
                           // about to realize if it hasn't been
                            if (!icon->realized) icon->block->realized_num++;
                            _icon_object_add(icon, sd, e, theme_edj_file,
                                             EINA_TRUE, num);
                         }
                       // position the icon object where it should be
                       evas_object_geometry_set(icon->o_base,
                                                rect.x, rect.y,
                                                rect.w, rect.h);
                       if (icon->over)
                         evas_object_geometry_set(icon->sd->o_over,
                                                  rect.x, rect.y,
                                                  rect.w, rect.h);
                    }
                  else
                    { // icon not in viewport
                       if (icon->realized)
                         { // it's realized so unrealize it
                            icon->realized = EINA_FALSE;
                            icon->block->realized_num--;
                            _icon_object_clear(icon);
                         }
                    }
                  num++;
               }
          }
        else if (block->realized_num > 0)
          { // block is realized but NOT in the viewport (not visible)
             EINA_LIST_FOREACH(block->icons, il, icon)
               { // go through each icon now
                  if (icon->realized)
                    { // if the icon was realized then unrealize it
                       if (icon->realized) block->realized_num--;
                       icon->realized = EINA_FALSE;
                       _icon_object_clear(icon);
                    }
               }
             num += eina_list_count(block->icons);
          }
        else
          num += eina_list_count(block->icons);
     }

   evas_object_geometry_set(sd->o_back,
                            sd->geom.x, sd->geom.y, sd->geom.w, sd->geom.h);
   _efm_focus_position(sd);
   _efm_sel_position(sd);
}

static void
_reset(Smart_Data *sd)
{
   Eina_Strbuf *buf;
   Block *block;
   Icon *icon;
   static char envbuf[4096];

   // clear out stuff there so we can start listing again...
   sd->file_max = 0;
   EINA_LIST_FREE(sd->blocks, block) _block_free(block);
   EINA_LIST_FREE(sd->icons, icon) _icon_free(icon);
   if (sd->reblock_job)
     {
        ecore_job_del(sd->reblock_job);
        sd->reblock_job = NULL;
     }
   if (sd->exe_open)
     {
        Pending_Exe_Del *pend = calloc(1, sizeof(Pending_Exe_Del));

        if (pend)
          {
             pend->exe = sd->exe_open;
             pend->timer = ecore_timer_add(5.0, _cb_exe_pending_timer, pend);
             _pending_exe_dels = eina_list_append(_pending_exe_dels, pend);
          }
        ecore_exe_interrupt(sd->exe_open);
        sd->exe_open = NULL;
     }
   if (sd->thread)
     {
        if (sd->thread_data) sd->thread_data->sd = NULL;
        ecore_thread_cancel(sd->thread);
        sd->thread = NULL;
     }
   // now start up opening up a dir again
   sd->thread_data = calloc(1, sizeof(Smart_Data_Thread));
   sd->thread_data->sd = sd;
   sd->thread_data->thq = eina_thread_queue_new();
   sd->thread = ecore_thread_feedback_run(_cb_thread_main, _cb_thread_notify,
                                          _cb_thread_done, _cb_thread_done,
                                          sd->thread_data, EINA_TRUE);
   buf = eina_strbuf_new();
   eina_strbuf_append(buf, elm_app_lib_dir_get());
   eina_strbuf_append(buf, "/efm/backends/");
   // XXX: choose backend other than default
   eina_strbuf_append(buf, "default");
   snprintf(envbuf, sizeof(envbuf),
            "EFM_BACKEND_DIR=%s", eina_strbuf_string_get(buf));
   putenv(envbuf);
   eina_strbuf_append(buf, "/open");
   sd->exe_open = ecore_exe_pipe_run(eina_strbuf_string_get(buf),
                                     ECORE_EXE_NOT_LEADER |
                                     ECORE_EXE_TERM_WITH_PARENT |
                                     ECORE_EXE_PIPE_READ |
                                     ECORE_EXE_PIPE_READ_LINE_BUFFERED |
                                     ECORE_EXE_PIPE_WRITE, NULL);
   eina_strbuf_free(buf);

   if (sd->path)
     {
        buf = cmd_strbuf_new("dir-set");
        cmd_strbuf_append(buf, "path", sd->path);
        cmd_strbuf_exe_consume(buf, sd->exe_open);
     }
}

static void
_cb_scroller_focus(void *data, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;

   printf("FOCUS\n");
   sd->focused = EINA_TRUE;
   evas_object_focus_set(sd->o_clip, EINA_TRUE);
}

static void
_cb_scroller_unfocus(void *data, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;

   printf("UNFOCUS\n");
   sd->focused = EINA_FALSE;
}

static void
_smart_calculate(Evas_Object *obj)
{
   ENTRY;

   if ((sd->reblocked) || (sd->relayout))
     {  // if we red-d the list of blocks (added/removed files) or need to
        // re-layout due to q width or otheer geometry change
        _relayout(sd);
        sd->reblocked = EINA_FALSE;
        sd->relayout = EINA_FALSE;
     }
   // moved/resize icons and/or realize/unrealize (create or delete objects
   // that we need to awe only keep a limited set of ivisible/active icons
   // that we need)
   _recalc(sd);
}

//////////////////////////////////////////////////////////////////////////////

Evas_Object *efm_add(Evas_Object *parent)
{
   if (!_smart)
     {
        evas_object_smart_clipped_smart_set(&_sc_parent);
        _sc           = _sc_parent;
        _sc.name      = "efm";
        _sc.version   = EVAS_SMART_CLASS_VERSION;
        _sc.add       = _smart_add;
        _sc.del       = _smart_del;
        _sc.resize    = _smart_resize;
        _sc.move      = _smart_move;
        _sc.calculate = _smart_calculate;
     };
   if (!_smart) _smart = evas_smart_class_new(&_sc);
   return evas_object_smart_add(evas_object_evas_get(parent), _smart);
}

static void
_reposition_detail_header_items(Smart_Data *sd)
{
   int i, vw, vh;
   Evas_Coord det_x, det_w, x, w, xp;

   if (!sd->o_detail_header) return;
   evas_object_geometry_get(sd->o_overlay_grid, &det_x, NULL, &det_w, NULL);
   elm_grid_size_get(sd->o_overlay_grid, &vw, &vh);
   xp = 0;
   elm_grid_size_set(sd->o_detail_header, vw, 100);
   for (i = 0; i < 7; i++)
     {
        if (i < 6)
          {
             elm_grid_pack_get(sd->o_list_detail[i],
                               &x, NULL, NULL, NULL);
             w = (x - xp);
          }
        else
          {
             w = vw - xp;
             x = xp + w;
          }
        elm_grid_pack(sd->o_detail_header, sd->o_detail_header_item[i],
                      xp, 0, w, 100);
        xp = x;
     }
}

static void
_cb_detail_header_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;

   _reposition_detail_header_items(sd);
}

static void
_cb_detail_header_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;
   int i;

   sd->o_detail_header = NULL;
   for (i = 0; i < 7; i++) sd->o_detail_header_item[i] = NULL;
}

static void
_cb_detail_header_item_hint(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;
   Evas_Coord h, minh = 0;
   int i;

   if (!sd->o_detail_header) return;
   for (i = 0; i < 7; i++)
     {
        if (obj == sd->o_detail_header_item[i])
          {
             evas_object_size_hint_min_get(obj, NULL, &h);
             sd->detail_header_min_h[i] = h;
             break;
          }
     }
   for (i = 0; i < 7; i++)
     {
        h = sd->detail_header_min_h[i];
        if (h > minh) minh = h;
     }
   evas_object_size_hint_min_set(sd->o_detail_header, 0, minh);
}

static void
_cb_detail_radio_changed(void *data, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
   Smart_Data *sd = data;
   Efm_Sort_Mode sort_mode;

   sort_mode = elm_radio_value_get(sd->o_detail_header_item[0]);
   efm_path_sort_mode_set(sd->o_smart,
                          sort_mode |
                          (sd->config.sort_mode & EFM_SORT_MODE_FLAGS));
   evas_object_smart_callback_call(sd->o_smart, "sort_mode", NULL);
}

static void
_detail_setup(Smart_Data *sd)
{
   Evas_Object *o, *o_grid, *o_radio_group;

   sd->o_detail_header = o_grid = o = elm_grid_add(sd->o_scroller);
   evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE,
                                  _cb_detail_header_resize, sd);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL,
                                  _cb_detail_header_del, sd);

   o_radio_group = o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[0] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_NAME);
   elm_grid_pack(o_grid, o, 0, 0, 40, 100);
   elm_object_text_set(o, "Name");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[1] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_SIZE);
   elm_grid_pack(o_grid, o, 40, 0, 10, 100);
   elm_radio_group_add(o, o_radio_group);
   elm_object_text_set(o, "Size");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[2] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_DATE);
   elm_grid_pack(o_grid, o, 50, 0, 10, 100);
   elm_radio_group_add(o, o_radio_group);
   elm_object_text_set(o, "Date");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[3] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_MIME);
   elm_grid_pack(o_grid, o, 60, 0, 10, 100);
   elm_radio_group_add(o, o_radio_group);
   elm_object_text_set(o, "Type");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[4] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_USER);
   elm_grid_pack(o_grid, o, 70, 0, 10, 100);
   elm_radio_group_add(o, o_radio_group);
   elm_object_text_set(o, "User");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[5] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_GROUP);
   elm_grid_pack(o_grid, o, 80, 0, 10, 100);
   elm_radio_group_add(o, o_radio_group);
   elm_object_text_set(o, "Group");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   o = elm_radio_add(sd->o_detail_header);
   sd->o_detail_header_item[6] = o;
   evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                  _cb_detail_header_item_hint, sd);
   elm_object_style_set(o, "sort_header");
   elm_radio_state_value_set(o, EFM_SORT_MODE_PERMISSIONS);
   elm_grid_pack(o_grid, o, 90, 0, 10, 100);
   elm_radio_group_add(o, o_radio_group);
   elm_object_text_set(o, "Permissions");
   evas_object_smart_callback_add(o, "changed", _cb_detail_radio_changed, sd);
   evas_object_show(o);

   elm_radio_value_set(o_radio_group, sd->config.sort_mode & EFM_SORT_MODE_MASK);
}

////////
void
efm_scroller_set(Evas_Object *obj, Evas_Object *scroller)
{
   Evas_Object *o, *o2;
   const char *theme_edj_file, *grp;
   Evas *e;
   ENTRY;

   // XXX: do any necessary re-inits ? should not be needed as scroller should
   // XXX: remove scroller drop target...
   // never change once set up at start
   sd->o_scroller = scroller;

   e = evas_object_evas_get(obj);

   o = edje_object_add(e);
   grp = "e/fileman/default/icon/fixed";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);
   o2 = evas_object_rectangle_add(e);
   evas_object_size_hint_min_set(o2,
                                 sd->config.icon_size * _scale_get(sd),
                                 sd->config.icon_size * _scale_get(sd));
   edje_object_part_swallow(o, "e.swallow.icon", o2);
   edje_object_size_min_calc(o, &(sd->icon_min_w), &(sd->icon_min_h));
   evas_object_del(o2);
   evas_object_del(o);

   o = edje_object_add(e);
   grp = "e/fileman/default/list/fixed";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);
   edje_object_size_min_calc(o, &(sd->list_min_w), &(sd->list_min_h));
   evas_object_del(o);

   sd->o_list_detailed_dummy = o = edje_object_add(e);
   evas_object_smart_member_add(o, obj);
   grp = "e/fileman/default/list/detailed";
   theme_edj_file = elm_theme_group_path_find(NULL, grp);
   edje_object_file_set(o, theme_edj_file, grp);
   edje_object_size_min_calc(o, &(sd->list_detailed_min_w), &(sd->list_detailed_min_h));
   evas_object_color_set(o, 0, 0, 0, 0);
   evas_object_pass_events_set(o, EINA_TRUE);
   evas_object_show(o);

   evas_object_smart_callback_add(sd->o_scroller, "focused",
                                  _cb_scroller_focus, sd);
   evas_object_smart_callback_add(sd->o_scroller, "unfocused",
                                  _cb_scroller_unfocus, sd);
   _drop_init(sd);
   _add_overlay_objects(sd);
}

Evas_Object *
efm_scroller_get(Evas_Object *obj)
{
   ENTRY NULL;

   return sd->o_scroller;
}

Evas_Object *
efm_detail_header_get(Evas_Object *obj)
{
   ENTRY NULL;

   if (!sd->o_detail_header) _detail_setup(sd);
   return sd->o_detail_header;
}

////////
void
efm_path_set(Evas_Object *obj, const char *path)
{
   ENTRY;

   if ((sd->path) && (path) && (!strcmp(sd->path, path))) return;
   eina_stringshare_replace(&(sd->path), path);
   _reset(sd);
}

const char *
efm_path_get(Evas_Object *obj)
{
   ENTRY NULL;

   return sd->path;
}

////////
void
efm_path_view_mode_set(Evas_Object *obj, Efm_View_Mode mode)
{
   int i;
   ENTRY;

   if (sd->config.view_mode == mode) return;
   sd->config.view_mode = mode;
   for (i = 0; i < 6; i++)
     {
        if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
          evas_object_show(sd->o_list_detail[i]);
        else
          evas_object_hide(sd->o_list_detail[i]);
     }
   _reset(sd);
}

Efm_View_Mode
efm_path_view_mode_get(Evas_Object *obj)
{
   ENTRY EFM_VIEW_MODE_ICONS;

   return sd->config.view_mode;
}

void
efm_path_sort_mode_set(Evas_Object *obj, Efm_Sort_Mode mode)
{
   ENTRY;

   if (sd->config.sort_mode == mode) return;
   sd->config.sort_mode = mode;
   _reset(sd);
}

Efm_Sort_Mode
efm_path_sort_mode_get(Evas_Object *obj)
{
   ENTRY EFM_SORT_MODE_NAME;

   return sd->config.sort_mode;
}

Evas_Coord
efm_column_min_get(Evas_Object *obj, int col)
{
   ENTRY 0;

   if (col < 0) return 0;
   else if (col >= 6) return 0;
   return sd->detail_min_w[col];
}
