#include "e.h"
#include "e_fm_device.h"
#include "e_fm_op.h"

#define OVERCLIP          128
#define ICON_BOTTOM_SPACE 100

/* in order to check files (ie: extensions) use simpler and faster
 * strcasecmp version that instead of checking case for each
 * character, check just the first and if it's upper, assume
 * everything is uppercase, otherwise assume everything is lowercase.
 */
//#define E_FM2_SIMPLE_STRCASE_FILES 1

/* FIXME: this is NOT complete. in icon view dnd needs to be much better about placement of icons and
 * being able to save/load icon placement. it doesn't support custom frames or icons yet
 */

#define EFM_SMART_CHECK(args ...)                                           \
  E_Fm2_Smart_Data * sd;                                                    \
                                                                            \
  if (evas_object_smart_smart_get(obj) != _e_fm2_smart) SMARTERRNR() args;  \
  sd = evas_object_smart_data_get(obj);                                     \
  if ((!sd) || (e_util_strcmp(evas_object_type_get(obj), "e_fm")))          \
    return args

typedef enum _E_Fm2_Action_Type
{
   FILE_ADD,
   FILE_DEL,
   FILE_CHANGE
} E_Fm2_Action_Type;

typedef struct _E_Fm2_Smart_Data        E_Fm2_Smart_Data;
typedef struct _E_Fm2_Region            E_Fm2_Region;
typedef struct _E_Fm2_Finfo             E_Fm2_Finfo;
typedef struct _E_Fm2_Action            E_Fm2_Action;
typedef struct _E_Fm2_Client            E_Fm2_Client;
typedef struct _E_Fm2_Uri               E_Fm2_Uri;
typedef struct _E_Fm2_Context_Menu_Data E_Fm2_Context_Menu_Data;

struct _E_Fm2_Smart_Data
{
   int          id;
   Evas_Coord   x, y, w, h, pw, ph;
   Eina_List   *icons;
   Evas_Object *obj;
   Evas_Object *clip;
   Evas_Object *underlay;
   unsigned int overlay_count;
   Evas_Object *overlay;
   Evas_Object *overlay_clip;
   Evas_Object *drop;
   Evas_Object *drop_in;
   Evas_Object *sel_rect;
   const char  *dev;
   const char  *path;
   const char  *realpath;

   struct
   {
      Evas_Coord w, h;
   } max, pmax, min; /* min is actually teh size of the largest icon, updated each placement */
   struct
   {
      Evas_Coord x, y;
   } pos;
   struct
   {
      Eina_List *list;
      int        member_max;
   } regions;
   struct
   {
      struct
      {
         E_Fm_Cb func;
         void   *data;
      } start, end, replace;
      E_Fm2_Menu_Flags flags;
   } icon_menu;

   struct
   {
      Ecore_Thread *thread;
      const char   *filename;
      Eina_Bool done E_BITFIELD;
   } new_file;

   E_Fm2_Icon      *last_selected;
   E_Fm2_Icon      *range_selected;
   E_Fm2_Icon      *range_select_anchor;
   Eina_List       *selected_icons;
   Eina_List       *icons_place;
   Eina_List       *queue;
   Ecore_Timer     *scan_timer;
   Ecore_Idler     *sort_idler;
   Ecore_Job       *scroll_job;
   Ecore_Job       *resize_job;
   Ecore_Job       *refresh_job;
   E_Menu          *menu;
   Eina_List       *rename_dialogs;
   E_Entry_Dialog  *entry_dialog;
   E_Dialog        *image_dialog;
   Eina_Bool        iconlist_changed E_BITFIELD;
   Eina_Bool        order_file E_BITFIELD;
   Eina_Bool        typebuf_visible E_BITFIELD;
   Eina_Bool        show_hidden_files E_BITFIELD;
   Eina_Bool        listing E_BITFIELD;
   Eina_Bool        inherited_dir_props E_BITFIELD;
   signed char      view_mode;  /* -1 = unset */
   signed short     icon_size;  /* -1 = unset */
   E_Fm2_View_Flags view_flags;

   E_Fm2_Icon      *last_placed;
   E_Fm2_Config    *config;
   const char      *custom_theme;
   const char      *custom_theme_content;

   struct
   {
      Evas_Object *obj, *obj2;
      Eina_List   *last_insert;
      Eina_List  **list_index;
      int          iter;
   } tmp;

   struct
   {
      Eina_List   *actions;
      Ecore_Idler *idler;
      Ecore_Timer *timer;
      Eina_Bool    deletions E_BITFIELD;
   } live;

   struct
   {
      char        *buf;
      const char  *start;
      Ecore_Timer *timer;
      unsigned int wildcard;
      Eina_Bool    setting E_BITFIELD;
      Eina_Bool    disabled E_BITFIELD;
   } typebuf;

   int                  busy_count;

   E_Object            *eobj;
   Evas_Object         *win;
   E_Drop_Handler      *drop_handler;
   E_Fm2_Icon          *drop_icon;
   Ecore_Animator      *dnd_scroller;
   Evas_Point           dnd_current;
   Eina_List           *mount_ops;
   E_Fm2_Mount         *mount;
   signed char          drop_after;
   Eina_Bool            drop_show E_BITFIELD;
   Eina_Bool            drop_in_show E_BITFIELD;
   Eina_Bool            drop_all E_BITFIELD;
   Eina_Bool            drag E_BITFIELD;
   Eina_Bool            selecting E_BITFIELD;
   Eina_Bool            toomany E_BITFIELD;
   struct
   {
      int ox, oy;
      int x, y, w, h;
   } selrect;

   E_Fm2_Icon          *iop_icon;

   Eina_List           *handlers;
   Efreet_Desktop      *desktop;
};

struct _E_Fm2_Region
{
   E_Fm2_Smart_Data *sd;
   Evas_Coord        x, y, w, h;
   Eina_List        *list;
   Eina_Bool         realized E_BITFIELD;
};

struct _E_Fm2_Icon
{
   int               saved_x, saved_y;
   double            selected_time;
   E_Fm2_Smart_Data *sd;
   E_Fm2_Region     *region;
   Evas_Coord        x, y, w, h, min_w, min_h;
   Evas_Object      *obj, *obj_icon;
   E_Menu           *menu;
   E_Entry_Dialog   *entry_dialog;
   Evas_Object      *entry_widget;
   E_Client_Hook    *focus_hook;
   Eio_File         *eio;
   Ecore_X_Window    keygrab;
   E_Config_Dialog  *prop_dialog;
   E_Dialog         *dialog;

   E_Fm2_Icon_Info   info;
   E_Fm2_Mount      *mount; // for dnd into unmounted dirs
   Ecore_Timer      *mount_timer; // autounmount in 15s

   struct
   {
      Evas_Coord   x, y;
      Ecore_Timer *dnd_end_timer; //we need this for XDirectSave drops so we don't lose the icon
      Eina_Bool    start E_BITFIELD;
      Eina_Bool    dnd E_BITFIELD; // currently dragging
      Eina_Bool    src E_BITFIELD; // drag source
      Eina_Bool    hidden E_BITFIELD; // dropped into different dir
   } drag;

   int       saved_rel;

   Eina_Bool realized E_BITFIELD;
   Eina_Bool selected E_BITFIELD;
   Eina_Bool last_selected E_BITFIELD;
   Eina_Bool saved_pos E_BITFIELD;
   Eina_Bool odd E_BITFIELD;
   Eina_Bool down_sel E_BITFIELD;
   Eina_Bool removable_state_change E_BITFIELD;
   Eina_Bool thumb_failed E_BITFIELD;
   Eina_Bool queued E_BITFIELD;
   Eina_Bool inserted E_BITFIELD;
};

struct _E_Fm2_Finfo
{
   struct stat st;
   int         broken_link;
   const char *lnk;
   const char *rlnk;
};

struct _E_Fm2_Action
{
   E_Fm2_Action_Type type;
   const char       *file;
   const char       *file2;
   int               flags;
   E_Fm2_Finfo       finf;
};

struct _E_Fm2_Client
{
   Ecore_Ipc_Client *cl;
   int               req;
};

struct _E_Fm2_Uri
{
   const char *hostname;
   const char *path;
};

struct _E_Fm2_Context_Menu_Data
{
   E_Fm2_Icon         *icon;
   E_Fm2_Smart_Data   *sd;
   E_Fm2_Mime_Handler *handler;
};
static Eina_Bool     _e_fm2_cb_drag_finished_show(E_Fm2_Icon *ic);
static const char   *_e_fm2_dev_path_map(E_Fm2_Smart_Data *sd, const char *dev, const char *path);
static void          _e_fm2_file_add(Evas_Object *obj, const char *file, int unique, Eina_Stringshare *file_rel, int after, E_Fm2_Finfo *finf);
static void          _e_fm2_file_del(Evas_Object *obj, const char *file);
static void          _e_fm2_queue_process(Evas_Object *obj);
static void          _e_fm2_queue_free(Evas_Object *obj);
static void          _e_fm2_regions_free(Evas_Object *obj);
static void          _e_fm2_regions_populate(Evas_Object *obj);
static void          _e_fm2_icons_place(Evas_Object *obj);
static void          _e_fm2_icons_free(Evas_Object *obj);
static void          _e_fm2_regions_eval(Evas_Object *obj);
static void          _e_fm2_config_free(E_Fm2_Config *cfg);

static void          _e_fm2_dir_load_props(E_Fm2_Smart_Data *sd);
static void          _e_fm2_dir_save_props(E_Fm2_Smart_Data *sd);

static Eina_List    *_e_fm2_file_fm2_find(const char *file);
static E_Fm2_Icon   *_e_fm2_icon_find(Evas_Object *obj, const char *file);
static const char   *_e_fm2_uri_escape(const char *path);
static Eina_List    *_e_fm2_uri_selected_icon_list_get(Eina_List *uri);

static E_Fm2_Icon   *_e_fm2_icon_new(E_Fm2_Smart_Data *sd, const char *file, E_Fm2_Finfo *finf);
static void          _e_fm2_icon_unfill(E_Fm2_Icon *ic);
static int           _e_fm2_icon_fill(E_Fm2_Icon *ic, E_Fm2_Finfo *finf);
static void          _e_fm2_icon_free(E_Fm2_Icon *ic);
static void          _e_fm2_icon_realize(E_Fm2_Icon *ic);
static void          _e_fm2_icon_unrealize(E_Fm2_Icon *ic);
static Eina_Bool     _e_fm2_icon_visible(const E_Fm2_Icon *ic);
static void          _e_fm2_icon_label_set(E_Fm2_Icon *ic, Evas_Object *obj);
static Evas_Object  *_e_fm2_icon_icon_direct_set(E_Fm2_Icon *ic, Evas_Object *o, Evas_Smart_Cb gen_func, void *data, int force_gen);
static void          _e_fm2_icon_icon_set(E_Fm2_Icon *ic);
static void          _e_fm2_icon_thumb(const E_Fm2_Icon *ic, Evas_Object *oic, int force);
static void          _e_fm2_icon_select(E_Fm2_Icon *ic);
static void          _e_fm2_icon_deselect(E_Fm2_Icon *ic);
static int           _e_fm2_icon_desktop_load(E_Fm2_Icon *ic);

static E_Fm2_Region *_e_fm2_region_new(E_Fm2_Smart_Data *sd);
static void          _e_fm2_region_free(E_Fm2_Region *rg);
static void          _e_fm2_region_realize(E_Fm2_Region *rg);
static void          _e_fm2_region_unrealize(E_Fm2_Region *rg);
static int           _e_fm2_region_visible(E_Fm2_Region *rg);

static void          _e_fm2_icon_make_visible(E_Fm2_Icon *ic);
static void          _e_fm2_icon_desel_any(Evas_Object *obj);
static E_Fm2_Icon   *_e_fm2_icon_first_selected_find(Evas_Object *obj);
static E_Fm2_Icon   *_e_fm2_icon_next_find(Evas_Object *obj, int next, int (*match_func)(E_Fm2_Icon *ic, void *data), void *data);

static void          _e_fm2_icon_sel_any(Evas_Object *obj);
static void          _e_fm2_icon_sel_first(Evas_Object *obj, Eina_Bool add);
static void          _e_fm2_icon_sel_last(Evas_Object *obj, Eina_Bool add);
static void          _e_fm2_icon_sel_prev(Evas_Object *obj, Eina_Bool add);
static void          _e_fm2_icon_sel_next(Evas_Object *obj, Eina_Bool add);
static void          _e_fm2_icon_sel_down(Evas_Object *obj, Eina_Bool add);
static void          _e_fm2_icon_sel_up(Evas_Object *obj, Eina_Bool add);
static void          _e_fm2_icon_range_select(E_Fm2_Icon *ic);

static void          _e_fm2_typebuf_show(Evas_Object *obj);
static void          _e_fm2_typebuf_hide(Evas_Object *obj);
//static void _e_fm2_typebuf_history_prev(Evas_Object *obj);
//static void _e_fm2_typebuf_history_next(Evas_Object *obj);
static void          _e_fm2_typebuf_run(Evas_Object *obj);
static E_Fm2_Icon   *_e_fm2_typebuf_match(Evas_Object *obj, int next);
static void          _e_fm2_typebuf_complete(Evas_Object *obj);
static void          _e_fm2_typebuf_char_append(Evas_Object *obj, const char *ch);
static void          _e_fm2_typebuf_char_backspace(Evas_Object *obj);

static void          _e_fm2_cb_dnd_enter(void *data, const char *type, void *event);
static void          _e_fm2_cb_dnd_move(void *data, const char *type, void *event);
static void          _e_fm2_cb_dnd_leave(void *data, const char *type, void *event);
static void          _e_fm2_cb_dnd_selection_notify(void *data, const char *type, void *event);
static void          _e_fm2_cb_icon_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_icon_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_icon_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_icon_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_icon_mouse_out(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_icon_thumb_dnd_gen(void *data, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_icon_thumb_gen(void *data, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_cb_scroll_job(void *data);
static void          _e_fm2_cb_resize_job(void *data);
static int           _e_fm2_cb_icon_sort(const void *data1, const void *data2);
static Eina_Bool     _e_fm2_cb_scan_timer(void *data);
static Eina_Bool     _e_fm2_cb_sort_idler(void *data);
static Eina_Bool     _e_fm2_cb_theme(void *data, int type EINA_UNUSED, void *event EINA_UNUSED);

static void          _e_fm2_obj_icons_place(E_Fm2_Smart_Data *sd);

static void          _e_fm2_smart_add(Evas_Object *object);
static void          _e_fm2_smart_del(Evas_Object *object);
static void          _e_fm2_smart_move(Evas_Object *object, Evas_Coord x, Evas_Coord y);
static void          _e_fm2_smart_resize(Evas_Object *object, Evas_Coord w, Evas_Coord h);
static void          _e_fm2_smart_show(Evas_Object *object);
static void          _e_fm2_smart_hide(Evas_Object *object);
static void          _e_fm2_smart_color_set(Evas_Object *obj, int r, int g, int b, int a);
static void          _e_fm2_smart_clip_set(Evas_Object *obj, Evas_Object *clip);
static void          _e_fm2_smart_clip_unset(Evas_Object *obj);

static void          _e_fm2_menu(Evas_Object *obj, unsigned int timestamp);
static void          _e_fm2_menu_post_cb(void *data, E_Menu *m);
static void          _e_fm2_icon_menu(E_Fm2_Icon *ic, Evas_Object *obj, unsigned int timestamp);
static void          _e_fm2_icon_menu_post_cb(void *data, E_Menu *m);
static void          _e_fm2_icon_menu_item_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_icon_view_menu_pre(void *data, E_Menu *m);
static void          _e_fm2_options_menu_pre(void *data, E_Menu *m);
static void          _e_fm2_add_menu_pre(void *data, E_Menu *m);
static void          _e_fm2_toggle_inherit_dir_props(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_menu_pre(void *data, E_Menu *m);
static void          _e_fm2_view_menu_grid_icons_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_menu_custom_icons_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_menu_list_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_menu_use_default_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_menu_set_background_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_menu_set_overlay_cb(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_view_image_sel(E_Fm2_Smart_Data *sd, const char *title, void (*ok_cb)(void *data, E_Dialog *dia));
static void          _e_fm2_view_image_sel_close(void *data, E_Dialog *dia);
static void          _e_fm2_refresh(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_toggle_hidden_files(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_toggle_single_click(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_toggle_secure_rm(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_toggle_ordering(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_sort(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_new_directory(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_rename(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_rename_delete_cb(void *obj);
static void          _e_fm2_file_rename_yes_cb(void *data, char *text);
static void          _e_fm2_file_rename_no_cb(void *data);
static void          _e_fm2_file_application_properties(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED);
static void          _e_fm2_file_properties(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_properties_delete_cb(void *obj);
static int           _e_fm2_file_do_rename(const char *text, E_Fm2_Icon *ic);

static Evas_Object  *_e_fm2_icon_entry_widget_add(E_Fm2_Icon *ic);
static void          _e_fm2_icon_entry_widget_del(E_Fm2_Icon *ic);
static void          _e_fm2_icon_entry_widget_cb_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void          _e_fm2_icon_entry_widget_accept(E_Fm2_Icon *ic);

static E_Dialog     *_e_fm_retry_abort_dialog(int pid, const char *str);
static void          _e_fm_retry_abort_retry_cb(void *data, E_Dialog *dialog);
static void          _e_fm_retry_abort_abort_cb(void *data, E_Dialog *dialog);

static E_Dialog     *_e_fm_overwrite_dialog(int pid, const char *str);
static void          _e_fm_overwrite_no_cb(void *data, E_Dialog *dialog);
static void          _e_fm_overwrite_no_all_cb(void *data, E_Dialog *dialog);
static void          _e_fm_overwrite_rename(void *data, E_Dialog *dialog);
static void          _e_fm_overwrite_yes_cb(void *data, E_Dialog *dialog);
static void          _e_fm_overwrite_yes_all_cb(void *data, E_Dialog *dialog);

static E_Dialog     *_e_fm_error_dialog(int pid, const char *str);
static void          _e_fm_error_retry_cb(void *data, E_Dialog *dialog);
static void          _e_fm_error_abort_cb(void *data, E_Dialog *dialog);
static void          _e_fm_error_link_source(void *data, E_Dialog *dialog);
static void          _e_fm_error_ignore_this_cb(void *data, E_Dialog *dialog);
static void          _e_fm_error_ignore_all_cb(void *data, E_Dialog *dialog);

static void          _e_fm_device_error_dialog(const char *title, const char *msg, const char *pstr);

static void          _e_fm2_file_delete(Evas_Object *obj);
static void          _e_fm2_file_delete_menu(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_delete_delete_cb(void *obj);
static void          _e_fm2_file_delete_yes_cb(void *data, E_Dialog *dialog);
static void          _e_fm2_file_delete_no_cb(void *data, E_Dialog *dialog);
static void          _e_fm2_refresh_job_cb(void *data);
static void          _e_fm_file_buffer_clear(void);
static void          _e_fm2_file_cut(Evas_Object *obj);
static void          _e_fm2_file_copy(Evas_Object *obj);
static void          _e_fm2_file_paste(Evas_Object *obj);
static void          _e_fm2_file_symlink(Evas_Object *obj);
static void          _e_fm2_file_cut_menu(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_copy_menu(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_paste_menu(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_file_symlink_menu(void *data, E_Menu *m, E_Menu_Item *mi);

static void          _e_fm2_live_file_add(Evas_Object *obj, const char *file, const char *file_rel, int after, E_Fm2_Finfo *finf);
static void          _e_fm2_live_file_del(Evas_Object *obj, const char *file);
static void          _e_fm2_live_file_changed(Evas_Object *obj, const char *file, E_Fm2_Finfo *finf);
static void          _e_fm2_live_process_begin(Evas_Object *obj);
static void          _e_fm2_live_process_end(Evas_Object *obj);
static void          _e_fm2_live_process(Evas_Object *obj);
static Eina_Bool     _e_fm2_cb_live_idler(void *data);
static Eina_Bool     _e_fm2_cb_live_timer(void *data);

static int           _e_fm2_theme_edje_object_set(E_Fm2_Smart_Data *sd, Evas_Object *o, const char *category, const char *group);
static int           _e_fm2_theme_edje_icon_object_set(E_Fm2_Smart_Data *sd, Evas_Object *o, const char *category, const char *group);

static void          _e_fm2_mouse_2_handler(E_Fm2_Icon *ic, void *evas_event);

static void          _e_fm2_client_spawn(void);
static E_Fm2_Client *_e_fm2_client_get(void);
static int           _e_fm2_client_monitor_add(const char *path);
static void          _e_fm2_client_monitor_del(int id, const char *path);
static int           _e_fm_client_file_del(const char *files, Eina_Bool secure, Evas_Object *e_fm);
//static int _e_fm2_client_file_trash(const char *path, Evas_Object *e_fm);
//static int           _e_fm2_client_file_mkdir(const char *path, const char *rel, int rel_to, int x, int y, int res_w, int res_h, Evas_Object *e_fm);

static void          _e_fm2_sel_rect_update(void *data);
static void          _e_fm2_context_menu_append(E_Fm2_Smart_Data *sd, const char *path, const Eina_List *l, E_Menu *mn, E_Fm2_Icon *ic);
static int           _e_fm2_context_list_sort(const void *data1, const void *data2);

void                 _e_fm2_path_parent_set(Evas_Object *obj, const char *path);

static void          _e_fm2_volume_mount(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_volume_unmount(void *data, E_Menu *m, E_Menu_Item *mi);
static void          _e_fm2_volume_eject(void *data, E_Menu *m, E_Menu_Item *mi);

static void          _e_fm2_icon_removable_update(E_Fm2_Icon *ic);
static void          _e_fm2_volume_icon_update(E_Volume *v);
static int           _e_fm2_desktop_open(E_Fm2_Smart_Data *sd);
static void          _e_fm2_operation_abort_internal(E_Fm2_Op_Registry_Entry *ere);

static Eina_Bool     _e_fm2_sys_suspend_hibernate(void *, int, void *);

static void          _e_fm2_favorites_thread_cb(void *d, Ecore_Thread *et);
static void          _e_fm2_thread_cleanup_cb(void *d, Ecore_Thread *et);

static void _e_fm2_new_file(void *data, E_Menu *m, E_Menu_Item *mi);

static Ecore_Exe *_e_fm2_exe = NULL;
static char *_e_fm2_meta_path = NULL;
static Evas_Smart *_e_fm2_smart = NULL;
static Eina_List *_e_fm2_list = NULL;
static Eina_List *_e_fm2_list_remove = NULL;
static int _e_fm2_list_walking = 0;
static Eina_List *_e_fm2_client_list = NULL;
static Eina_List *_e_fm2_menu_contexts = NULL;
static Eina_List *_e_fm_file_buffer = NULL; /* Files for copy&paste are saved here. */
static int _e_fm_file_buffer_copying = 0;
static const char *_e_fm2_icon_desktop_str = NULL;
static const char *_e_fm2_icon_thumb_str = NULL;
static const char *_e_fm2_mime_inode_directory = NULL;
static const char *_e_fm2_mime_app_desktop = NULL;
static const char *_e_fm2_mime_app_edje = NULL;
static const char *_e_fm2_mime_text_uri_list = NULL;
static const char *_e_fm2_mime_xmozurl = NULL;
static const char *_e_fm2_xds = NULL;

static Eina_List *_e_fm_handlers = NULL;

static const char **_e_fm2_dnd_types[] =
{
   &_e_fm2_mime_text_uri_list,
   &_e_fm2_xds,
   &_e_fm2_mime_xmozurl,
   NULL
};

static Ecore_Timer *_e_fm2_mime_flush = NULL;
static Ecore_Timer *_e_fm2_mime_clear = NULL;
static Ecore_Thread *_e_fm2_favorites_thread = NULL;

/* contains:
 * _e_volume_edd
 * _e_storage_edd
 * _e_volume_free()
 * _e_storage_free()
 * _e_volume_edd_new()
 * _e_storage_edd_new()
 * _e_storage_volume_edd_init()
 * _e_storage_volume_edd_shutdown()
 */

#include "e_fm_shared_codec.h"

static inline Eina_Bool
_e_fm2_icon_realpath(const E_Fm2_Icon *ic, char *buf, int buflen)
{
   int r = snprintf(buf, buflen, "%s/%s", ic->sd->realpath, ic->info.file);
   return r < buflen;
}

static inline Eina_Bool
_e_fm2_icon_path(const E_Fm2_Icon *ic, char *buf, int buflen)
{
   int r;

   if (ic->info.real_link)
     r = snprintf(buf, buflen, "%s", ic->info.real_link);
   else
     r = snprintf(buf, buflen, "%s/%s", ic->sd->path, ic->info.file);

   return r < buflen;
}

static inline Eina_Bool
_e_fm2_ext_is_edje(const char *ext)
{
#ifdef E_FM2_SIMPLE_STRCASE_FILES
   if ((ext[0] == 'e') && (ext[1] == 'd') && (ext[2] == 'j'))
     return 1;
   else if ((ext[0] == 'E') && (ext[1] == 'D') && (ext[2] == 'J'))
     return 1;
   else
     return 0;
#else
   return strcasecmp(ext, "edj") == 0;
#endif
}

static inline Eina_Bool
_e_fm2_ext_is_desktop(const char *ext)
{
#ifdef E_FM2_SIMPLE_STRCASE_FILES
   if ((ext[0] == 'd') &&
       ((strcmp(ext + 1, "esktop") == 0) ||
        (strcmp(ext + 1, "irectory") == 0)))
     return 1;
   else if ((ext[0] == 'D') &&
            ((strcmp(ext + 1, "ESKTOP") == 0) ||
             (strcmp(ext + 1, "IRECTORY") == 0)))
     return 1;
   else
     return 0;
#else
   if ((ext[0] != 'd') && (ext[0] != 'D'))
     return 0;

   ext++;
   return (strcasecmp(ext, "esktop") == 0) ||
          (strcasecmp(ext, "irectory") == 0);
#endif
}

static inline Eina_Bool
_e_fm2_ext_is_imc(const char *ext)
{
#ifdef E_FM2_SIMPLE_STRCASE_FILES
   if ((ext[0] == 'i') && (ext[1] == 'm') && (ext[2] == 'c'))
     return 1;
   else if ((ext[0] == 'I') && (ext[1] == 'M') && (ext[2] == 'C'))
     return 1;
   else
     return 0;
#else
   return strcasecmp(ext, "imc") == 0;
#endif
}

static inline Eina_Bool
_e_fm2_file_is_edje(const char *file)
{
   const char *p = strrchr(file, '.');
   return (p) && (_e_fm2_ext_is_edje(p + 1));
}

static inline Eina_Bool
_e_fm2_file_is_desktop(const char *file)
{
   const char *p = strrchr(file, '.');
   return (p) && (_e_fm2_ext_is_desktop(p + 1));
}

static inline Eina_Bool
_e_fm2_toomany_get(const E_Fm2_Smart_Data *sd)
{
   char mode;

   mode = sd->config->view.mode;
   if (sd->view_mode > -1) mode = sd->view_mode;
   if (((mode >= E_FM2_VIEW_MODE_CUSTOM_ICONS) &&
        (mode <= E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS)) &&
       (eina_list_count(sd->icons) >= 500))
     return EINA_TRUE;
   return EINA_FALSE;
}

static inline char
_e_fm2_view_mode_get(const E_Fm2_Smart_Data *sd)
{
   char mode;

   mode = sd->config->view.mode;
   if (sd->view_mode > -1) mode = sd->view_mode;
   if ((sd->toomany) &&
       ((mode >= E_FM2_VIEW_MODE_CUSTOM_ICONS) &&
        (mode <= E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS)))
     return E_FM2_VIEW_MODE_GRID_ICONS;
   return mode;
}

static inline Evas_Coord
_e_fm2_icon_w_get(const E_Fm2_Smart_Data *sd)
{
   if (sd->icon_size > -1)
     return sd->icon_size * e_scale;
   if (sd->config->icon.icon.w)
     return sd->config->icon.icon.w;
   return sd->config->icon.list.w;
}

static inline Evas_Coord
_e_fm2_icon_h_get(const E_Fm2_Smart_Data *sd)
{
   if (sd->icon_size > -1)
     return sd->icon_size * e_scale;
   if (sd->config->icon.icon.h)
     return sd->config->icon.icon.h;
   return sd->config->icon.list.h;
}

static inline unsigned int
_e_fm2_icon_mime_size_normalize(const E_Fm2_Icon *ic)
{
   return e_util_icon_size_normalize(_e_fm2_icon_w_get(ic->sd));
}

static Eina_Bool
_e_fm2_mime_flush_cb(void *data EINA_UNUSED)
{
   efreet_mime_type_cache_flush();
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_fm2_mime_clear_cb(void *data EINA_UNUSED)
{
   efreet_mime_type_cache_clear();
   return ECORE_CALLBACK_RENEW;
}

static void
_e_fm2_op_registry_go_on(int id)
{
   E_Fm2_Op_Registry_Entry *ere = e_fm2_op_registry_entry_get(id);
   if (!ere) return;
   ere->status = E_FM2_OP_STATUS_IN_PROGRESS;
   ere->needs_attention = 0;
   ere->dialog = NULL;
   e_fm2_op_registry_entry_changed(ere);
}

static void
_e_fm2_op_registry_aborted(int id)
{
   E_Fm2_Op_Registry_Entry *ere = e_fm2_op_registry_entry_get(id);
   if (!ere) return;
   ere->status = E_FM2_OP_STATUS_ABORTED;
   ere->needs_attention = 0;
   ere->dialog = NULL;
   ere->finished = 1;
   e_fm2_op_registry_entry_changed(ere);
   // XXX e_fm2_op_registry_entry_del(id);
}

static void
_e_fm2_op_registry_error(int id, E_Dialog *dlg)
{
   E_Fm2_Op_Registry_Entry *ere = e_fm2_op_registry_entry_get(id);
   if (!ere) return;
   ere->status = E_FM2_OP_STATUS_ERROR;
   ere->needs_attention = 1;
   ere->dialog = dlg;
   e_fm2_op_registry_entry_changed(ere);
}

static void
_e_fm2_op_registry_needs_attention(int id, E_Dialog *dlg)
{
   E_Fm2_Op_Registry_Entry *ere = e_fm2_op_registry_entry_get(id);
   if (!ere) return;
   ere->needs_attention = 1;
   ere->dialog = dlg;
   e_fm2_op_registry_entry_changed(ere);
}

/////////////// DBG:
static void
_e_fm2_op_registry_entry_print(const E_Fm2_Op_Registry_Entry *ere)
{
   const char *status_strings[] =
   {
      "UNKNOWN", "IN_PROGRESS", "SUCCESSFUL", "ABORTED", "ERROR"
   };
   const char *status;

   if (ere->status <= E_FM2_OP_STATUS_ERROR)
     status = status_strings[ere->status];
   else
     status = status_strings[0];

   DBG("id: %8d, op: %2d [%s] finished: %hhu, needs_attention: %hhu\n"
       "    %3d%% (%" PRIi64 "/%" PRIi64 "), start_time: %10.0f, eta: %5ds, xwin: %#x\n"
                                         "    src=[%s]\n"
                                         "    dst=[%s]",
       ere->id, ere->op, status, ere->finished, ere->needs_attention,
       ere->percent, ere->done, ere->total, ere->start_time, ere->eta,
       e_fm2_op_registry_entry_xwin_get(ere),
       ere->src, ere->dst);
}

static Eina_Bool
_e_fm2_exe_del(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   Ecore_Exe_Event_Del *ev = event;
   if (ev->exe == _e_fm2_exe) _e_fm2_exe = NULL;
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_fm2_op_registry_entry_add_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   const E_Fm2_Op_Registry_Entry *ere = event;
   DBG("E FM OPERATION STARTED: id=%d, op=%d", ere->id, ere->op);
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_fm2_op_registry_entry_del_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   const E_Fm2_Op_Registry_Entry *ere = event;
   puts("E FM OPERATION FINISHED:");
   _e_fm2_op_registry_entry_print(ere);
   puts("---");
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_fm2_op_registry_entry_changed_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   const E_Fm2_Op_Registry_Entry *ere = event;
   puts("E FM OPERATION CHANGED:");
   _e_fm2_op_registry_entry_print(ere);
   puts("---");
   return ECORE_CALLBACK_RENEW;
}

/////////////// DBG:

/***/

EINTERN int
e_fm2_init(void)
{
   char path[PATH_MAX];

   eina_init();
   ecore_init();
   _e_storage_volume_edd_init();
   e_user_dir_concat_static(path, "fileman/metadata");
   ecore_file_mkpath(path);
   _e_fm2_meta_path = strdup(path);

   {
      static const Evas_Smart_Class sc =
      {
         "e_fm",
         EVAS_SMART_CLASS_VERSION,
         _e_fm2_smart_add, /* add */
         _e_fm2_smart_del, /* del */
         _e_fm2_smart_move, /* move */
         _e_fm2_smart_resize, /* resize */
         _e_fm2_smart_show, /* show */
         _e_fm2_smart_hide, /* hide */
         _e_fm2_smart_color_set, /* color_set */
         _e_fm2_smart_clip_set, /* clip_set */
         _e_fm2_smart_clip_unset, /* clip_unset */
         NULL,
         NULL,
         NULL,
         NULL,
         NULL,
         NULL,
         NULL
      };
      _e_fm2_smart = evas_smart_class_new(&sc);
   }
//   _e_fm2_client_spawn();
   e_fm2_custom_file_init();
   e_fm2_op_registry_init();
   efreet_mime_init();

   /* XXX: move this to a central/global place? */
   _e_fm2_mime_flush = ecore_timer_loop_add(60.0, _e_fm2_mime_flush_cb, NULL);
   _e_fm2_mime_clear = ecore_timer_loop_add(600.0, _e_fm2_mime_clear_cb, NULL);

   _e_fm2_icon_desktop_str = eina_stringshare_add("DESKTOP");
   _e_fm2_icon_thumb_str = eina_stringshare_add("THUMB");
   _e_fm2_mime_inode_directory = eina_stringshare_add("inode/directory");
   _e_fm2_mime_app_desktop = eina_stringshare_add("application/x-desktop");
   _e_fm2_mime_app_edje = eina_stringshare_add("application/x-extension-edj");
   _e_fm2_mime_text_uri_list = eina_stringshare_add("text/uri-list");
   _e_fm2_xds = eina_stringshare_add("XdndDirectSave0");
   _e_fm2_mime_xmozurl = eina_stringshare_add("text/x-moz-url");

   _e_fm2_favorites_thread = ecore_thread_run(_e_fm2_favorites_thread_cb,
                                              _e_fm2_thread_cleanup_cb,
                                              _e_fm2_thread_cleanup_cb, NULL);

   E_LIST_HANDLER_APPEND(_e_fm_handlers, ECORE_EXE_EVENT_DEL, _e_fm2_exe_del, NULL);
   /// DBG
   E_LIST_HANDLER_APPEND(_e_fm_handlers, E_EVENT_FM_OP_REGISTRY_ADD, _e_fm2_op_registry_entry_add_cb, NULL);
   E_LIST_HANDLER_APPEND(_e_fm_handlers, E_EVENT_FM_OP_REGISTRY_DEL, _e_fm2_op_registry_entry_del_cb, NULL);
   E_LIST_HANDLER_APPEND(_e_fm_handlers, E_EVENT_FM_OP_REGISTRY_CHANGED, _e_fm2_op_registry_entry_changed_cb, NULL);
   /// DBG
   E_LIST_HANDLER_APPEND(_e_fm_handlers, E_EVENT_SYS_HIBERNATE, _e_fm2_sys_suspend_hibernate, NULL);
   E_LIST_HANDLER_APPEND(_e_fm_handlers, E_EVENT_SYS_RESUME, _e_fm2_sys_suspend_hibernate, NULL);

   return 1;
}

EINTERN int
e_fm2_shutdown(void)
{
   e_fm2_die();

   E_FREE_LIST(_e_fm2_list, evas_object_del);

   eina_stringshare_replace(&_e_fm2_icon_desktop_str, NULL);
   eina_stringshare_replace(&_e_fm2_icon_thumb_str, NULL);
   eina_stringshare_replace(&_e_fm2_mime_inode_directory, NULL);
   eina_stringshare_replace(&_e_fm2_mime_app_desktop, NULL);
   eina_stringshare_replace(&_e_fm2_mime_app_edje, NULL);
   eina_stringshare_replace(&_e_fm2_mime_text_uri_list, NULL);
   eina_stringshare_replace(&_e_fm2_xds, NULL);
   eina_stringshare_replace(&_e_fm2_mime_xmozurl, NULL);

   E_FREE_LIST(_e_fm_handlers, ecore_event_handler_del);

   ecore_timer_del(_e_fm2_mime_flush);
   _e_fm2_mime_flush = NULL;
   ecore_timer_del(_e_fm2_mime_clear);
   _e_fm2_mime_clear = NULL;

   if (_e_fm2_favorites_thread) ecore_thread_cancel(_e_fm2_favorites_thread);
   _e_fm2_favorites_thread = NULL;

   evas_smart_free(_e_fm2_smart);
   _e_fm2_smart = NULL;
   E_FREE(_e_fm2_meta_path);
   e_fm2_custom_file_shutdown();
   _e_storage_volume_edd_shutdown();
   e_fm2_op_registry_shutdown();
   efreet_mime_shutdown();
   ecore_shutdown();
   eina_shutdown();
   return 1;
}

E_API void
e_fm2_die(void)
{
   if (_e_fm2_exe)
     {
        ecore_exe_kill(_e_fm2_exe);
        ecore_exe_free(_e_fm2_exe);
        _e_fm2_exe = NULL;
     }
}

E_API Evas_Object *
e_fm2_add(Evas *evas)
{
   return evas_object_smart_add(evas, _e_fm2_smart);
}

static Eina_Bool
_e_fm2_cb_dnd_drop(void *data, const char *type)
{
   E_Fm2_Smart_Data *sd = data;
   Eina_Bool allow;
   char buf[PATH_MAX];

   if (sd->config->view.link_drop && (type == _e_fm2_xds))
     allow = EINA_FALSE;
   else
     {
        if (sd->drop_icon)
          {
             snprintf(buf, sizeof(buf), "%s/%s", sd->realpath, sd->drop_icon->info.file);
             allow = ecore_file_can_write(buf);
          }
        else
          allow = (sd->realpath && ecore_file_can_write(sd->realpath));
     }

   e_drop_xds_update(allow, sd->drop_icon ? buf : sd->realpath);
   if (sd->dnd_scroller) ecore_animator_del(sd->dnd_scroller);
   sd->dnd_scroller = NULL;
   sd->dnd_current.x = sd->dnd_current.y = 0;
   return allow;
}

static void
_e_fm2_cb_mount_ok(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return;  // safety

   if (sd->mount->volume->efm_mode != EFM_MODE_USING_HAL_MOUNT)
     {
        /* Clean up typebuf. */
        _e_fm2_typebuf_hide(data);
        /* we only just now have the mount point so we should do stuff we couldn't do before */
        eina_stringshare_replace(&sd->realpath, sd->mount->volume->mount_point);
        eina_stringshare_replace(&sd->mount->mount_point, sd->mount->volume->mount_point);
        _e_fm2_dir_load_props(sd);
     }

   if ((sd->realpath) && (strcmp(sd->mount->mount_point, sd->realpath)))
     {
        e_fm2_path_set(sd->obj, "/", sd->mount->mount_point);
     }
   else
     {
        sd->id = _e_fm2_client_monitor_add(sd->mount->mount_point);
        sd->listing = EINA_TRUE;
        evas_object_smart_callback_call(data, "dir_changed", NULL);
        sd->tmp.iter = EINA_FALSE;
     }
}

static void
_e_fm2_cb_mount_fail(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return;  // safety
   if (sd->mount)
     {
        // At this moment E_Fm2_Mount object already deleted in e_fm_device.c
        sd->mount = NULL;
        if (sd->config->view.open_dirs_in_place)
          e_fm2_path_set(data, "favorites", "/");
        else
          evas_object_smart_callback_call(data, "dir_deleted", NULL);
     }
}

static void
_e_fm2_cb_unmount_ok(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return;
   if (sd->mount)
     {
        sd->mount = NULL;
        if (sd->config->view.open_dirs_in_place && sd->realpath)
          _e_fm2_path_parent_set(data, sd->realpath);
        else
          evas_object_smart_callback_call(data, "dir_deleted", NULL);
     }
}

void
_e_fm2_path_parent_set(Evas_Object *obj, const char *path)
{
   char buf[PATH_MAX], *p;
   int idx;

   p = strrchr(path, '/');
   if (!p || (p == path))
     e_fm2_path_set(obj, "/", "/");
   else
     {
        idx = p - path;
        if (idx < PATH_MAX)
          {
             strncpy(buf, path, idx);
             buf[idx] = '\0';
             e_fm2_path_set(obj, "/", buf);
          }
        else
          e_fm2_path_set(obj, "/", "/");
     }
}

static void
_e_fm2_favorites_thread_cb(void *d EINA_UNUSED, Ecore_Thread *et EINA_UNUSED)
{
   e_fm2_favorites_init();
}

static void
_e_fm2_thread_cleanup_cb(void *d EINA_UNUSED, Ecore_Thread *et EINA_UNUSED)
{
   _e_fm2_favorites_thread = NULL;
}

E_API void
e_fm2_path_set(Evas_Object *obj, const char *dev, const char *path)
{
   const char *real_path;
   EFM_SMART_CHECK();

   /* internal config for now - don't see a point making this configurable */
   sd->regions.member_max = 64;

   if (!sd->config)
     {
        sd->config = E_NEW(E_Fm2_Config, 1);
        if (!sd->config) return;
//	sd->config->view.mode = E_FM2_VIEW_MODE_ICONS;
        sd->config->view.mode = E_FM2_VIEW_MODE_LIST;
        sd->config->view.open_dirs_in_place = EINA_TRUE;
        sd->config->view.selector = EINA_TRUE;
        sd->config->view.single_click = e_config->filemanager_single_click;
        sd->config->view.single_click_delay = EINA_FALSE;
        sd->config->view.no_subdir_jump = EINA_FALSE;
        sd->config->icon.max_thumb_size = 5;
        sd->config->icon.icon.w = 128;
        sd->config->icon.icon.h = 128;
        sd->config->icon.list.w = 24;
        sd->config->icon.list.h = 24;
        sd->config->icon.fixed.w = EINA_TRUE;
        sd->config->icon.fixed.h = EINA_TRUE;
        sd->config->icon.extension.show = EINA_FALSE;
        sd->config->list.sort.no_case = EINA_TRUE;
        sd->config->list.sort.dirs.first = EINA_TRUE;
        sd->config->list.sort.dirs.last = EINA_FALSE;
        sd->config->selection.single = EINA_FALSE;
        sd->config->selection.windows_modifiers = EINA_FALSE;
        sd->config->theme.background = NULL;
        sd->config->theme.frame = NULL;
        sd->config->theme.icons = NULL;
        sd->config->theme.fixed = EINA_FALSE;
     }

   real_path = _e_fm2_dev_path_map(sd, dev, path);

   /* If the path doesn't exist, popup a dialog */
   if (dev && strncmp(dev, "removable:", 10)
       && !ecore_file_exists(real_path))
     {
        E_Dialog *dialog;
        char text[4096 + 256];

        dialog = e_dialog_new(NULL, "E", "_fm_file_unexisting_path_dialog");
        e_dialog_button_add(dialog, _("Close"), NULL, NULL, dialog);
        e_dialog_button_focus_num(dialog, 0);
        e_dialog_title_set(dialog, _("Nonexistent path"));
        e_dialog_icon_set(dialog, "dialog-error", 64);

        snprintf(text, sizeof(text), _("%s doesn't exist."), real_path);

        e_dialog_text_set(dialog, text);
        elm_win_center(dialog->win, 1, 1);
        e_dialog_show(dialog);
        return;
     }
   if (real_path && (sd->realpath == real_path)) return;

   E_FREE_FUNC(sd->scan_timer, ecore_timer_del);
   E_FREE_FUNC(sd->sort_idler, ecore_idler_del);
   if (sd->busy_count)
     sd->busy_count--;
   if (sd->busy_count == 0)
     {
        edje_object_signal_emit(sd->overlay, "e,state,busy,stop", "e");
        e_fm2_custom_file_flush();
     }
   if (sd->realpath) _e_fm2_client_monitor_del(sd->id, sd->realpath);
   sd->listing = EINA_FALSE;
   if (sd->new_file.thread) ecore_thread_cancel(sd->new_file.thread);
   sd->new_file.thread = NULL;
   eina_stringshare_replace(&sd->new_file.filename, NULL);
   eina_stringshare_replace(&sd->dev, dev);
   eina_stringshare_replace(&sd->path, path);
   eina_stringshare_del(sd->realpath);
   sd->realpath = real_path;
   _e_fm2_queue_free(obj);
   _e_fm2_regions_free(obj);
   _e_fm2_icons_free(obj);
   sd->overlay_count = 0;
   edje_object_part_text_set(sd->overlay, "e.text.busy_label", "");

   _e_fm2_dir_load_props(sd);

   /* If the path change from a mountpoint to something else, we fake-unmount */
   if (sd->mount && sd->mount->mount_point && real_path
       && strncmp(sd->mount->mount_point, sd->realpath,
                  strlen(sd->mount->mount_point)))
     {
        e_fm2_device_unmount(sd->mount);
        sd->mount = NULL;
     }

   /* Clean up typebuf. */
   _e_fm2_typebuf_hide(obj);

   /* If the path is of type removable: we add a new mountpoint */
   if (sd->dev && !sd->mount && !strncmp(sd->dev, "removable:", 10))
     {
        E_Volume *v = NULL;

        v = e_fm2_device_volume_find(sd->dev + sizeof("removable:") - 1);
        if (v)
          {
             sd->mount = e_fm2_device_mount(v, _e_fm2_cb_mount_ok, _e_fm2_cb_mount_fail,
                                            _e_fm2_cb_unmount_ok, NULL, obj);
             if ((v->efm_mode == EFM_MODE_USING_HAL_MOUNT) && (!sd->mount->mounted)) return;
          }
     }
   else if (sd->config->view.open_dirs_in_place == 0)
     {
        E_Fm2_Mount *m;
        m = e_fm2_device_mount_find(sd->realpath);
        if (m)
          {
             sd->mount = e_fm2_device_mount(m->volume,
                                            _e_fm2_cb_mount_ok, _e_fm2_cb_mount_fail,
                                            _e_fm2_cb_unmount_ok, NULL, obj);
             if ((m->volume->efm_mode != EFM_MODE_USING_HAL_MOUNT) && (!sd->mount->mounted)) return;
          }
     }
   if (!sd->realpath) return;

   if (!sd->mount || sd->mount->mounted)
     {
        if (sd->dev && (!strcmp(sd->dev, "favorites")) && (!_e_fm2_favorites_thread))
          _e_fm2_favorites_thread = ecore_thread_run(_e_fm2_favorites_thread_cb,
                                                     _e_fm2_thread_cleanup_cb,
                                                     _e_fm2_thread_cleanup_cb, NULL);
        sd->id = _e_fm2_client_monitor_add(sd->realpath);
        sd->listing = EINA_TRUE;
     }

   evas_object_smart_callback_call(obj, "dir_changed", NULL);
   sd->tmp.iter = EINA_FALSE;
}

E_API void
e_fm2_underlay_show(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   evas_object_show(sd->underlay);
}

E_API void
e_fm2_underlay_hide(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   evas_object_hide(sd->underlay);
}

E_API void
e_fm2_all_unsel(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_icon_desel_any(obj);
}

E_API void
e_fm2_all_sel(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_icon_sel_any(obj);
}

E_API void
e_fm2_first_sel(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_icon_sel_first(obj, EINA_FALSE);
}

E_API void
e_fm2_last_sel(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_icon_sel_last(obj, EINA_FALSE);
}

E_API void
e_fm2_custom_theme_set(Evas_Object *obj, const char *path)
{
   EFM_SMART_CHECK();
   eina_stringshare_replace(&sd->custom_theme, path);
   _e_fm2_theme_edje_object_set(sd, sd->drop, "base/theme/fileman",
                                "list/drop_between");
   _e_fm2_theme_edje_object_set(sd, sd->drop_in, "base/theme/fileman",
                                "list/drop_in");
   _e_fm2_theme_edje_object_set(sd, sd->overlay, "base/theme/fileman",
                                "overlay");
   _e_fm2_theme_edje_object_set(sd, sd->sel_rect, "base/theme/fileman",
                                "rubberband");
}

E_API void
e_fm2_custom_theme_content_set(Evas_Object *obj, const char *content)
{
   EFM_SMART_CHECK();
   eina_stringshare_replace(&sd->custom_theme_content, content);
   _e_fm2_theme_edje_object_set(sd, sd->drop, "base/theme/fileman",
                                "list/drop_between");
   _e_fm2_theme_edje_object_set(sd, sd->drop_in, "base/theme/fileman",
                                "list/drop_in");
   _e_fm2_theme_edje_object_set(sd, sd->overlay, "base/theme/fileman",
                                "overlay");
}

E_API void
e_fm2_path_get(Evas_Object *obj, const char **dev, const char **path)
{
   if (dev) *dev = NULL;
   if (path) *path = NULL;
   EFM_SMART_CHECK();
   if (dev) *dev = sd->dev;
   if (path) *path = sd->path;
}

static E_Fm2_Custom_File *
_e_fm2_dir_load_props_from_parent(const char *path)
{
   E_Fm2_Custom_File *cf;
   char *parent;

   if ((!path) || (path[0] == '\0') || (strcmp(path, "/") == 0))
     return NULL;

   parent = ecore_file_dir_get(path);
   cf = e_fm2_custom_file_get(parent);
   if (((cf) && (cf->dir) && (cf->dir->prop.in_use)) || (!strcmp(parent, path)))
     {
        free(parent);
        return cf;
     }

   cf = _e_fm2_dir_load_props_from_parent(parent);
   free(parent);
   return cf;
}

static void
_e_fm2_dir_load_props(E_Fm2_Smart_Data *sd)
{
   E_Fm2_Custom_File *cf;

   if (!sd->realpath) return;  /* come back later */
   if (!(sd->view_flags & E_FM2_VIEW_LOAD_DIR_CUSTOM)) return;

   cf = e_fm2_custom_file_get(sd->realpath);
   if ((cf) && (cf->dir))
     {
        Evas_Coord x, y;

        if (sd->max.w - sd->w > 0)
          x = (sd->max.w - sd->w) * cf->dir->pos.x;
        else
          x = 0;

        if (sd->max.h - sd->h > 0)
          y = (sd->max.h - sd->h) * cf->dir->pos.y;
        else
          y = 0;

        e_fm2_pan_set(sd->obj, x, y);

        if (cf->dir->prop.in_use)
          {
             sd->view_mode = cf->dir->prop.view_mode;
             sd->icon_size = cf->dir->prop.icon_size;
             sd->order_file = !!cf->dir->prop.order_file;
             sd->show_hidden_files = !!cf->dir->prop.show_hidden_files;
             sd->inherited_dir_props = EINA_FALSE;
             sd->config->list.sort.no_case = cf->dir->prop.sort.no_case;
             sd->config->list.sort.size = cf->dir->prop.sort.size;
             sd->config->list.sort.extension = cf->dir->prop.sort.extension;
             sd->config->list.sort.mtime = cf->dir->prop.sort.mtime;
             sd->config->list.sort.dirs.first = cf->dir->prop.sort.dirs.first;
             sd->config->list.sort.dirs.last = cf->dir->prop.sort.dirs.last;
             return;
          }
     }
   else
     {
        sd->pos.x = 0;
        sd->pos.y = 0;
     }

   if (!(sd->view_flags & E_FM2_VIEW_INHERIT_DIR_CUSTOM))
     {
        sd->view_mode = -1;
        sd->icon_size = -1;
        sd->order_file = EINA_FALSE;
        sd->show_hidden_files = EINA_FALSE;
        sd->inherited_dir_props = EINA_FALSE;
        return;
     }

   sd->inherited_dir_props = EINA_TRUE;

   cf = _e_fm2_dir_load_props_from_parent(sd->realpath);
   if ((cf) && (cf->dir) && (cf->dir->prop.in_use))
     {
        sd->view_mode = cf->dir->prop.view_mode;
        sd->icon_size = cf->dir->prop.icon_size;
        sd->order_file = !!cf->dir->prop.order_file;
        sd->show_hidden_files = !!cf->dir->prop.show_hidden_files;
        sd->config->list.sort.no_case = cf->dir->prop.sort.no_case;
        sd->config->list.sort.size = cf->dir->prop.sort.size;
        sd->config->list.sort.extension = cf->dir->prop.sort.extension;
        sd->config->list.sort.mtime = cf->dir->prop.sort.mtime;
        sd->config->list.sort.dirs.first = cf->dir->prop.sort.dirs.first;
        sd->config->list.sort.dirs.last = cf->dir->prop.sort.dirs.last;
     }
   else
     {
        sd->view_mode = -1;
        sd->icon_size = -1;
        sd->order_file = EINA_FALSE;
        sd->show_hidden_files = EINA_FALSE;
     }
}

static void
_e_fm2_dir_save_props(E_Fm2_Smart_Data *sd)
{
   E_Fm2_Custom_File *cf, cf0;
   E_Fm2_Custom_Dir dir0;

   if (!(sd->view_flags & E_FM2_VIEW_SAVE_DIR_CUSTOM)) return;

   cf = e_fm2_custom_file_get(sd->realpath);
   if (!cf)
     {
        cf = &cf0;
        memset(cf, 0, sizeof(*cf));
        cf->dir = &dir0;
     }
   else if (!cf->dir)
     {
        E_Fm2_Custom_File *cf2 = cf;
        cf = &cf0;
        memcpy(cf, cf2, sizeof(*cf2));
        cf->dir = &dir0;
     }

   if (sd->max.w - sd->w > 0)
     cf->dir->pos.x = sd->pos.x / (double)(sd->max.w - sd->w);
   else
     cf->dir->pos.x = 0.0;

   if (sd->max.h - sd->h)
     cf->dir->pos.y = sd->pos.y / (double)(sd->max.h - sd->h);
   else
     cf->dir->pos.y = 0.0;

   cf->dir->prop.icon_size = sd->icon_size;
   cf->dir->prop.view_mode = sd->view_mode;
   cf->dir->prop.order_file = sd->order_file;
   cf->dir->prop.show_hidden_files = sd->show_hidden_files;
   cf->dir->prop.in_use = !sd->inherited_dir_props;

   if (sd->config)
     {
        cf->dir->prop.sort.no_case = sd->config->list.sort.no_case;
        cf->dir->prop.sort.size = sd->config->list.sort.size;
        cf->dir->prop.sort.extension = sd->config->list.sort.extension;
        cf->dir->prop.sort.mtime = sd->config->list.sort.mtime;
        cf->dir->prop.sort.dirs.first = sd->config->list.sort.dirs.first;
        cf->dir->prop.sort.dirs.last = sd->config->list.sort.dirs.last;
     }

   e_fm2_custom_file_set(sd->realpath, cf);
   e_fm2_custom_file_flush();
}

E_API void
e_fm2_refresh(Evas_Object *obj)
{
   EFM_SMART_CHECK();

   evas_object_smart_callback_call(sd->obj, "changed", NULL);

   _e_fm2_dir_save_props(sd);

   _e_fm2_queue_free(obj);
   _e_fm2_regions_free(obj);
   _e_fm2_icons_free(obj);

   sd->order_file = EINA_FALSE;

   if (sd->realpath)
     {
        sd->listing = EINA_FALSE;
        _e_fm2_client_monitor_del(sd->id, sd->realpath);
        sd->id = _e_fm2_client_monitor_add(sd->realpath);
        sd->listing = EINA_TRUE;
     }

   sd->tmp.iter = EINA_FALSE;
}

E_API int
e_fm2_has_parent_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(0);
   if (!sd->path) return 0;
   if (strcmp(sd->path, "/")) return 1;
   if (!sd->realpath) return 0;
   if ((sd->realpath[0] == 0) || (!strcmp(sd->realpath, "/"))) return 0;
   return 1;
}

E_API const char *
e_fm2_real_path_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(NULL);
   return sd->realpath;
}

E_API void
e_fm2_parent_go(Evas_Object *obj)
{
   char *p, *path;
   char buf[PATH_MAX];
   EFM_SMART_CHECK();
   if (!sd->path) return;
   path = memcpy(buf, sd->path, strlen(sd->path) + 1);
   if ((p = strrchr(path, '/'))) *p = 0;
   if (*path)
     e_fm2_path_set(obj, sd->dev, path);
   else if (sd->realpath)
     {
        path = ecore_file_dir_get(sd->realpath);
        e_fm2_path_set(obj, "/", path);
        free(path);
     }
}

E_API void
e_fm2_config_set(Evas_Object *obj, E_Fm2_Config *cfg)
{
   EFM_SMART_CHECK();
   if (sd->config == cfg) return;
   if (sd->config) _e_fm2_config_free(sd->config);
   sd->config = NULL;
   if (!cfg) return;
   sd->config = E_NEW(E_Fm2_Config, 1);
   if (!sd->config) return;
   memcpy(sd->config, cfg, sizeof(E_Fm2_Config));
   sd->config->icon.key_hint = eina_stringshare_add(cfg->icon.key_hint);
   sd->config->theme.background = eina_stringshare_add(cfg->theme.background);
   sd->config->theme.frame = eina_stringshare_add(cfg->theme.frame);
   sd->config->theme.icons = eina_stringshare_add(cfg->theme.icons);
}

E_API E_Fm2_Config *
e_fm2_config_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(NULL);
   return sd->config;
}

E_API Eina_List *
e_fm2_selected_list_get(Evas_Object *obj)
{
   Eina_List *list = NULL, *l;
   E_Fm2_Icon *ic;

   EFM_SMART_CHECK(NULL);
   EINA_LIST_FOREACH(sd->selected_icons, l, ic)
     list = eina_list_append(list, &(ic->info));
   return list;
}

E_API Eina_List *
e_fm2_all_list_get(Evas_Object *obj)
{
   Eina_List *list = NULL, *l;
   E_Fm2_Icon *ic;

   EFM_SMART_CHECK(NULL);
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        list = eina_list_append(list, &(ic->info));
     }
   return list;
}

E_API E_Fm2_Icon_Info *
e_fm2_icon_file_get(Evas_Object *obj, const char *file)
{
   Eina_List *l;
   E_Fm2_Icon *ic;

   EFM_SMART_CHECK(NULL);
   if (!file) return NULL;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if ((ic->info.file) && (!strcmp(ic->info.file, file)))
          return &(ic->info);
     }
   return NULL;
}

E_API void
e_fm2_deselect_all(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_icon_desel_any(obj);
}

E_API void
e_fm2_select_set(Evas_Object *obj, const char *file, int select_)
{
   Eina_List *l;
   E_Fm2_Icon *ic;

   EFM_SMART_CHECK();
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if ((file) && (!strcmp(ic->info.file, file)))
          {
             if (select_) _e_fm2_icon_select(ic);
             else _e_fm2_icon_deselect(ic);
          }
        else
          {
             if (ic->sd->config->selection.single)
               _e_fm2_icon_deselect(ic);
             ic->last_selected = EINA_FALSE;
          }
     }
}

E_API void
e_fm2_file_show(Evas_Object *obj, const char *file)
{
   Eina_List *l;
   E_Fm2_Icon *ic;

   EFM_SMART_CHECK();
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!strcmp(ic->info.file, file))
          {
             _e_fm2_icon_make_visible(ic);
             return;
          }
     }
}

E_API void
e_fm2_icon_menu_replace_callback_set(Evas_Object *obj, E_Fm_Cb func, void *data)
{
   EFM_SMART_CHECK();
   sd->icon_menu.replace.func = func;
   sd->icon_menu.replace.data = data;
}

E_API void
e_fm2_icon_menu_start_extend_callback_set(Evas_Object *obj, E_Fm_Cb func, void *data)
{
   EFM_SMART_CHECK();
   sd->icon_menu.start.func = func;
   sd->icon_menu.start.data = data;
}

E_API void
e_fm2_icon_menu_end_extend_callback_set(Evas_Object *obj, E_Fm_Cb func, void *data)
{
   EFM_SMART_CHECK();
   sd->icon_menu.end.func = func;
   sd->icon_menu.end.data = data;
}

E_API void
e_fm2_icon_menu_flags_set(Evas_Object *obj, E_Fm2_Menu_Flags flags)
{
   EFM_SMART_CHECK();
   sd->icon_menu.flags = flags;
}

E_API E_Fm2_Menu_Flags
e_fm2_icon_menu_flags_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(0);
   return sd->icon_menu.flags;
}

E_API void
e_fm2_view_flags_set(Evas_Object *obj, E_Fm2_View_Flags flags)
{
   EFM_SMART_CHECK();
   sd->view_flags = flags;
}

E_API E_Fm2_View_Flags
e_fm2_view_flags_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(0);
   return sd->view_flags;
}

E_API E_Object *
e_fm2_window_object_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(NULL);
   return sd->eobj;
}

E_API void
e_fm2_window_object_set(Evas_Object *obj, E_Object *eobj)
{
   const char *drop[] = {"text/uri-list", "text/x-moz-url", "XdndDirectSave0"};

   EFM_SMART_CHECK();
   sd->eobj = eobj;
   sd->win = NULL;
   if (sd->drop_handler) e_drop_handler_del(sd->drop_handler);
   sd->drop_handler = e_drop_handler_add(sd->eobj, NULL,
                                         sd,
                                         _e_fm2_cb_dnd_enter,
                                         _e_fm2_cb_dnd_move,
                                         _e_fm2_cb_dnd_leave,
                                         _e_fm2_cb_dnd_selection_notify,
                                         drop, 3,
                                         sd->x, sd->y, sd->w, sd->h);
   e_drop_handler_responsive_set(sd->drop_handler);
   e_drop_handler_xds_set(sd->drop_handler, _e_fm2_cb_dnd_drop);
}

E_API void
e_fm2_window_set(Evas_Object *obj, Evas_Object *win)
{
   const char *drop[] = {"text/uri-list", "text/x-moz-url", "XdndDirectSave0"};

   EFM_SMART_CHECK();
   sd->win = win;
   sd->eobj = NULL;
   if (sd->drop_handler) e_drop_handler_del(sd->drop_handler);
   sd->drop_handler = e_drop_handler_add(NULL, win,
                                         sd,
                                         _e_fm2_cb_dnd_enter,
                                         _e_fm2_cb_dnd_move,
                                         _e_fm2_cb_dnd_leave,
                                         _e_fm2_cb_dnd_selection_notify,
                                         drop, 3,
                                         sd->x, sd->y, sd->w, sd->h);
   e_drop_handler_responsive_set(sd->drop_handler);
   e_drop_handler_xds_set(sd->drop_handler, _e_fm2_cb_dnd_drop);
}

static void
_e_fm2_icons_update_helper(E_Fm2_Smart_Data *sd, Eina_Bool icon_only)
{
   Eina_List *l;
   E_Fm2_Icon *ic;
   char buf[PATH_MAX], *pfile;
   int bufused, buffree;

   if ((!sd->realpath) || (!sd->icons)) return;
   bufused = eina_strlcpy(buf, sd->realpath, sizeof(buf));
   if (bufused >= (int)(sizeof(buf) - 2))
     return;

   if ((bufused > 0) && (buf[bufused - 1] != '/'))
     {
        buf[bufused] = '/';
        bufused++;
     }

   pfile = buf + bufused;
   buffree = sizeof(buf) - bufused;

   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        E_Fm2_Custom_File *cf;

        eina_stringshare_del(ic->info.icon);
        ic->info.icon = NULL;
        ic->info.icon_type = EINA_FALSE;

        if (_e_fm2_file_is_desktop(ic->info.file))
          _e_fm2_icon_desktop_load(ic);

        if ((int)eina_strlcpy(pfile, ic->info.file, buffree) >= buffree)
          continue;

        cf = e_fm2_custom_file_get(buf);
        if (cf)
          {
             if (cf->icon.valid)
               {
                  eina_stringshare_replace(&ic->info.icon, cf->icon.icon);
                  ic->info.icon_type = cf->icon.type;
               }
          }

        if (!ic->realized) continue;
        if (icon_only)
          {
             E_FREE_FUNC(ic->obj_icon, evas_object_del);
             _e_fm2_icon_icon_set(ic);
          }
        else
          {
             _e_fm2_icon_unrealize(ic);
             _e_fm2_icon_realize(ic);
          }
     }
   e_fm2_custom_file_flush();
}

E_API void
e_fm2_icons_update(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_icons_update_helper(sd, EINA_FALSE);
}

E_API void
e_fm2_pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
{
   EFM_SMART_CHECK();
   x = MIN(x, sd->max.w - sd->w);
   if (x < 0) x = 0;
   y = MIN(y, sd->max.h - sd->h);
   if (y < 0) y = 0;
   if ((sd->pos.x == x) && (sd->pos.y == y)) return;
   sd->pos.x = x;
   sd->pos.y = y;
   if (sd->scroll_job) ecore_job_del(sd->scroll_job);
   sd->scroll_job = ecore_job_add(_e_fm2_cb_scroll_job, obj);
}

E_API void
e_fm2_pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
{
   EFM_SMART_CHECK();
   if (x) *x = sd->pos.x;
   if (y) *y = sd->pos.y;
}

E_API void
e_fm2_pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
{
   Evas_Coord mx, my;
   EFM_SMART_CHECK();
   mx = sd->max.w - sd->w;
   if (mx < 0) mx = 0;
   my = sd->max.h - sd->h;
   if (my < 0) my = 0;
   if (x) *x = mx;
   if (y) *y = my;
}

E_API void
e_fm2_pan_child_size_get(Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
{
   EFM_SMART_CHECK();
   if (w) *w = sd->max.w;
   if (h) *h = sd->max.h;
}

E_API void
e_fm2_all_icons_update(void)
{
   Evas_Object *o;
   const Eina_List *l;

   _e_fm2_list_walking++;
   EINA_LIST_FOREACH(_e_fm2_list, l, o)
     {
        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, o))) continue;
        e_fm2_icons_update(o);
     }
   _e_fm2_list_walking--;
   if (_e_fm2_list_walking == 0)
     {
        EINA_LIST_FREE(_e_fm2_list_remove, o)
          {
             _e_fm2_list = eina_list_remove(_e_fm2_list, o);
          }
     }
}

static const char *
_e_fm2_path_join(char *buf, int buflen, const char *base, const char *component)
{
   if ((!buf) || (!component))
     return NULL;

   if (component[0] == '/')
     return component;
   else if (component[0] == '\0')
     return base;
   else if (component[0] == '.')
     {
        if (component[1] == '/')
          {
             component += 2;

             if (!base)
               return component;

             if (snprintf(buf, buflen, "%s/%s", base, component) < buflen)
               return buf;
             else
               return NULL;
          }
        else if ((component[1] == '.') && (component[2] == '/'))
          {
             const char *p;
             int len;

             component += 3;

             if (!base)
               return component;

             p = strrchr(base, '/');
             if (!p)
               return component;

             len = p - base;
             if (snprintf(buf, buflen, "%.*s/%s", len, base, component) < buflen)
               return buf;
             else
               return NULL;
          }
     }

   if (snprintf(buf, buflen, "%s/%s", base, component) < buflen)
     return buf;
   else
     return NULL;
}

/**
 * Explicitly set an Edje icon from the given icon path.
 *
 * @param iconpath path to edje file that contains 'icon' group.
 *
 * @see _e_fm2_icon_explicit_get()
 */
static Evas_Object *
_e_fm2_icon_explicit_edje_get(Evas *evas, const E_Fm2_Icon *ic EINA_UNUSED, const char *iconpath, const char **type_ret)
{
   Evas_Object *o = edje_object_add(evas);
   if (!o)
     return NULL;

   if (!edje_object_file_set(o, iconpath, "icon"))
     {
        evas_object_del(o);
        return NULL;
     }

   if (type_ret) *type_ret = "CUSTOM";
   return o;
}

/**
 * Explicitly set icon from theme using its name.
 *
 * @param name will be prefixed by 'e/icons/' to form the group name in theme.
 *
 * @see e_util_edje_icon_set()
 * @see _e_fm2_icon_explicit_get()
 */
static Evas_Object *
_e_fm2_icon_explicit_theme_icon_get(Evas *evas, const E_Fm2_Icon *ic EINA_UNUSED, const char *name, const char **type_ret)
{
   Evas_Object *o = e_icon_add(evas);

   if (!o) return NULL;

   e_icon_scale_size_set(o, _e_fm2_icon_mime_size_normalize(ic));

   if (!e_util_icon_theme_set(o, name))
     {
        evas_object_del(o);
        return NULL;
     }

   if (type_ret) *type_ret = "THEME_ICON";
   return o;
}

/**
 * Explicitly set icon from file manager theem using its name.
 *
 * @param name will be prefixed with 'base/theme/fileman' to form the
 *    group name in theme.
 *
 * @see _e_fm2_theme_edje_icon_object_set()
 * @see _e_fm2_icon_explicit_get()
 */
static Evas_Object *
_e_fm2_icon_explicit_theme_get(Evas *evas, const E_Fm2_Icon *ic, const char *name, const char **type_ret)
{
   Evas_Object *o = edje_object_add(evas);
   if (!o)
     return NULL;

   if (!_e_fm2_theme_edje_icon_object_set(ic->sd, o, "base/theme/fileman", name))
     {
        evas_object_del(o);
        return NULL;
     }

   if (type_ret) *type_ret = "THEME";
   return o;
}

/**
 * Explicitly set icon to given value.
 *
 * This will try to identify if icon is an edje or regular file or even
 * an icon name to get from icon set.
 *
 * @param icon might be an absolute or relative path, or icon name or edje path.
 */
static Evas_Object *
_e_fm2_icon_explicit_get(Evas *evas, const E_Fm2_Icon *ic, const char *icon, const char **type_ret)
{
   char buf[PATH_MAX];
   const char *iconpath;

   iconpath = _e_fm2_path_join(buf, sizeof(buf), ic->sd->realpath, icon);
   if (!iconpath)
     {
        ERR("could not create icon \"%s\".", icon);
        return NULL;
     }

   if (_e_fm2_file_is_edje(iconpath))
     return _e_fm2_icon_explicit_edje_get(evas, ic, iconpath, type_ret);
   else
     {
        Evas_Object *o = e_icon_add(evas);
        if (!o)
          return NULL;

        e_icon_scale_size_set(o, _e_fm2_icon_mime_size_normalize(ic));
        e_icon_file_set(o, iconpath);
        e_icon_fill_inside_set(o, 1);
        if (type_ret) *type_ret = "CUSTOM";
        return o;
     }

   return NULL;
}

/**
 * Creates an icon that generates a thumbnail if required.
 *
 * @param group if given, will be used in e_thumb_icon_file_set()
 * @param cb function to callback when thumbnail generation is over.
 * @param data extra data to give to @p cb
 * @param force_gen whenever to force generation of thumbnails, even it exists.
 */
static Evas_Object *
_e_fm2_icon_thumb_get(Evas *evas, const E_Fm2_Icon *ic, const char *group, Evas_Smart_Cb cb, void *data, int force_gen, const char **type_ret)
{
   Evas_Object *o;
   char buf[PATH_MAX];

   if (ic->thumb_failed)
     return NULL;

   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf)))
     return NULL;

   o = e_thumb_icon_add(evas);
   if (!o)
     return NULL;

   e_thumb_icon_file_set(o, buf, group);
   e_thumb_icon_size_set(o, 128, 128);

   if (cb) evas_object_smart_callback_add(o, "e_thumb_gen", cb, data);

   _e_fm2_icon_thumb(ic, o, force_gen);
   if (type_ret) *type_ret = "THUMB";
   return o;
}

/**
 * Generates the thumbnail of the given edje file.
 *
 * It will use 'icon.key_hint' from config if set and then try some well
 * known groups like 'icon', 'e/desktop/background' and 'e/init/splash'.
 */
static Evas_Object *
_e_fm2_icon_thumb_edje_get(Evas *evas, const E_Fm2_Icon *ic, Evas_Smart_Cb cb, void *data, int force_gen, const char **type_ret)
{
   char buf[PATH_MAX];
   const char **itr = NULL, *group = NULL;
   const char *known_groups[] = {
      NULL,
      "e/desktop/background",
      "icon",
      "e/init/splash",
      /* XXX TODO: add more? example 'screenshot', 'preview' */
      NULL
   };
   Evas_Object *o;

   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf))) return NULL;

   known_groups[0] = ic->sd->config->icon.key_hint;
   if (known_groups[0]) itr = known_groups;
   else itr = known_groups + 1;

   for (; *itr; itr++)
     {
        if (edje_file_group_exists(buf, *itr)) break;
     }

   if (*itr)
     group = eina_stringshare_add(*itr);
   else
     {
        Eina_List *l;

        l = edje_file_collection_list(buf);
        group = eina_stringshare_ref(eina_list_data_get(l));
        edje_file_collection_list_free(l);
     }
   if (!group) return NULL;
   o = _e_fm2_icon_thumb_get(evas, ic, group, cb, data, force_gen, type_ret);
   eina_stringshare_del(group);
   return o;
}

static Eina_Bool
_e_fm2_icon_cache_update(void *data, int type EINA_UNUSED, void *event EINA_UNUSED)
{
   _e_fm2_icons_update_helper(data, EINA_TRUE);
   return ECORE_CALLBACK_RENEW;
}

/**
 * Machinery for _e_fm2_icon_desktop_get() and others with instances of desktop.
 */
static Evas_Object *
_e_fm2_icon_desktop_get_internal(Evas *evas, const E_Fm2_Icon *ic, Efreet_Desktop *desktop, const char **type_ret)
{
   Evas_Object *o;

   if (!desktop->icon)
     return NULL;

   if (_e_fm2_file_is_edje(desktop->icon))
     return _e_fm2_icon_explicit_edje_get(evas, ic, desktop->icon, type_ret);

   o = _e_fm2_icon_explicit_theme_icon_get(evas, ic, desktop->icon, type_ret);
   if (o) return o;

   o = e_util_desktop_icon_add(desktop, 48, evas);
//   o = e_util_icon_theme_icon_add(desktop->icon, 48, evas);
   if (o && type_ret) *type_ret = "DESKTOP";
   return o;
}

/**
 * Use freedesktop.org '.desktop' files to set icon.
 */
static Evas_Object *
_e_fm2_icon_desktop_get(Evas *evas, const E_Fm2_Icon *ic, const char **type_ret)
{
   Efreet_Desktop *ef;
   Evas_Object *o;
   char buf[PATH_MAX];

   if (!ic->info.file) return NULL;

   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf)))
     return NULL;

   ef = efreet_desktop_new(buf);
   if (!ef) return NULL;

   o = _e_fm2_icon_desktop_get_internal(evas, ic, ef, type_ret);
   efreet_desktop_free(ef);
   return o;
}

static inline const char *
_e_fm2_icon_mime_type_special_match(const E_Fm2_Icon *ic)
{
   const Eina_List *l;
   const E_Config_Mime_Icon *mi;
   const char *mime = ic->info.mime;

   EINA_LIST_FOREACH(e_config->mime_icons, l, mi)
     if (mi->mime == mime) /* both in the same stringshare pool */
       return mi->icon;

   return NULL;
}

static Evas_Object *
_e_fm2_icon_mime_fdo_get(Evas *evas, const E_Fm2_Icon *ic, const char **type_ret)
{
   const char *icon;
   unsigned int size;

   size = _e_fm2_icon_mime_size_normalize(ic);
   icon = efreet_mime_type_icon_get(ic->info.mime, e_config->icon_theme, size);
   if (icon)
     return _e_fm2_icon_explicit_get(evas, ic, icon, type_ret);
   return NULL;
}

static Evas_Object *
_e_fm2_icon_mime_theme_get(Evas *evas, const E_Fm2_Icon *ic, const char **type_ret EINA_UNUSED)
{
   char buf[1024];
   const char *file;

   if (snprintf(buf, sizeof(buf), "e/icons/fileman/mime/%s", ic->info.mime) >=
       (int)sizeof(buf))
     return NULL;

   file = e_theme_edje_file_get("base/theme/icons", buf);
   if (file && file[0])
     {
        Evas_Object *o = edje_object_add(evas);
        if (!o) return NULL;
        if (!edje_object_file_set(o, file, buf))
          {
             evas_object_del(o);
             return NULL;
          }
        return o;
     }

   return NULL;
}

/**
 * Use mime type information to set icon.
 */
static Evas_Object *
_e_fm2_icon_mime_get(Evas *evas, const E_Fm2_Icon *ic, Evas_Smart_Cb gen_func EINA_UNUSED, void *data EINA_UNUSED, int force_gen EINA_UNUSED, const char **type_ret)
{
   Evas_Object *o = NULL;

   if (e_config->icon_theme_overrides)
     o = _e_fm2_icon_mime_fdo_get(evas, ic, type_ret);
   else
     o = _e_fm2_icon_mime_theme_get(evas, ic, type_ret);

   if (o) return o;

   if (!e_config->icon_theme_overrides)
     o = _e_fm2_icon_mime_fdo_get(evas, ic, type_ret);
   else
     o = _e_fm2_icon_mime_theme_get(evas, ic, type_ret);

   return o;
}

/**
 * Discovers the executable of Input Method Config file and set icon.
 */
static Evas_Object *
_e_fm2_icon_imc_get(Evas *evas, const E_Fm2_Icon *ic, const char **type_ret)
{
   E_Input_Method_Config *imc;
   Efreet_Desktop *desktop;
   Eet_File *imc_ef;
   Evas_Object *o = NULL;
   char buf[PATH_MAX];

   if (!ic->info.file)
     return NULL;

   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf)))
     return NULL;

   imc_ef = eet_open(buf, EET_FILE_MODE_READ);
   if (!imc_ef)
     return NULL;

   imc = e_intl_input_method_config_read(imc_ef);
   eet_close(imc_ef);

   if (!imc->e_im_setup_exec)
     {
        e_intl_input_method_config_free(imc);
        return NULL;
     }

   desktop = efreet_util_desktop_exec_find(imc->e_im_setup_exec);
   if (desktop)
     {
        o = _e_fm2_icon_desktop_get_internal(evas, ic, desktop, type_ret);
        efreet_desktop_free(desktop);
     }
   e_intl_input_method_config_free(imc);

   if ((o) && (type_ret)) *type_ret = "IMC";
   return o;
}

/**
 * Use heuristics to discover and set icon.
 */
static Evas_Object *
_e_fm2_icon_discover_get(Evas *evas, const E_Fm2_Icon *ic, Evas_Smart_Cb gen_func, void *data, int force_gen, const char **type_ret)
{
   const char *p;

   p = strrchr(ic->info.file, '.');
   if (!p) return NULL;

   p++;
   if (_e_fm2_ext_is_edje(p))
     return _e_fm2_icon_thumb_edje_get(evas, ic, gen_func,
                                       data, force_gen, type_ret);
   if (_e_fm2_ext_is_desktop(p))
     return _e_fm2_icon_desktop_get(evas, ic, type_ret);
   if (_e_fm2_ext_is_imc(p))
     return _e_fm2_icon_imc_get(evas, ic, type_ret);
   if (evas_object_image_extension_can_load_get(p - 1))
     return _e_fm2_icon_thumb_get(evas, ic, NULL, gen_func, data, force_gen, type_ret);
   return NULL;
}

/**
 * Get the object representing the icon.
 *
 * @param evas canvas instance to use to store the icon.
 * @param ic icon to get information in order to find the icon.
 * @param gen_func if thumbnails need to be generated, call this function
 *     when it's over.
 * @param data extra data to give to @p gen_func.
 * @param force_gen force thumbnail generation.
 * @param type_ret string that identifies type of icon.
 */
E_API Evas_Object *
e_fm2_icon_get(Evas *evas, E_Fm2_Icon *ic,
               Evas_Smart_Cb gen_func,
               void *data, int force_gen, const char **type_ret)
{
   const char *icon;
   Evas_Object *o = NULL;

   if (ic->info.icon)
     {
        if ((ic->info.icon[0] == '/') ||
            ((ic->info.icon[0] == '.') &&
             ((ic->info.icon[1] == '/') ||
              ((ic->info.icon[1] == '.') && (ic->info.icon[2] == '/')))))
          {
             o = _e_fm2_icon_explicit_get(evas, ic, ic->info.icon, type_ret);
             if (o) return o;
          }

        o = _e_fm2_icon_explicit_theme_icon_get(evas, ic, ic->info.icon, type_ret);
        if (o) return o;
     }

   if (ic->sd->config->icon.max_thumb_size && (ic->info.statinfo.st_size > ic->sd->config->icon.max_thumb_size * 1024 * 1024))
     ic->thumb_failed = EINA_TRUE;

   /* create thumbnails for edje files */
   if ((ic->info.file) && (_e_fm2_file_is_edje(ic->info.file)))
     {
        o = _e_fm2_icon_thumb_edje_get
            (evas, ic, gen_func, data, force_gen, type_ret);
        if (o) return o;
     }

   /* disabled until everyone has edje in mime.types:
    *  use mimetype to identify edje.
    * if (ic->info.mime == _e_fm2_mime_app_edje)
    *   return _e_fm2_icon_thumb_edje_get
    *     (evas, ic, gen_func, data, force_gen, type_ret); */

   /* check user preferences */
   icon = _e_fm2_icon_mime_type_special_match(ic);
   if (icon)
     {
        if (icon == _e_fm2_icon_desktop_str)
          o = _e_fm2_icon_desktop_get(evas, ic, type_ret);
        else if (icon == _e_fm2_icon_thumb_str)
          {
             if (!ic->thumb_failed)
               o = _e_fm2_icon_thumb_get
                   (evas, ic, NULL, gen_func, data, force_gen, type_ret);
          }
        else if (strncmp(icon, "e/icons/fileman/", 16) == 0)
          o = _e_fm2_icon_explicit_theme_get(evas, ic, icon + 16, type_ret);
        else
          o = _e_fm2_icon_explicit_get(evas, ic, icon, type_ret);

        if (o) return o;
     }

   if ((!ic->thumb_failed) && (ic->info.icon_type == 1))
     {
        o = _e_fm2_icon_thumb_get(evas, ic, NULL,
                                  gen_func, data, force_gen, type_ret);
        if (o) return o;
     }

   if (ic->info.file)
     {
        o = _e_fm2_icon_discover_get(evas, ic, gen_func, data,
                                     force_gen, type_ret);
        if (o) return o;
     }
   if (ic->info.mime)
     {
        o = _e_fm2_icon_mime_get(evas, ic, gen_func, data, force_gen, type_ret);
        if (o) return o;
     }

//fallback:
   o = _e_fm2_icon_explicit_theme_icon_get(evas, ic, "unknown", type_ret);
   if (o) return o;

   return _e_fm2_icon_explicit_theme_get(evas, ic, "text/plain", type_ret);
}

E_API E_Fm2_Icon_Info *
e_fm2_icon_file_info_get(E_Fm2_Icon *ic)
{
   if (!ic) return NULL;
   return &(ic->info);
}

E_API void
e_fm2_icon_geometry_get(E_Fm2_Icon *ic, int *x, int *y, int *w, int *h)
{
   int xx, yy, ww, hh;

   if (x) *x = 0;
   if (y) *y = 0;
   if (w) *w = 0;
   if (h) *h = 0;

   if (ic)
     {
        evas_object_geometry_get(ic->obj, &xx, &yy, &ww, &hh);
        if (x) *x = xx;
        if (y) *y = yy;
        if (w) *w = ww;
        if (h) *h = hh;
     }
}

/* FIXME: track real exe with exe del events etc. */
static int _e_fm2_client_spawning = 0;

static void
_e_fm2_client_spawn(void)
{
   char buf[4096];

   if (_e_fm2_client_spawning) return;
   if (e_sys_on_the_way_out_get()) return;
   snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_fm", e_prefix_lib_get());
   if (_e_fm2_exe) e_fm2_die();
   _e_fm2_exe = ecore_exe_pipe_run(buf, ECORE_EXE_NOT_LEADER | ECORE_EXE_TERM_WITH_PARENT, NULL);
   _e_fm2_client_spawning = 1;
}

static E_Fm2_Client *
_e_fm2_client_get(void)
{
   Eina_List *l;
   E_Fm2_Client *cl, *cl_chosen = NULL;
   int min_req = 0x7fffffff;

   /* if we don't have a slave - spawn one */
   if (!_e_fm2_client_list)
     {
        _e_fm2_client_spawn();
        return NULL;
     }
   EINA_LIST_FOREACH(_e_fm2_client_list, l, cl)
     {
        if (cl->req < min_req)
          {
             min_req = cl->req;
             cl_chosen = cl;
          }
     }
   return cl_chosen;
}

typedef struct _E_Fm2_Message E_Fm2_Message;

struct _E_Fm2_Message
{
   int   major, minor, ref, ref_to, response;
   void *data;
   int   size;
};

static Eina_List *_e_fm2_messages = NULL;

static void
_e_fm2_client_message_queue(int major, int minor, int ref, int ref_to, int response, const void *data, int size)
{
   E_Fm2_Message *msg;

   msg = E_NEW(E_Fm2_Message, 1);
   if (!msg) return;
   msg->major = major;
   msg->minor = minor;
   msg->ref = ref;
   msg->ref_to = ref_to;
   msg->response = response;
   if (data)
     {
        msg->size = size;
        msg->data = malloc(size);
        if (msg->data)
          memcpy(msg->data, data, size);
        else
          {
             free(msg);
             return;
          }
     }
   _e_fm2_messages = eina_list_append(_e_fm2_messages, msg);
}

static void
_e_fm2_client_message_flush(E_Fm2_Client *cl, E_Fm2_Message *msg)
{
   _e_fm2_messages = eina_list_remove(_e_fm2_messages, msg);
   ecore_ipc_client_send(cl->cl, msg->major, msg->minor,
                         msg->ref, msg->ref_to, msg->response,
                         msg->data, msg->size);
   cl->req++;
   free(msg->data);
   free(msg);
}

static void
_e_fm2_client_messages_flush(void)
{
   while (_e_fm2_messages)
     {
        E_Fm2_Client *cl;

        cl = _e_fm2_client_get();
        if (!cl) break;
        _e_fm2_client_message_flush(cl, eina_list_data_get(_e_fm2_messages));
     }
}

static int
_e_fm_client_send_new(int minor, void *data, int size)
{
   static int id = 0;
   E_Fm2_Client *cl;

   id++;
   cl = _e_fm2_client_get();
   if (!cl)
     {
        _e_fm2_client_message_queue(E_IPC_DOMAIN_FM, minor,
                                    id, 0, 0,
                                    data, size);
     }
   else
     {
        ecore_ipc_client_send(cl->cl, E_IPC_DOMAIN_FM, minor,
                              id, 0, 0,
                              data, size);
        cl->req++;
     }

   return id;
}

static int
_e_fm_client_send(int minor, int id, void *data, int size)
{
   E_Fm2_Client *cl;

   cl = _e_fm2_client_get();
   if (!cl)
     {
        _e_fm2_client_message_queue(E_IPC_DOMAIN_FM, minor,
                                    id, 0, 0,
                                    data, size);
     }
   else
     {
        ecore_ipc_client_send(cl->cl, E_IPC_DOMAIN_FM, minor,
                              id, 0, 0,
                              data, size);
        cl->req++;
     }

   return id;
}

static int
_e_fm2_client_monitor_add(const char *path)
{
   return _e_fm_client_send_new(E_FM_OP_MONITOR_START, (void *)path, strlen(path) + 1);
}

static void
_e_fm2_client_monitor_del(int id, const char *path)
{
   _e_fm_client_send(E_FM_OP_MONITOR_END, id, (void *)path, strlen(path) + 1);
}

static int
_e_fm_client_file_del(const char *files, Eina_Bool secure, Evas_Object *e_fm)
{
   int id, op = E_FM_OP_REMOVE;

   if (secure) op = E_FM_OP_SECURE_REMOVE;

   id = _e_fm_client_send_new(op, (void *)files, strlen(files) + 1);
   e_fm2_op_registry_entry_add(id, e_fm, op, _e_fm2_operation_abort_internal);
   return id;
}

#if 0
static int
_e_fm2_client_file_trash(const char *path, Evas_Object *e_fm)
{
   int id = _e_fm_client_send_new(E_FM_OP_TRASH, (void *)path, strlen(path) + 1);
   e_fm2_op_registry_entry_add(id, e_fm, E_FM_OP_TRASH, _e_fm2_operation_abort_internal);
   return id;
}

static int
_e_fm2_client_file_mkdir(const char *path, const char *rel, int rel_to, int x, int y, int res_w EINA_UNUSED, int res_h EINA_UNUSED, Evas_Object *e_fm)
{
   char *d;
   int l1, l2, l, id;

   l1 = strlen(path);
   l2 = strlen(rel);
   l = l1 + 1 + l2 + 1 + (sizeof(int) * 3);
   d = alloca(l);
   strcpy(d, path);
   strcpy(d + l1 + 1, rel);
   memcpy(d + l1 + 1 + l2 + 1, &rel_to, sizeof(int));
   memcpy(d + l1 + 1 + l2 + 1 + sizeof(int), &x, sizeof(int));
   memcpy(d + l1 + 1 + l2 + 1 + (2 * sizeof(int)), &y, sizeof(int));

   id = _e_fm_client_send_new(E_FM_OP_MKDIR, (void *)d, l);
   e_fm2_op_registry_entry_add(id, e_fm, E_FM_OP_MKDIR, _e_fm2_operation_abort_internal);
   return id;
}

#endif

E_API int
e_fm2_client_file_move(Evas_Object *e_fm, const char *args)
{
   int id;
   E_Fm_Op_Type op = e_config->filemanager_copy ? E_FM_OP_MOVE : E_FM_OP_RENAME;

   id = _e_fm_client_send_new(op, (void *)args, strlen(args) + 1);
   e_fm2_op_registry_entry_add(id, e_fm, op, _e_fm2_operation_abort_internal);
   return id;
}

E_API int
e_fm2_client_file_copy(Evas_Object *e_fm, const char *args)
{
   int id = _e_fm_client_send_new(E_FM_OP_COPY, (void *)args, strlen(args) + 1);
   e_fm2_op_registry_entry_add(id, e_fm, E_FM_OP_COPY, _e_fm2_operation_abort_internal);
   return id;
}

E_API int
e_fm2_client_file_symlink(Evas_Object *e_fm, const char *args)
{
   int id = _e_fm_client_send_new(E_FM_OP_SYMLINK, (void *)args, strlen(args) + 1);
   e_fm2_op_registry_entry_add(id, e_fm, E_FM_OP_SYMLINK, _e_fm2_operation_abort_internal);
   return id;
}

E_API int
_e_fm2_client_mount(const char *udi, const char *mountpoint)
{
   char *d;
   int l, l1, l2 = 0;

   if (!udi)
     return 0;

   l1 = strlen(udi);
   if (mountpoint)
     {
        l2 = strlen(mountpoint);
        l = l1 + 1 + l2 + 1;
     }
   else
     l = l1 + 1;
   d = alloca(l);
   strcpy(d, udi);
   if (mountpoint)
     strcpy(d + l1 + 1, mountpoint);

   return _e_fm_client_send_new(E_FM_OP_MOUNT, (void *)d, l);
}

E_API int
_e_fm2_client_unmount(const char *udi)
{
   char *d;
   int l, l1;

   if (!udi)
     return 0;

   l1 = strlen(udi);
   l = l1 + 1;
   d = alloca(l);
   strcpy(d, udi);

   _e_fm2_client_get();

   elm_cache_all_flush();
   edje_file_cache_flush();
   edje_collection_cache_flush();
   if (e_comp)
     {
        evas_image_cache_flush(e_comp->evas);
        evas_font_cache_flush(e_comp->evas);
     }

   return _e_fm_client_send_new(E_FM_OP_UNMOUNT, (void *)d, l);
}

E_API int
_e_fm2_client_eject(const char *udi)
{
   char *data;
   int size;

   if (!udi)
     return 0;

   size = strlen(udi) + 1;
   data = alloca(size);
   strcpy(data, udi);

   return _e_fm_client_send_new(E_FM_OP_EJECT, data, size);
}

static void
_e_fm2_client_monitor_list_end(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   sd->busy_count--;
   if (sd->busy_count == 0)
     {
        edje_object_signal_emit(sd->overlay, "e,state,busy,stop", "e");
        e_fm2_custom_file_flush();
     }
   if (sd->tmp.obj)
     {
        evas_object_del(sd->tmp.obj);
        sd->tmp.obj = NULL;
     }
   if (sd->tmp.obj2)
     {
        evas_object_del(sd->tmp.obj2);
        sd->tmp.obj2 = NULL;
     }
   if (sd->scan_timer)
     {
        ecore_timer_del(sd->scan_timer);
        sd->scan_timer = NULL;
     }
   if (sd->sort_idler)
     {
        ecore_idler_del(sd->sort_idler);
        sd->sort_idler = NULL;
     }
   E_FREE(sd->tmp.list_index);
   _e_fm2_queue_free(obj);
   _e_fm2_obj_icons_place(sd);
   _e_fm2_live_process_begin(obj);
}

E_API void
_e_fm2_file_force_update(const char *path)
{
   Evas_Object *o;
   char *dir;
   Eina_List *l;

   dir = ecore_file_dir_get(path);
   if (!dir) return;
   EINA_LIST_FOREACH(_e_fm2_list, l, o)
     {
        const char *rp;
        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, o))) continue;
        rp = e_fm2_real_path_get(o);
        if (!rp) continue;
        if (!strcmp(rp, dir))
          {
             E_Fm2_Icon *ic;

             ic = _e_fm2_icon_find(o, ecore_file_file_get(path));
             if (ic)
               {
                  E_Fm2_Finfo finf;

                  memset(&finf, 0, sizeof(E_Fm2_Finfo));
                  memcpy(&(finf.st), &(ic->info.statinfo),
                         sizeof(struct stat));
                  finf.broken_link = ic->info.broken_link;
                  finf.lnk = ic->info.link;
                  finf.rlnk = ic->info.real_link;
                  ic->removable_state_change = EINA_TRUE;
                  _e_fm2_live_file_changed(o, ecore_file_file_get(path),
                                           &finf);
               }
          }
     }
   free(dir);
}

E_API void
e_fm2_client_data(Ecore_Ipc_Event_Client_Data *e)
{
   Evas_Object *obj;
   Eina_List *l, *dels = NULL;
   E_Fm2_Client *cl;

   if (e->major != 6 /*E_IPC_DOMAIN_FM*/) return;
   EINA_LIST_FOREACH(_e_fm2_client_list, l, cl)
     {
        if (cl->cl == e->client) break;
     }
   if (!l)
     {
        cl = E_NEW(E_Fm2_Client, 1);
        cl->cl = e->client;
        _e_fm2_client_list = eina_list_prepend(_e_fm2_client_list, cl);
        /* FIXME: new client - send queued msgs */
        _e_fm2_client_spawning = 0;
        _e_fm2_client_messages_flush();
     }

   _e_fm2_list_walking++;
   EINA_LIST_FOREACH(_e_fm2_list, l, obj)
     {
        unsigned char *p;
        char *evdir;
        const char *dir, *path, *lnk, *rlnk, *file;
        struct stat st;
        int broken_link;
        E_Fm2_Smart_Data *sd;

        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, obj))) continue;
        dir = e_fm2_real_path_get(obj);
        sd = evas_object_smart_data_get(obj);
        switch (e->minor)
          {
           case E_FM_OP_HELLO: /*hello*/
//             printf("E_FM_OP_HELLO\n");
             break;

           case E_FM_OP_OK: /*req ok*/
//             printf("E_FM_OP_OK\n");
             cl->req--;
             break;

           case E_FM_OP_FILE_ADD: /*file add*/
//             printf("E_FM_OP_FILE_ADD\n");
           case E_FM_OP_FILE_CHANGE: /*file change*/
//             printf("E_FM_OP_FILE_CHANGE\n");
           {
              E_Fm2_Finfo finf;

              p = e->data;
              /* NOTE: i am NOT converting this data to portable arch/os independent
               * format. i am ASSUMING e_fm_main and e are local and built together
               * and thus this will work. if this ever changes this here needs to
               * change */
              memcpy(&st, p, sizeof(struct stat));
              p += sizeof(struct stat);

              broken_link = p[0];
              p += 1;

              path = (char *)p;
              p += strlen(path) + 1;

              lnk = (char *)p;
              p += strlen(lnk) + 1;

              rlnk = (char *)p;

              memcpy(&(finf.st), &st, sizeof(struct stat));
              finf.broken_link = broken_link;
              finf.lnk = lnk;
              finf.rlnk = rlnk;

              evdir = ecore_file_dir_get(path);
              if ((evdir) && (sd->id == e->ref_to) &&
                  ((!strcmp(evdir, "") || ((dir) && (!strcmp(dir, evdir))))))
                {
//                       printf(" ch/add response = %i\n", e->response);
                   free(evdir);
                   if (e->response == 0)    /*live changes*/
                     {
                        if (e->minor == E_FM_OP_FILE_ADD)    /*file add*/
                          {
                             _e_fm2_live_file_add
                               (obj, ecore_file_file_get(path),
                               NULL, 0, &finf);
                             break;
                          }
                        if (e->minor == E_FM_OP_FILE_CHANGE)    /*file change*/
                          {
                             _e_fm2_live_file_changed
                               (obj, (char *)ecore_file_file_get(path),
                               &finf);
                          }
                        break;
                     }
                   /*file add - listing*/
                   if (e->minor == E_FM_OP_FILE_ADD)    /*file add*/
                     {
                        if (!sd->scan_timer)
                          {
                             sd->scan_timer =
                               ecore_timer_loop_add(0.5,
                                               _e_fm2_cb_scan_timer,
                                               sd->obj);
                             sd->busy_count++;
                             if (sd->busy_count == 1)
                               edje_object_signal_emit(sd->overlay, "e,state,busy,start", "e");
                          }
                        else
                          {
                             if ((eina_list_count(sd->icons) > 50) && (ecore_timer_interval_get(sd->scan_timer) < 1.5))
                               {
                                  /* increase timer interval when loading large directories to
                                   * dramatically improve load times
                                   */
                                  ecore_timer_interval_set(sd->scan_timer, 1.5);
                                  ecore_timer_loop_reset(sd->scan_timer);
                               }
                          }
                        if (path[0] != 0)
                          {
                             file = ecore_file_file_get(path);
                             if ((!strcmp(file, ".order")))
                               sd->order_file = EINA_TRUE;
                             else
                               {
                                  unsigned int n;

                                  n = eina_list_count(sd->queue) + eina_list_count(sd->icons);
                                  if (!((file[0] == '.') &&
                                        (!sd->show_hidden_files)))
                                    {
                                       char buf[1024];

                                       _e_fm2_file_add(obj, file,
                                                       sd->order_file,
                                                       NULL, 0, &finf);
                                       if (n - sd->overlay_count > 150)
                                         {
                                            sd->overlay_count = n + 1;
                                            snprintf(buf, sizeof(buf), P_("%u file", "%u files", sd->overlay_count), sd->overlay_count);
                                            edje_object_part_text_set(sd->overlay, "e.text.busy_label", buf);
                                         }
                                    }
                               }
                          }
                        if (e->response == 2)    /* end of scan */
                          {
                             sd->listing = EINA_FALSE;
                             if (sd->scan_timer)
                               {
                                  ecore_timer_interval_set(sd->scan_timer, 0.0001);
                                  ecore_timer_loop_reset(sd->scan_timer);
                               }
                             else
                               {
                                  _e_fm2_client_monitor_list_end(obj);
                               }
                          }
                     }
                   break;
                }
              free(evdir);
//                       printf(" ...\n");
              if ((sd->id != e->ref_to) || (path[0] != 0)) break;
//                            printf(" end response = %i\n", e->response);
              if (e->response == 2)     /* end of scan */
                {
                   sd->listing = EINA_FALSE;
                   if (sd->scan_timer)
                     {
                        ecore_timer_del(sd->scan_timer);
                        sd->scan_timer =
                          ecore_timer_loop_add(0.0001,
                                          _e_fm2_cb_scan_timer,
                                          sd->obj);
                     }
                   else
                     {
                        _e_fm2_client_monitor_list_end(obj);
                     }
                }
           }
           break;

           case E_FM_OP_FILE_DEL: /*file del*/
//             printf("E_FM_OP_FILE_DEL\n");
             path = e->data;
             evdir = ecore_file_dir_get(path);
             if ((dir) && (evdir) && (sd->id == e->ref_to) && (!strcmp(dir, evdir)))
               {
                  _e_fm2_live_file_del
                    (obj, ecore_file_file_get(path));
               }
             free(evdir);
             break;

           case E_FM_OP_MONITOR_END: /*mon dir del*/
//             printf("E_FM_OP_MONITOR_END\n");
             path = e->data;
             /* FIXME dir should not be null but can. fix segv
                when mounting cdrom with udisk here
                btw monitor end seems to be a strange event for
                mounting disks.
              */
             if ((dir) && (path) && (sd->id == e->ref_to) && (!strcmp(dir, path)))
               {
                  dels = eina_list_append(dels, obj);
               }
             break;

           default:
             break;
          }
     }
   EINA_LIST_FREE(dels, obj)
     {
        E_Fm2_Smart_Data *sd;

        sd = evas_object_smart_data_get(obj);
        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, obj))) continue;
        if (sd->config->view.open_dirs_in_place)
          _e_fm2_path_parent_set(obj, sd->realpath);
        else
          evas_object_smart_callback_call(obj, "dir_deleted", NULL);
     }
   _e_fm2_list_walking--;
   if (_e_fm2_list_walking == 0)
     {
        EINA_LIST_FREE(_e_fm2_list_remove, obj)
          {
             _e_fm2_list = eina_list_remove(_e_fm2_list, obj);
          }
     }
   switch (e->minor)
     {
      case E_FM_OP_INIT:
        e_config->device_detect_mode = strtoul((char*)e->data, NULL, 10);
        break;
      case E_FM_OP_MONITOR_SYNC:  /*mon list sync*/
        ecore_ipc_client_send(cl->cl, E_IPC_DOMAIN_FM, E_FM_OP_MONITOR_SYNC,
                              0, 0, e->response,
                              NULL, 0);
        break;

      case E_FM_OP_STORAGE_ADD:  /*storage add*/
        if ((e->data) && (e->size > 0))
          {
             E_Storage *s;

             s = _e_fm_shared_codec_storage_decode(e->data, e->size);
             if (s) e_fm2_device_storage_add(s);
          }
        break;

      case E_FM_OP_STORAGE_DEL:  /*storage del*/
        if ((e->data) && (e->size > 0))
          {
             char *udi;
             E_Storage *s;

             udi = e->data;
             s = e_fm2_device_storage_find(udi);
             if (s) e_fm2_device_storage_del(s);
          }
        break;

      case E_FM_OP_VOLUME_LIST_DONE:
        e_fm2_device_check_desktop_icons();
        break;

      case E_FM_OP_VOLUME_ADD:  /*volume add*/
        if ((e->data) && (e->size > 0))
          {
             E_Volume *v;

             v = _e_fm_shared_codec_volume_decode(e->data, e->size);
             if (!v) break;
             e_config->device_detect_mode = v->efm_mode;
             e_fm2_device_volume_add(v);
             if (v->mounted)
               e_fm2_device_mount(v, NULL, NULL, NULL, NULL, NULL);
             else if ((!starting) && e_config->device_auto_mount && v->first_time)
               {
                  v->auto_unmount = !v->mounted;
                  _e_fm2_client_mount(v->udi, v->mount_point);
               }
             if (e_config->device_desktop)
               e_fm2_device_show_desktop_icons();
             else
               e_fm2_device_hide_desktop_icons();
             v->first_time = 0;
          }
        break;

      case E_FM_OP_VOLUME_DEL:  /*volume del*/
        if ((e->data) && (e->size > 0))
          {
             char *udi;
             E_Volume *v;

             udi = e->data;
             v = e_fm2_device_volume_find(udi);
             if (v) e_fm2_device_volume_del(v);
          }
        break;

      case E_FM_OP_MOUNT_DONE:  /*mount done*/
        if ((e->data) && (e->size > 1))
          {
             E_Volume *v;
             char *udi, *mountpoint = NULL;

             udi = e->data;
             if ((unsigned int)e->size != (strlen(udi) + 1))
               mountpoint = udi + strlen(udi) + 1;
             v = e_fm2_device_volume_find(udi);
             if (v)
               {
                  e_fm2_device_mount_add(v, mountpoint);
                  _e_fm2_volume_icon_update(v);
                  if (e_config->device_auto_open && !eina_list_count(v->mounts))
                    {
                       E_Action *a;
                       unsigned int count;

                       a = e_action_find("fileman");
                       count = eina_list_count(_e_fm2_list);
                       if (a && a->func.go && mountpoint)
                         {
                            Evas_Object *fm;
                            Eina_List *m;
                            Eina_Bool auto_unmount = v->auto_unmount;

                            a->func.go(E_OBJECT(e_comp), mountpoint);
                            if (count == eina_list_count(_e_fm2_list)) break;
                            EINA_LIST_REVERSE_FOREACH(_e_fm2_list, m, fm)
                              {
                                 E_Fm2_Smart_Data *sd = evas_object_smart_data_get(fm);
                                 if (e_util_strcmp(sd->realpath, mountpoint)) continue;
                                 if (sd->mount) break;
                                 sd->mount = e_fm2_device_mount(v, _e_fm2_cb_mount_ok,
                                                                _e_fm2_cb_mount_fail,
                                                                _e_fm2_cb_unmount_ok, NULL, fm);
                                 v->auto_unmount = auto_unmount;
                                 break;
                              }
                         }
                    }
               }
          }
        break;

      case E_FM_OP_UNMOUNT_DONE:  /*unmount done*/
        if ((e->data) && (e->size > 1))
          {
             E_Volume *v;
             char *udi;

             udi = e->data;
             v = e_fm2_device_volume_find(udi);
             if (v)
               {
                  e_fm2_device_mount_del(v);
                  _e_fm2_volume_icon_update(v);
               }
          }
        break;

      case E_FM_OP_EJECT_DONE:
        break;

      case E_FM_OP_MOUNT_ERROR:  /*mount error*/
        if (e->data && (e->size > 1))
          {
             E_Volume *v;
             char *udi;

             udi = e->data;
             v = e_fm2_device_volume_find(udi);
             if (v)
               {
                  _e_fm_device_error_dialog(_("Mount Error"), _("Can't mount device"), e->data);
                  e_fm2_device_mount_fail(v);
               }
          }
        break;

      case E_FM_OP_UNMOUNT_ERROR:  /*unmount error*/
        if (e->data && (e->size > 1))
          {
             E_Volume *v;
             char *udi;

             udi = e->data;
             v = e_fm2_device_volume_find(udi);
             if (v)
               {
                  _e_fm_device_error_dialog(_("Unmount Error"), _("Can't unmount device"), e->data);
                  e_fm2_device_unmount_fail(v);
               }
          }
        break;

      case E_FM_OP_EJECT_ERROR:
        if (e->data && (e->size > 1))
          {
             E_Volume *v;
             char *udi;

             udi = e->data;
             v = e_fm2_device_volume_find(udi);
             if (v)
               _e_fm_device_error_dialog(_("Eject Error"), _("Can't eject device"), e->data);
          }
        break;

      case E_FM_OP_ERROR:  /*error*/
      {
         E_Dialog *dlg;
         ERR("Error from slave #%d: %s", e->ref, (char *)e->data);
         dlg = _e_fm_error_dialog(e->ref, e->data);
         _e_fm2_op_registry_error(e->ref, dlg);
      }
      break;

      case E_FM_OP_ERROR_RETRY_ABORT:  /*error*/
      {
         E_Dialog *dlg;
         ERR("Error from slave #%d: %s", e->ref, (char *)e->data);
         dlg = _e_fm_retry_abort_dialog(e->ref, (char *)e->data);
         _e_fm2_op_registry_error(e->ref, dlg);
      }
      break;

      case E_FM_OP_OVERWRITE:  /*overwrite*/
      {
         E_Dialog *dlg;
         ERR("Overwrite from slave #%d: %s", e->ref, (char *)e->data);
         dlg = _e_fm_overwrite_dialog(e->ref, (char *)e->data);
         _e_fm2_op_registry_needs_attention(e->ref, dlg);
      }
      break;

      case E_FM_OP_PROGRESS:  /*progress*/
      {
         int percent, seconds;
         off_t done, total;
         char *src = NULL;
         char *dst = NULL;
         char *p = e->data;
         E_Fm2_Op_Registry_Entry *ere;

         if (!e->data) return;

#define UP(value, type) (value) = *(type *)(void *)p; p += sizeof(type)
         UP(percent, int);
         UP(seconds, int);
         UP(done, off_t);
         UP(total, off_t);
#undef UP
         src = p;
         dst = p + strlen(src) + 1;
         // printf("%s:%s(%d) Progress from slave #%d:\n\t%d%% done,\n\t%d seconds left,\n\t%zd done,\n\t%zd total,\n\tsrc = %s,\n\tdst = %s.\n", __FILE__, __FUNCTION__, __LINE__, e->ref, percent, seconds, done, total, src, dst);

         ere = e_fm2_op_registry_entry_get(e->ref);
         if (!ere) return;
         ere->percent = percent;
         ere->done = done;
         ere->total = total;
         ere->eta = seconds;
         e_fm2_op_registry_entry_files_set(ere, src, dst);
         if (ere->percent == 100)
           {
              ere->status = E_FM2_OP_STATUS_SUCCESSFUL;
              ere->finished = 1;
           }
         e_fm2_op_registry_entry_changed(ere);
      }
      break;

      case E_FM_OP_QUIT:  /*finished*/
      {
         E_Fm2_Op_Registry_Entry *ere = e_fm2_op_registry_entry_get(e->ref);
         if (ere)
           {
              ere->finished = 1;
              ere->eta = 0;
              e_fm2_op_registry_entry_changed(ere);
           }
         e_fm2_op_registry_entry_del(e->ref);
      }
      break;

      default:
        break;
     }
}

E_API void
e_fm2_client_del(Ecore_Ipc_Event_Client_Del *e)
{
   Eina_List *l;
   E_Fm2_Client *cl;

   EINA_LIST_FOREACH(_e_fm2_client_list, l, cl)
     {
        if (cl->cl == e->client)
          {
             _e_fm2_client_list = eina_list_remove_list(_e_fm2_client_list, l);
             free(cl);
             break;
          }
     }
}

/* local subsystem functions */
static const char *
_e_fm2_dev_path_map(E_Fm2_Smart_Data *sd, const char *dev, const char *path)
{
   char buf[PATH_MAX] = "", *s, *ss;
   int len;

   /* map a device name to a mount point/path on the os (and append path) */

   /* FIXME: load mappings from config and use them first - maybe device
    * discovery should be done through config and not the below (except
    * maybe for home directory and root fs and other simple thngs */
   /* FIXME: also add other virtualized dirs like "backgrounds", "themes",
    * "favorites" */
#define CMP(x)        ((dev) && (e_util_glob_case_match(dev, x)))
#define PRT(args ...) snprintf(buf, sizeof(buf), ##args)

   if (dev)
     {
        if (dev[0] == '/')
          {
             if (dev[1] == '\0')
               {
                  if (eina_strlcpy(buf, path, sizeof(buf)) >=
                      (int)sizeof(buf))
                    return NULL;
               }
             else
               {
                  if (PRT("%s/%s", dev, path) >= (int)sizeof(buf))
                    return NULL;
               }
          }
        else if ((dev[0] == '~') && (dev[1] == '/') && (dev[2] == '\0'))
          {
             s = (char *)e_user_homedir_get();
             if (PRT("%s/%s", s, path) >= (int)sizeof(buf))
               return NULL;
          }
        else if (strcmp(dev, "favorites") == 0)
          {
             /* this is a virtual device - it's where your favorites list is
              * stored - a dir with
                .desktop files or symlinks (in fact anything
              * you like).
              */
             if (e_user_dir_concat_static(buf, "fileman/favorites") >= sizeof(buf))
               return NULL;
             if (sd && sd->config) sd->config->view.no_typebuf_set = EINA_TRUE;
             ecore_file_mkpath(buf);
          }
        else if (strcmp(dev, "desktop") == 0)
          {
             /* this is a virtual device - it's where your favorites list is
              * stored - a dir with
                .desktop files or symlinks (in fact anything
              * you like).
              */
             if ((!path) || (!path[0]) || (!strcmp(path, "/")))
               {
                  snprintf(buf, sizeof(buf), "%s", efreet_desktop_dir_get());
                  ecore_file_mkpath(buf);
               }
             else if (path[0] == '/')
               snprintf(buf, sizeof(buf), "%s/%s", efreet_desktop_dir_get(), path);
             else
               {
                  snprintf(buf, sizeof(buf), "%s-%s", efreet_desktop_dir_get(), path);
                  ecore_file_mkpath(buf);
               }
          }
        else if (strcmp(dev, "temp") == 0)
          PRT("/tmp");
        /* FIXME: replace all this removable, dvd and like with hal */
        else if (!strncmp(dev, "removable:", sizeof("removable:") - 1))
          {
             E_Volume *v;

             v = e_fm2_device_volume_find(dev + strlen("removable:"));
             if (v)
               {
                  if ((!v->mount_point) && (v->efm_mode == EFM_MODE_USING_HAL_MOUNT))
                    v->mount_point = e_fm2_device_volume_mountpoint_get(v);
                  else if (!v->mount_point)
                    return NULL;

                  if (PRT("%s/%s", v->mount_point, path) >= (int)sizeof(buf))
                    return NULL;
               }
          }
/*  else if (CMP("dvd") || CMP("dvd-*")) */
/*    { */
/*       /\* FIXME: find dvd mountpoint optionally for dvd no. X *\/ */
/*       /\* maybe make part of the device mappings config? *\/ */
/*    } */
/*  else if (CMP("cd") || CMP("cd-*") || CMP("cdrom") || CMP("cdrom-*") || */
/*     CMP("dvd") || CMP("dvd-*")) */
/*    { */
/*       /\* FIXME: find cdrom or dvd mountpoint optionally for cd/dvd no. X *\/ */
/*       /\* maybe make part of the device mappings config? *\/ */
/*    } */
     }
   /* FIXME: add code to find USB devices (multi-card readers or single,
    * usb thumb drives, other usb storage devices (usb hd's etc.)
    */
   /* maybe make part of the device mappings config? */
   /* FIXME: add code for finding nfs shares, smb shares etc. */
   /* maybe make part of the device mappings config? */

   if (buf[0] == '\0')
     {
        if (eina_strlcpy(buf, path, sizeof(buf)) >= sizeof(buf))
          return NULL;
     }

   /* strip out excess multiple slashes */
   s = buf;
   while (*s)
     {
        while ((s[0] == '/') && (s[1] == '/'))
          {
             ss = s;
             do
               {
                  ss[0] = ss[1];
                  ss++;
               }
             while (*ss);
          }
        s++;
     }
   /* strip out slashes at the end - unless its just "/" */
   len = s - buf;
   while ((len > 1) && (buf[len - 1] == '/'))
     {
        buf[len - 1] = 0;
        len--;
     }
   return eina_stringshare_add(buf);
}

static void
_e_fm2_file_add(Evas_Object *obj, const char *file, int unique, Eina_Stringshare *file_rel, int after, E_Fm2_Finfo *finf)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic, *ic2;
   Eina_List *l;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* if we only want unique icon names - if it's there - ignore */
   if (unique)
     {
        EINA_LIST_FOREACH(sd->icons, l, ic)
          {
             if (!strcmp(ic->info.file, file))
               {
                  sd->tmp.last_insert = NULL;
                  return;
               }
          }
        EINA_LIST_FOREACH(sd->queue, l, ic)
          {
             if (!strcmp(ic->info.file, file))
               {
                  sd->tmp.last_insert = NULL;
                  return;
               }
          }
     }
   /* create icon obj and append to unsorted list */
   ic = _e_fm2_icon_new(sd, file, finf);
   if (ic)
     {
        if (!file_rel)
          {
             if (ic->queued) abort();
             if (ic->inserted) abort();
             /* respekt da ordah! */
             if (sd->order_file)
               sd->queue = eina_list_append(sd->queue, ic);
             else
               {
                  /* insertion sort it here to spread the sort load into idle time */
                  sd->queue = eina_list_sorted_insert(sd->queue, _e_fm2_cb_icon_sort, ic);
               }
             ic->queued = EINA_TRUE;
          }
        else
          {
             if (ic->queued) abort();
             if (ic->inserted) abort();
             EINA_LIST_FOREACH(sd->icons, l, ic2)
               {
                  if (ic2->info.file == file_rel)
                    {
                       if (after)
                         sd->icons = eina_list_append_relative(sd->icons, ic, ic2);
                       else
                         sd->icons = eina_list_prepend_relative(sd->icons, ic, ic2);
                       ic->inserted = EINA_TRUE;
                       break;
                    }
               }
             if (ic->inserted)
               {
                  sd->icons = eina_list_append(sd->icons, ic);
                  ic->inserted = EINA_TRUE;
               }
             sd->icons_place = eina_list_append(sd->icons_place, ic);
          }
        sd->tmp.last_insert = NULL;
        sd->iconlist_changed = EINA_TRUE;
     }
}

static void
_e_fm2_file_del(Evas_Object *obj, const char *file)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;
   Eina_List *l;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!strcmp(ic->info.file, file))
          {
             if (!ic->inserted) abort();
             sd->icons = eina_list_remove_list(sd->icons, l);
             ic->inserted = EINA_FALSE;
             sd->icons_place = eina_list_remove(sd->icons_place, ic);
             if (ic->region)
               {
                  ic->region->list = eina_list_remove(ic->region->list, ic);
                  ic->region = NULL;
               }
             _e_fm2_icon_free(ic);
             return;
          }
     }
   EINA_LIST_FOREACH(sd->queue, l, ic)
     {
        if (!strcmp(ic->info.file, file))
          {
             INF("MATCH!");
             sd->queue = eina_list_remove_list(sd->queue, l);
             ic->queued = EINA_FALSE;
             _e_fm2_icon_free(ic);
             return;
          }
     }
}

static void
_e_fm_file_buffer_clear(void)
{
   const char *s;
   EINA_LIST_FREE(_e_fm_file_buffer, s)
     eina_stringshare_del(s);

   _e_fm_file_buffer_copying = 0;
}

static Eina_Bool
_e_fm2_buffer_fill(Evas_Object *obj)
{
   Eina_List *sel;
   char buf[PATH_MAX], *pfile;
   int bufused, buffree;
   const char *real_path;
   const E_Fm2_Icon_Info *ici;

   sel = e_fm2_selected_list_get(obj);
   if (!sel) return EINA_FALSE;

   real_path = e_fm2_real_path_get(obj);
   if (!real_path) return EINA_FALSE;

   bufused = eina_strlcpy(buf, real_path, sizeof(buf));
   if (bufused >= (int)sizeof(buf) - 2) return EINA_FALSE;

   if ((bufused > 0) && (buf[bufused - 1] != '/'))
     {
        buf[bufused] = '/';
        bufused++;
     }

   pfile = buf + bufused;
   buffree = sizeof(buf) - bufused;

   EINA_LIST_FREE(sel, ici)
     {
        if (!ici) continue;
        if ((int)eina_strlcpy(pfile, ici->file, buffree) >= buffree) continue;
        _e_fm_file_buffer = eina_list_append(_e_fm_file_buffer, _e_fm2_uri_escape(buf));
     }

   return EINA_TRUE;
}

static void
_e_fm2_file_cut(Evas_Object *obj)
{
   _e_fm_file_buffer_clear();
   _e_fm2_buffer_fill(obj);
}

static void
_e_fm2_file_copy(Evas_Object *obj)
{
   _e_fm_file_buffer_clear();
   _e_fm_file_buffer_copying = _e_fm2_buffer_fill(obj);
}

static void
_e_fm2_file_paste(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *paths;
   const char *filepath;
   size_t length = 0;
   size_t size = 0;
   char *args = NULL;
   Eina_Bool memerr = EINA_FALSE;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;

   /* Convert URI list to a list of real paths. */
   paths = e_fm2_uri_path_list_get(_e_fm_file_buffer);
   if (!paths) return;
   EINA_LIST_FREE(paths, filepath)
     {
        /* Get file's full path. */
        if (!filepath) continue;
        /* Check if file is protected. */
        if (e_filereg_file_protected(filepath))
          {
             eina_stringshare_del(filepath);
             continue;
          }
        /* Put filepath into a string of args.
         * If there are more files, put an additional space.
         */
        if (!memerr)
          {
             args = e_util_string_append_quoted(args, &size, &length, filepath);
             if (!args) memerr = EINA_TRUE;
             else
               {
                  args = e_util_string_append_char(args, &size, &length, ' ');
                  if (!args) memerr = EINA_TRUE;
               }
          }
        eina_stringshare_del(filepath);
     }
   if (memerr) return;

   /* Add destination to the arguments. */
   {
      E_Fm2_Icon *ic = NULL;

      if (eina_list_count(sd->selected_icons) == 1)
        {
           ic = eina_list_data_get(sd->selected_icons);
           if (ic->info.link || (!S_ISDIR(ic->info.statinfo.st_mode))) ic = NULL;
        }
      if (ic)
        {
           args = e_util_string_append_quoted(args, &size, &length, sd->realpath);
           if (!args) return;
           args = e_util_string_append_char(args, &size, &length, '/');
           if (!args) return;
           args = e_util_string_append_quoted(args, &size, &length, ic->info.file);
           if (!args) return;
        }
      else
        {
           args = e_util_string_append_quoted(args, &size, &length, sd->realpath);
           if (!args) return;
        }
   }

   /* Roll the operation! */
   if (_e_fm_file_buffer_copying)
     {
        if (sd->config->view.link_drop)
          e_fm2_client_file_symlink(sd->obj, args);
        else
          e_fm2_client_file_copy(sd->obj, args);
     }
   else
     {
        if (sd->config->view.link_drop)
          e_fm2_client_file_symlink(sd->obj, args);
        else
          e_fm2_client_file_move(sd->obj, args);
        _e_fm_file_buffer_clear();
     }

   free(args);
}

static void
_e_fm2_file_symlink(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *paths;
   const char *filepath;
   size_t length = 0;
   size_t size = 0;
   char *args = NULL;
   Eina_Bool memerr = EINA_FALSE;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;

   /* Convert URI list to a list of real paths. */
   paths = e_fm2_uri_path_list_get(_e_fm_file_buffer);
   EINA_LIST_FREE(paths, filepath)
     {
        /* Get file's full path. */
        if (!filepath)
          continue;

        /* Check if file is protected. */
        if (e_filereg_file_protected(filepath))
          {
             eina_stringshare_del(filepath);
             continue;
          }

        /* Put filepath into a string of args.
         * If there are more files, put an additional space.
         */
        if (!memerr)
          {
             args = e_util_string_append_quoted(args, &size, &length, filepath);
             if (!args) memerr = EINA_TRUE;
             else
               {
                  args = e_util_string_append_char(args, &size, &length, ' ');
                  if (!args) memerr = EINA_TRUE;
               }
          }

        eina_stringshare_del(filepath);
     }
   if (memerr) return;

   /* Add destination to the arguments. */
   args = e_util_string_append_quoted(args, &size, &length, sd->realpath);
   if (!args) return;

   /* Roll the operation! */
   if (_e_fm_file_buffer_copying) e_fm2_client_file_symlink(sd->obj, args);

   free(args);
}

static void
_e_fm2_file_cut_menu(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   if (!sd) return;
   _e_fm2_file_cut(sd->obj);
}

static void
_e_fm2_file_copy_menu(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   if (!sd) return;
   _e_fm2_file_copy(sd->obj);
}

static void
_e_fm2_file_paste_menu(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   if (!sd) return;
   _e_fm2_file_paste(sd->obj);
}

static void
_e_fm2_file_symlink_menu(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   if (!sd) return;
   _e_fm2_file_symlink(sd->obj);
}

static void
_e_fm2_queue_process(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic, *ic2;
   Eina_List *l, **ll;
   int added = 0, i, p0, p1, n, v;
   double t;
   char buf[4096];

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->queue)
     {
        if (sd->resize_job) ecore_job_del(sd->resize_job);
        sd->resize_job = ecore_job_add(_e_fm2_cb_resize_job, obj);
        evas_object_smart_callback_call(sd->obj, "changed", NULL);
        sd->tmp.last_insert = NULL;
        return;
     }
//   double tt = ecore_time_get();
//   int queued = eina_list_count(sd->queue);
/* take unsorted and insert into the icon list - reprocess regions */
   t = ecore_time_get();
   if (!sd->tmp.last_insert)
     {
#if 1
        n = eina_list_count(sd->icons);
        E_FREE(sd->tmp.list_index);
        if (n > 0)
          sd->tmp.list_index = calloc(n, sizeof(Eina_List *));
        if (sd->tmp.list_index)
          {
             ll = sd->tmp.list_index;
             for (l = sd->icons; l; l = eina_list_next(l))
               {
                  *ll = l;
                  ll++;
               }
             /* binary search first queue */
             ic = eina_list_data_get(sd->queue);
             p0 = 0; p1 = n;
             i = (p0 + p1) / 2;
             ll = sd->tmp.list_index;
             if (ll[i])
               do /* avoid garbage deref */
                 {
                    ic2 = eina_list_data_get(ll[i]);
                    v = _e_fm2_cb_icon_sort(ic, ic2);
                    if (v < 0) /* ic should go before ic2 */
                      p1 = i;
                    else /* ic should go at or after ic2 */
                      p0 = i;
                    i = (p0 + p1) / 2;
                    l = ll[i];
                 }
               while ((p1 - p0) > 1);
          }
        else
#endif
        l = sd->icons;
     }
   else
     l = sd->tmp.last_insert;
   while (sd->queue)
     {
        ic = sd->queue->data;
        sd->queue = eina_list_remove_list(sd->queue, sd->queue);
        /* insertion sort - better than qsort for the way we are doing
         * things - incrimentally scan and sort as we go as we now know
         * that the queue files are in order, we speed up insertions to
         * a worst case of O(n) where n is the # of files in the list
         * so far
         */
        if (sd->order_file)
          {
             l = NULL;
          }
        else
          {
             EINA_LIST_FOREACH(l, l, ic2)
               {
                  if (_e_fm2_cb_icon_sort(ic, ic2) < 0)
                    {
                       if (!ic->queued) abort();
                       if (ic->inserted) abort();
                       ic->queued = EINA_FALSE;
                       ic->inserted = EINA_TRUE;
                       if (l == sd->icons)
                         sd->icons = eina_list_prepend(sd->icons, ic);
                       else
                         sd->icons = eina_list_prepend_relative_list(sd->icons,
                                                                     ic, l);
                       sd->tmp.last_insert = l;
                       break;
                    }
               }
          }
        if (!l)
          {
             if (!ic->queued) abort();
             if (ic->inserted) abort();
             ic->queued = EINA_FALSE;
             ic->inserted = EINA_TRUE;
             sd->icons = eina_list_append(sd->icons, ic);
             sd->tmp.last_insert = eina_list_last(sd->icons);
          }
        sd->icons_place = eina_list_append(sd->icons_place, ic);
        added++;
        /* if we spent more than 1/20th of a second inserting - give up
         * for now */
        if ((_e_fm2_toomany_get(sd)) && (!sd->toomany))
          {
             sd->toomany = EINA_TRUE;
             break;
          }
        if ((ecore_time_get() - t) > 0.01) break;
     }
//   printf("FM: SORT %1.3f (%i files) (%i queued, %i added) [%i iter]\n",
//	  ecore_time_get() - tt, eina_list_count(sd->icons), queued,
//	  added, sd->tmp.iter);
   sd->overlay_count = eina_list_count(sd->icons);
   snprintf(buf, sizeof(buf), P_("%u file", "%u files", sd->overlay_count), sd->overlay_count);
   edje_object_part_text_set(sd->overlay, "e.text.busy_label", buf);
   if (sd->resize_job) ecore_job_del(sd->resize_job);
   // this will handle a relayout if we have too many icons
   sd->resize_job = ecore_job_add(_e_fm2_cb_resize_job, obj);
   evas_object_smart_callback_call(sd->obj, "changed", NULL);
   sd->tmp.iter++;
}

static void
_e_fm2_queue_free(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* just free the icons in the queue  and the queue itself */
   EINA_LIST_FREE(sd->queue, ic)
     {
        if (!ic->queued) abort();
        if (ic->inserted) abort();
        ic->queued = EINA_FALSE;
        _e_fm2_icon_free(ic);
     }
}

static void
_e_fm2_regions_free(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Region *rg;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* free up all regions */
   EINA_LIST_FREE(sd->regions.list, rg)
     _e_fm2_region_free(rg);
}

static void
_e_fm2_regions_populate(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;
   E_Fm2_Region *rg;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* take the icon list and split into regions */
   rg = NULL;
   evas_event_freeze(evas_object_evas_get(obj));
   edje_freeze();
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!rg)
          {
             rg = _e_fm2_region_new(sd);
             sd->regions.list = eina_list_append(sd->regions.list, rg);
          }
        ic->region = rg;
        rg->list = eina_list_append(rg->list, ic);
        if (rg->w == 0)
          {
             rg->x = ic->x;
             rg->y = ic->y;
             rg->w = ic->w;
             rg->h = ic->h;
          }
        else
          {
             if (ic->x < rg->x)
               {
                  rg->w += rg->x - ic->x;
                  rg->x = ic->x;
               }
             if ((ic->x + ic->w) > (rg->x + rg->w))
               {
                  rg->w += (ic->x + ic->w) - (rg->x + rg->w);
               }
             if (ic->y < rg->y)
               {
                  rg->h += rg->y - ic->y;
                  rg->y = ic->y;
               }
             if ((ic->y + ic->h) > (rg->y + rg->h))
               {
                  rg->h += (ic->y + ic->h) - (rg->y + rg->h);
               }
          }
        if ((int)eina_list_count(rg->list) > sd->regions.member_max)
          rg = NULL;
     }
   _e_fm2_regions_eval(obj);
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if ((!ic->region->realized) && (ic->realized))
          _e_fm2_icon_unrealize(ic);
     }
   _e_fm2_obj_icons_place(sd);
   edje_thaw();
   evas_event_thaw(evas_object_evas_get(obj));
}

static void
_e_fm2_icons_place_icons(E_Fm2_Smart_Data *sd)
{
   Eina_List *l;
   E_Fm2_Icon *ic;
   Evas_Coord x, y, rh;

   x = 0; y = 0;
   rh = 0;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if ((x > 0) && ((x + ic->w) > sd->w))
          {
             x = 0;
             y += rh;
             rh = 0;
          }
        ic->x = x;
        ic->y = y;
        x += ic->w;
        sd->min.w = MAX(ic->min_w, sd->min.w);
        sd->min.h = MAX(ic->min_h, sd->min.h);
        if (ic->h > rh) rh = ic->h;
        if ((ic->x + ic->w) > sd->max.w) sd->max.w = ic->x + ic->w;
        if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
     }
}

static void
_e_fm2_icons_place_grid_icons(E_Fm2_Smart_Data *sd)
{
   Eina_List *l;
   E_Fm2_Icon *ic;
   Evas_Coord x, y, gw, gh;
   int cols = 1, col;

   gw = 0; gh = 0;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (ic->w > gw) gw = ic->w;
        if (ic->h > gh) gh = ic->h;
     }
   if (gw > 0) cols = sd->w / gw;
   if (cols < 1) cols = 1;
   x = 0; y = 0; col = 0;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        ic->x = x + ((gw - ic->w) / 2);
        ic->y = y + (gh - ic->h);
        x += gw;
        col++;
        if (col >= cols)
          {
             col = 0;
             x = 0;
             y += gh;
          }
        if ((ic->x + ic->w) > sd->max.w) sd->max.w = ic->x + ic->w;
        if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
        sd->min.w = MAX(ic->min_w, sd->min.w);
        sd->min.h = MAX(ic->min_h, sd->min.h);
     }
}

static int
_e_fm2_icons_icon_overlaps(E_Fm2_Icon *ic)
{
   Eina_List *l;
   E_Fm2_Icon *ic2;

   /* this is really slow... when we have a lot of icons */
   EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
     {
        if ((ic2 != ic) && (ic2->saved_pos))
          {
             if (E_INTERSECTS(ic2->x, ic2->y, ic2->w, ic2->h,
                              ic->x, ic->y, ic->w, ic->h))
               return 1;
          }
     }
   return 0;
}

static int
_e_fm2_icons_icon_row_ok(E_Fm2_Icon *ic)
{
   if (ic->x + ic->w > ic->sd->w) return 0;
   if (ic->x < 0) return 0;
   if (ic->y < 0) return 0;
   return 1;
}

static void
_e_fm2_icon_place_relative(E_Fm2_Icon *ic, E_Fm2_Icon *icr, int xrel, int yrel, int xa, int ya)
{
   ic->x = icr->x;
   ic->y = icr->y;

   if (xrel > 0) ic->x += icr->w;
   else if (xrel < 0)
     ic->x -= ic->w;
   else if (xa == 1)
     ic->x += (icr->w - ic->w) / 2;
   else if (xa == 2)
     ic->x += icr->w - ic->w;

   if (yrel > 0) ic->y += icr->h;
   else if (yrel < 0)
     ic->y -= ic->h;
   else if (ya == 1)
     ic->y += (icr->h - ic->h) / 2;
   else if (ya == 2)
     ic->y += icr->h - ic->h;
}

static void
_e_fm2_icons_place_icon(E_Fm2_Icon *ic)
{
   Eina_List *l;
   E_Fm2_Icon *ic2;

   ic->x = 0;
   ic->y = 0;
   ic->saved_pos = EINA_TRUE;
   /* ### BLAH ### */
//   if (!_e_fm2_icons_icon_overlaps(ic)) return;
/*
   _e_fm2_icon_place_relative(ic, ic2, 1, 0, 0, 2);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
   _e_fm2_icon_place_relative(ic, ic2, 0, -1, 0, 0);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
   _e_fm2_icon_place_relative(ic, ic2, 0, -1, 1, 0);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
   _e_fm2_icon_place_relative(ic, ic2, 1, 0, 0, 0);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
   _e_fm2_icon_place_relative(ic, ic2, 1, 0, 0, 1);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
   _e_fm2_icon_place_relative(ic, ic2, 0, 1, 0, 0);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
   _e_fm2_icon_place_relative(ic, ic2, 0, 1, 1, 0);
   if (_e_fm2_icons_icon_row_ok(ic) && !_e_fm2_icons_icon_overlaps(ic)) return;
 */
   if ((ic->sd->last_placed) && (ic->sd->toomany))
     {
        ic2 = ic->sd->last_placed;
        // ###_
        _e_fm2_icon_place_relative(ic, ic2, 1, 0, 0, 2);
        if (_e_fm2_icons_icon_row_ok(ic) &&
            !_e_fm2_icons_icon_overlaps(ic)) goto done;
        // _###
        _e_fm2_icon_place_relative(ic, ic2, -1, 0, 0, 2);
        if (_e_fm2_icons_icon_row_ok(ic) &&
            !_e_fm2_icons_icon_overlaps(ic)) goto done;
        // ###
        //  |
        _e_fm2_icon_place_relative(ic, ic2, 0, 1, 1, 0);
        if (_e_fm2_icons_icon_row_ok(ic) &&
            !_e_fm2_icons_icon_overlaps(ic)) goto done;
        //  |
        // ###
        _e_fm2_icon_place_relative(ic, ic2, 0, -1, 1, 0);
        if (_e_fm2_icons_icon_row_ok(ic) &&
            !_e_fm2_icons_icon_overlaps(ic)) goto done;
        // do this anyway - dont care.
        // ###
        //  |
        _e_fm2_icon_place_relative(ic, ic2, 0, 1, 1, 0);
        ic->x = 0;
        goto done;
     }
   EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
     {
        if ((ic2 != ic) && (ic2->saved_pos))
          {
             // ###_
             _e_fm2_icon_place_relative(ic, ic2, 1, 0, 0, 2);
             if (_e_fm2_icons_icon_row_ok(ic) &&
                 !_e_fm2_icons_icon_overlaps(ic)) goto done;
             // _###
             _e_fm2_icon_place_relative(ic, ic2, -1, 0, 0, 2);
             if (_e_fm2_icons_icon_row_ok(ic) &&
                 !_e_fm2_icons_icon_overlaps(ic)) goto done;
          }
     }

   EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
     {
        if ((ic2 != ic) && (ic2->saved_pos))
          {
             // ###
             //  |
             _e_fm2_icon_place_relative(ic, ic2, 0, 1, 1, 0);
             if (_e_fm2_icons_icon_row_ok(ic) &&
                 !_e_fm2_icons_icon_overlaps(ic)) goto done;
             //  |
             // ###
             _e_fm2_icon_place_relative(ic, ic2, 0, -1, 1, 0);
             if (_e_fm2_icons_icon_row_ok(ic) &&
                 !_e_fm2_icons_icon_overlaps(ic)) goto done;
          }
        if ((ic2 != ic) && (ic2->saved_pos))
          {
// TODO: if uncomment this, change EINA_LIST_FOREACH to EINA_LIST_FOREACH_SAFE!
//	     ic->sd->icons_place = eina_list_remove_list(ic->sd->icons_place, pl);
          }
     }
done:
   ic->sd->last_placed = ic;
   return;
}

static void
_e_fm2_icons_place_custom_icons(E_Fm2_Smart_Data *sd)
{
   Eina_List *l;
   E_Fm2_Icon *ic;

   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!ic->saved_pos)
          {
             /* FIXME: place using smart place fn */
             _e_fm2_icons_place_icon(ic);
          }

        if ((ic->x + ic->w) > sd->max.w) sd->max.w = ic->x + ic->w;
        if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
        sd->min.w = MAX(ic->min_w, sd->min.w);
        sd->min.h = MAX(ic->min_h, sd->min.h);
     }
}

static void
_e_fm2_icons_place_custom_grid_icons(E_Fm2_Smart_Data *sd)
{
   /* FIXME: not going to implement this at this stage */
   Eina_List *l;
   E_Fm2_Icon *ic;

   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!ic->saved_pos)
          {
             /* FIXME: place using grid fn */
          }

        if ((ic->x + ic->w) > sd->max.w) sd->max.w = ic->x + ic->w;
        if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
        sd->min.w = MAX(ic->min_w, sd->min.w);
        sd->min.h = MAX(ic->min_h, sd->min.h);
     }
}

static void
_e_fm2_icons_place_custom_smart_grid_icons(E_Fm2_Smart_Data *sd)
{
   /* FIXME: not going to implement this at this stage */
   Eina_List *l;
   E_Fm2_Icon *ic;

   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!ic->saved_pos)
          {
             /* FIXME: place using smart grid fn */
          }

        if ((ic->x + ic->w) > sd->max.w) sd->max.w = ic->x + ic->w;
        if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
        sd->min.w = MAX(ic->min_w, sd->min.w);
        sd->min.h = MAX(ic->min_h, sd->min.h);
     }
}

static void
_e_fm2_icons_place_list(E_Fm2_Smart_Data *sd)
{
   Eina_List *l;
   E_Fm2_Icon *ic;
   Evas_Coord x, y;
   int w, i;

   w = i = x = y = 0;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        ic->x = x;
        ic->y = y;
        sd->min.w = MAX(ic->min_w, sd->min.w);
        sd->min.h = MAX(ic->min_h, sd->min.h);
        if (sd->w > ic->min_w)
          ic->w = sd->w;
        else
          ic->w = ic->min_w;
        y += ic->h;
        ic->odd = (i & 0x01);
        if ((ic->w != sd->w) && ((ic->x + ic->w) > sd->max.w)) sd->max.w = ic->x + ic->w;
        else if (ic->min_w > sd->max.w)
          sd->max.w = ic->min_w;
        if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
        w = MAX(w, ic->min_w);
        w = MAX(w, sd->w);
        i++;
     }
   EINA_LIST_FOREACH(sd->icons, l, ic)
     ic->w = w;
}

static void
_e_fm2_icons_place(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* take the icon list and find a location for them */
   sd->max.w = 0;
   sd->max.h = 0;
   switch (_e_fm2_view_mode_get(sd))
     {
      case E_FM2_VIEW_MODE_ICONS:
        _e_fm2_icons_place_icons(sd);
//	sd->max.h += ICON_BOTTOM_SPACE;
        break;

      case E_FM2_VIEW_MODE_GRID_ICONS:
        _e_fm2_icons_place_grid_icons(sd);
//	sd->max.h += ICON_BOTTOM_SPACE;
        break;

      case E_FM2_VIEW_MODE_CUSTOM_ICONS:
        _e_fm2_icons_place_custom_icons(sd);
//	sd->max.h += ICON_BOTTOM_SPACE;
        break;

      case E_FM2_VIEW_MODE_CUSTOM_GRID_ICONS:
        /* FIXME: not going to implement this at this stage */
        _e_fm2_icons_place_custom_grid_icons(sd);
//	sd->max.h += ICON_BOTTOM_SPACE;
        break;

      case E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS:
        /* FIXME: not going to implement this at this stage */
        _e_fm2_icons_place_custom_smart_grid_icons(sd);
//	sd->max.h += ICON_BOTTOM_SPACE;
        break;

      case E_FM2_VIEW_MODE_LIST:
        _e_fm2_icons_place_list(sd);
        break;

      default:
        break;
     }
   /* tell our parent scrollview - if any, that we have changed */
   evas_object_smart_callback_call(sd->obj, "changed", NULL);
}

static void
_e_fm2_icons_free(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   _e_fm2_queue_free(obj);
   /* free all icons */
   EINA_LIST_FREE(sd->icons, ic)
     {
        if (ic->queued) abort();
        if (!ic->inserted) abort();
        ic->inserted = EINA_FALSE;
        _e_fm2_icon_free(ic);
     }
   sd->last_selected = NULL;
   sd->range_selected = NULL;
   eina_list_free(sd->icons_place);
   sd->icons_place = NULL;
   sd->tmp.last_insert = NULL;
   E_FREE(sd->tmp.list_index);
}

static void
_e_fm2_regions_eval(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;
   E_Fm2_Region *rg;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   EINA_LIST_FOREACH(sd->regions.list, l, rg)
     {
        if (_e_fm2_region_visible(rg))
          _e_fm2_region_realize(rg);
        else
          _e_fm2_region_unrealize(rg);
     }
}

static void
_e_fm2_config_free(E_Fm2_Config *cfg)
{
   if (cfg->icon.key_hint) eina_stringshare_del(cfg->icon.key_hint);
   if (cfg->theme.background) eina_stringshare_del(cfg->theme.background);
   if (cfg->theme.frame) eina_stringshare_del(cfg->theme.frame);
   if (cfg->theme.icons) eina_stringshare_del(cfg->theme.icons);
   free(cfg);
}

static Eina_List *
_e_fm2_file_fm2_find(const char *file)
{
   char *dir;
   Eina_List *l, *ret = NULL;
   Evas_Object *obj;

   dir = ecore_file_dir_get(file);
   if (!dir) return NULL;
   EINA_LIST_FOREACH(_e_fm2_list, l, obj)
     {
        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, obj))) continue;
        if (!strcmp(e_fm2_real_path_get(obj), dir))
          ret = eina_list_append(ret, obj);
     }
   free(dir);
   return ret;
}

static E_Fm2_Icon *
_e_fm2_icon_find(Evas_Object *obj, const char *file)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        if (!strcmp(ic->info.file, file)) return ic;
     }
   return NULL;
}

/* Escape illegal caracters within an uri and return an eina_stringshare */
static const char *
_e_fm2_uri_escape(const char *path)
{
   char dest[PATH_MAX * 3 + 7];
   const char *p;
   int i;

   if (!path) return NULL;
   memset(dest, 0, PATH_MAX * 3 + 7);

   snprintf(dest, 8, "file://");

   /* Most app doesn't handle the hostname in the uri so it's put to NULL */
   for (i = 7, p = path; *p != '\0'; p++, i++)
     {
        if (isalnum(*p) || strchr("/$-_.+!*'()", *p))
          dest[i] = *p;
        else
          {
             snprintf(&(dest[i]), 4, "%%%02X", (unsigned char)*p);
             i += 2;
          }
     }

   return eina_stringshare_add(dest);
}

/* Parse a single uri and return an E_Fm2_Uri struct.
 * If the parsing have failed it return NULL.
 * The E_Fm2_Uri may have hostname parameter and always a path.
 * If there's no hostname in the uri then the hostname parameter is NULL
 */
static E_Fm2_Uri *
_e_fm2_uri_parse(const char *val)
{
   E_Fm2_Uri *uri;
   const char *p;
   char hostname[PATH_MAX], path[PATH_MAX];
   int i = 0;

   /* if value is a raw path: /path/to/blah just return it */
   if (val[0] == '/')
     {
        uri = E_NEW(E_Fm2_Uri, 1);
        uri->hostname = NULL;
        uri->path = eina_stringshare_add(val);
        return uri;
     }
   /* The shortest possible path is file:///
    * anything smaller than that can't be a valid uri
    */
   if (strlen(val) <= 7 && strncmp(val, "file://", 7)) return NULL;
   memset(path, 0, PATH_MAX);

   /* An uri should be in a form file://<hostname>/<path> */
   p = val + 7;
   if (*p != '/')
     {
        for (i = 0; *p != '/' && *p != '\0' && i < _POSIX_HOST_NAME_MAX; p++, i++)
          hostname[i] = *p;
     }
   hostname[i] = '\0';

   /* See http://www.faqs.org/rfcs/rfc1738.html for the escaped chars */
   for (i = 0; (*p != '\0') && (i < (PATH_MAX - 1)); i++, p++)
     {
        if (*p == '%')
          {
             path[i] = *(++p);
             path[i + 1] = *(++p);
             path[i] = (char)strtol(&(path[i]), NULL, 16);
             path[i + 1] = '\0';
          }
        else
          path[i] = *p;
     }

   uri = E_NEW(E_Fm2_Uri, 1);
   if (hostname[0]) uri->hostname = eina_stringshare_add(hostname);
   else uri->hostname = NULL;
   uri->path = eina_stringshare_add(path);

   return uri;
}

/* Takes an Eina_List of uri and return an Eina_List of real paths */
static Eina_List *
_e_fm2_uri_selected_icon_list_get(Eina_List *uri)
{
   Eina_List *icons = NULL;
   Eina_List *l;
   const char *path;

   EINA_LIST_FOREACH(uri, l, path)
     {
        Eina_List *fms;
        Evas_Object *fm;
        E_Fm2_Icon *ic;

        fms = _e_fm2_file_fm2_find(path);
        if (!fms) continue;
        ic = NULL;
        EINA_LIST_FREE(fms, fm)
          {
             ic = _e_fm2_icon_find(fm, ecore_file_file_get(path));
             if (!ic) continue;
             if (eina_list_data_find(ic->sd->selected_icons, ic)) break;
             ic = NULL;
          }
        if (ic) icons = eina_list_append(icons, ic);
     }
   return icons;
}

/**************************/

static E_Fm2_Icon *
_e_fm2_icon_new(E_Fm2_Smart_Data *sd, const char *file, E_Fm2_Finfo *finf)
{
   E_Fm2_Icon *ic;

   /* create icon */
   ic = E_NEW(E_Fm2_Icon, 1);
   ic->info.fm = sd->obj;
   ic->info.ic = ic;
   ic->info.file = eina_stringshare_add(file);
   ic->sd = sd;
   if (!_e_fm2_icon_fill(ic, finf))
     {
        eina_stringshare_del(ic->info.file);
        free(ic);
        return NULL;
     }
   return ic;
}

static void
_e_fm2_icon_unfill(E_Fm2_Icon *ic)
{
   eina_stringshare_del(ic->info.mime);
   eina_stringshare_del(ic->info.label);
   eina_stringshare_del(ic->info.comment);
   eina_stringshare_del(ic->info.generic);
   eina_stringshare_del(ic->info.icon);
   eina_stringshare_del(ic->info.link);
   eina_stringshare_del(ic->info.real_link);
   eina_stringshare_del(ic->info.category);
   ic->info.mime = NULL;
   ic->info.label = NULL;
   ic->info.comment = NULL;
   ic->info.generic = NULL;
   ic->info.icon = NULL;
   ic->info.link = NULL;
   ic->info.real_link = NULL;
   ic->info.mount = EINA_FALSE;
   ic->info.removable = EINA_FALSE;
   ic->info.removable_full = EINA_FALSE;
   ic->info.deleted = EINA_FALSE;
   ic->info.broken_link = EINA_FALSE;
}

static void
_e_fm2_icon_geom_adjust(E_Fm2_Icon *ic, int saved_x, int saved_y, int saved_w EINA_UNUSED, int saved_h EINA_UNUSED, int saved_res_w, int saved_res_h)
{
   int qx, qy, rx, ry, x, y;

   if (!((_e_fm2_view_mode_get(ic->sd) == E_FM2_VIEW_MODE_CUSTOM_ICONS) &&
         (ic->sd->config->view.fit_custom_pos) &&
         (saved_res_w > 0) &&
         (saved_res_h > 0)))
     return;
   if (saved_res_w >= 3) qx = saved_x / (saved_res_w / 3);
   else qx = 0;
   rx = saved_x - ((saved_res_w / 2) * qx);
   x = ((ic->sd->w / 2) * qx) + rx;
   ic->x = x;

   if (saved_res_h >= 3) qy = saved_y / (saved_res_h / 3);
   else qy = 0;
   ry = saved_y - ((saved_res_h / 2) * qy);
   y = ((ic->sd->h / 2) * qy) + ry;
   ic->y = y;
}

static const char *
_mime_get(const char *path)
{
   const char *mime = efreet_mime_special_type_get(path);
   if (!mime) mime = efreet_mime_globs_type_get(path);
   if (!mime) mime = efreet_mime_fallback_type_get(path);
   return mime;
}

static int
_e_fm2_icon_fill(E_Fm2_Icon *ic, E_Fm2_Finfo *finf)
{
   Evas_Coord mw = 0, mh = 0;
   Evas_Object *obj, *obj2;
   char buf[PATH_MAX];
   const char *mime;
   E_Fm2_Custom_File *cf;

   if (!finf) return 0;
   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf)))
     return 0;
   cf = e_fm2_custom_file_get(buf);
   memcpy(&(ic->info.statinfo), &(finf->st), sizeof(struct stat));
   if ((finf->lnk) && (finf->lnk[0]))
     ic->info.link = eina_stringshare_add(finf->lnk);
   else
     ic->info.link = NULL;
   if ((finf->rlnk) && (finf->rlnk[0]))
     ic->info.real_link = eina_stringshare_add(finf->rlnk);
   else
     ic->info.real_link = NULL;
   ic->info.broken_link = finf->broken_link;

   if ((!ic->info.link) && (S_ISDIR(ic->info.statinfo.st_mode)))
     {
        ic->info.mime = eina_stringshare_ref(_e_fm2_mime_inode_directory);
     }
   else if (ic->info.real_link)
     {
        mime = _mime_get(ic->info.real_link);
        if (!mime)
          /* XXX REMOVE/DEPRECATE ME LATER */
          mime = e_fm_mime_filename_get(ic->info.file);
        if (mime) ic->info.mime = eina_stringshare_add(mime);
     }

   if (!ic->info.mime)
     {
        mime = _mime_get(buf);
        if (!mime)
          /* XXX REMOVE/DEPRECATE ME LATER */
          mime = e_fm_mime_filename_get(ic->info.file);
        if (mime) ic->info.mime = eina_stringshare_add(mime);
     }

   if (_e_fm2_file_is_desktop(ic->info.file))
     _e_fm2_icon_desktop_load(ic);

   if (cf)
     {
        if (cf->icon.valid)
          {
             if (cf->icon.icon)
               {
                  eina_stringshare_replace(&ic->info.icon, cf->icon.icon);
               }
             ic->info.icon_type = cf->icon.type;
          }
        if (cf->geom.valid)
          {
             ic->saved_pos = EINA_TRUE;
             ic->x = cf->geom.x;
             ic->y = cf->geom.y;
             if (cf->geom.w > 0) ic->w = cf->geom.w;
             if (cf->geom.h > 0) ic->h = cf->geom.h;
             _e_fm2_icon_geom_adjust(ic, cf->geom.x, cf->geom.y, cf->geom.w, cf->geom.h, cf->geom.res_w, cf->geom.res_h);
          }
     }

   evas_event_freeze(evas_object_evas_get(ic->sd->obj));
   edje_freeze();

   switch (_e_fm2_view_mode_get(ic->sd))
     {
      case E_FM2_VIEW_MODE_ICONS:
      case E_FM2_VIEW_MODE_GRID_ICONS:
      case E_FM2_VIEW_MODE_CUSTOM_ICONS:
      case E_FM2_VIEW_MODE_CUSTOM_GRID_ICONS:
      case E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS:
        /* FIXME: need to define icon edjes. here goes:
         *
         * fileman/icon/fixed
         * fileman/icon/variable
         * fileman/list/fixed
         * fileman/list/variable
         * fileman/list_odd/fixed
         * fileman/list_odd/variable
         *
         */
        if ((!ic->sd->config->icon.fixed.w) || (!ic->sd->config->icon.fixed.h))
          {
             obj = ic->sd->tmp.obj;
             if (!obj)
               {
                  obj = edje_object_add(evas_object_evas_get(ic->sd->obj));
                  if ((ic->sd->config->icon.fixed.w) && (ic->sd->config->icon.fixed.h))
                    _e_fm2_theme_edje_object_set(ic->sd, obj,
                                                 "base/theme/fileman",
                                                 "icon/fixed");
                  else
                    _e_fm2_theme_edje_object_set(ic->sd, obj,
                                                 "base/theme/fileman",
                                                 "icon/variable");
                  ic->sd->tmp.obj = obj;
               }
             _e_fm2_icon_label_set(ic, obj);
             obj2 = ic->sd->tmp.obj2;
             if (!obj2)
               {
                  obj2 = evas_object_rectangle_add(evas_object_evas_get(ic->sd->obj));
                  ic->sd->tmp.obj2 = obj2;
               }
             /* FIXME: if icons are allowed to have their own size - use it */
             evas_object_size_hint_min_set(obj2, _e_fm2_icon_w_get(ic->sd), _e_fm2_icon_h_get(ic->sd));
             evas_object_size_hint_max_set(obj2, _e_fm2_icon_w_get(ic->sd), _e_fm2_icon_h_get(ic->sd));
             edje_object_part_swallow(obj, "e.swallow.icon", obj2);
             edje_object_size_min_calc(obj, &mw, &mh);
          }
        ic->w = mw;
        ic->h = mh;
        if (ic->sd->config->icon.fixed.w) ic->w = _e_fm2_icon_w_get(ic->sd);
        if (ic->sd->config->icon.fixed.h) ic->h = _e_fm2_icon_h_get(ic->sd);
        ic->min_w = mw;
        ic->min_h = mh;
        break;

      case E_FM2_VIEW_MODE_LIST:
        obj = ic->sd->tmp.obj;
        if (!obj)
          {
             obj = edje_object_add(evas_object_evas_get(ic->sd->obj));
// vairable sized list items are pretty usless - ignore.
//		  if (ic->sd->config->icon.fixed.w)
             _e_fm2_theme_edje_object_set(ic->sd, obj,
                                          "base/theme/fileman",
                                          "list/fixed");
//		  else
//		    _e_fm2_theme_edje_object_set(ic->sd, obj, "base/theme/fileman",
//					    "list/variable");
             ic->sd->tmp.obj = obj;
          }
        _e_fm2_icon_label_set(ic, obj);
        obj2 = ic->sd->tmp.obj2;
        if (!obj2)
          {
             obj2 = evas_object_rectangle_add(evas_object_evas_get(ic->sd->obj));
             ic->sd->tmp.obj2 = obj2;
          }
        evas_object_size_hint_min_set(obj2, ic->sd->config->icon.list.w, ic->sd->config->icon.list.h);
        evas_object_size_hint_max_set(obj2, ic->sd->config->icon.list.w, ic->sd->config->icon.list.h);
        edje_object_part_swallow(obj, "e.swallow.icon", obj2);
        edje_object_size_min_calc(obj, &mw, &mh);
        ic->w = MAX(mw, ic->sd->w);
        ic->h = mh;
        ic->min_w = mw;
        ic->min_h = mh;
        break;

      default:
        break;
     }
   edje_thaw();
   evas_event_thaw(evas_object_evas_get(ic->sd->obj));
   return 1;
}

static void
_e_fm2_icon_free(E_Fm2_Icon *ic)
{
   if (ic->queued) abort();
   if (ic->inserted) abort();
   if (ic->eio)
     {
        eio_file_cancel(ic->eio);
        ic->eio = NULL;
     }
   /* free icon, object data etc. etc. */
   if (ic->sd->last_placed == ic)
     {
        ic->sd->last_placed = NULL;
     }
   if (ic->sd->drop_icon == ic)
     {
        /* FIXME: call hide call */
        ic->sd->drop_icon = NULL;
     }
   _e_fm2_icon_unrealize(ic);
   if (ic->menu)
     {
        e_menu_post_deactivate_callback_set(ic->menu, NULL, NULL);
        e_object_del(E_OBJECT(ic->menu));
        ic->menu = NULL;
     }
   if (ic->dialog)
     {
        e_object_del(E_OBJECT(ic->dialog));
        ic->dialog = NULL;
     }
   if (ic->entry_dialog)
     {
        e_object_del(E_OBJECT(ic->entry_dialog));
        ic->entry_dialog = NULL;
     }
   if (ic->entry_widget)
     _e_fm2_icon_entry_widget_del(ic);
   if (ic->prop_dialog)
     {
        e_object_del(E_OBJECT(ic->prop_dialog));
        ic->prop_dialog = NULL;
     }
   if (ic->mount)
     e_fm2_device_unmount(ic->mount);
   if (ic->mount_timer) ecore_timer_del(ic->mount_timer);
   if (ic->selected)
     ic->sd->selected_icons = eina_list_remove(ic->sd->selected_icons, ic);
   if (ic->drag.dnd_end_timer)
     ecore_timer_del(ic->drag.dnd_end_timer);
   eina_stringshare_del(ic->info.file);
   eina_stringshare_del(ic->info.mime);
   eina_stringshare_del(ic->info.label);
   eina_stringshare_del(ic->info.comment);
   eina_stringshare_del(ic->info.generic);
   eina_stringshare_del(ic->info.icon);
   eina_stringshare_del(ic->info.link);
   eina_stringshare_del(ic->info.real_link);
   eina_stringshare_del(ic->info.category);
   memset(ic, 0xff, sizeof(*ic));
   free(ic);
}

static void
_e_fm2_icon_label_click(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
{
   E_Fm2_Icon *ic = data;
   if (ic->entry_widget || ic->entry_dialog) return;
   if (!ic->selected) return;
   if (ecore_loop_time_get() - ic->selected_time < 0.2) return;

   if (ic->sd->config->view.no_click_rename) return;
   if (eina_list_count(ic->sd->selected_icons) != 1) return;
   if (eina_list_data_get(ic->sd->selected_icons) != ic) return;
   _e_fm2_file_rename(ic, NULL, NULL);
}

static void
_e_fm2_icon_realize(E_Fm2_Icon *ic)
{
   if (ic->realized) goto new_file;
   /* actually create evas objects etc. */
   ic->realized = EINA_TRUE;
   evas_event_freeze(evas_object_evas_get(ic->sd->obj));
   ic->obj = edje_object_add(evas_object_evas_get(ic->sd->obj));
   edje_object_freeze(ic->obj);
   evas_object_smart_member_add(ic->obj, ic->sd->obj);
   evas_object_stack_below(ic->obj, ic->sd->drop);
   if (_e_fm2_view_mode_get(ic->sd) == E_FM2_VIEW_MODE_LIST)
     {
        const char *stacking;

//        if (ic->sd->config->icon.fixed.w)
//	  {
        if (ic->odd)
          _e_fm2_theme_edje_object_set(ic->sd, ic->obj,
                                       "base/theme/widgets",
                                       "list_odd/fixed");
        else
          _e_fm2_theme_edje_object_set(ic->sd, ic->obj,
                                       "base/theme/widgets",
                                       "list/fixed");
        stacking = edje_object_data_get(ic->obj, "stacking");
        if (stacking)
          {
             if (!strcmp(stacking, "below"))
               evas_object_stack_above(ic->obj, ic->sd->underlay);
             else if (!strcmp(stacking, "above"))
               evas_object_stack_below(ic->obj, ic->sd->drop);
          }

//	  }
//	else
//	  {
//	     if (ic->odd)
//	       _e_fm2_theme_edje_object_set(ic->sd, ic->obj, "base/theme/widgets",
//				       "list_odd/variable");
//	     else
//	       _e_fm2_theme_edje_object_set(ic->sd, ic->obj, "base/theme/widgets",
//				       "list/variable");
//	  }
     }
   else
     {
        if (ic->sd->config->icon.fixed.w)
          _e_fm2_theme_edje_object_set(ic->sd, ic->obj,
                                       "base/theme/fileman",
                                       "icon/fixed");
        else
          _e_fm2_theme_edje_object_set(ic->sd, ic->obj,
                                       "base/theme/fileman",
                                       "icon/variable");
     }
   _e_fm2_icon_label_set(ic, ic->obj);
   evas_object_clip_set(ic->obj, ic->sd->clip);
   evas_object_move(ic->obj,
                    ic->sd->x + ic->x - ic->sd->pos.x,
                    ic->sd->y + ic->y - ic->sd->pos.y);
   evas_object_resize(ic->obj, ic->w, ic->h);

   evas_object_event_callback_add(ic->obj, EVAS_CALLBACK_MOUSE_DOWN, _e_fm2_cb_icon_mouse_down, ic);
   evas_object_event_callback_add(ic->obj, EVAS_CALLBACK_MOUSE_UP, _e_fm2_cb_icon_mouse_up, ic);
   evas_object_event_callback_add(ic->obj, EVAS_CALLBACK_MOUSE_MOVE, _e_fm2_cb_icon_mouse_move, ic);
   evas_object_event_callback_add(ic->obj, EVAS_CALLBACK_MOUSE_IN, _e_fm2_cb_icon_mouse_in, ic);
   evas_object_event_callback_add(ic->obj, EVAS_CALLBACK_MOUSE_OUT, _e_fm2_cb_icon_mouse_out, ic);
   edje_object_signal_callback_add(ic->obj, "e,action,label,click", "e", _e_fm2_icon_label_click, ic);

   _e_fm2_icon_icon_set(ic);

   edje_object_thaw(ic->obj);
   evas_event_thaw(evas_object_evas_get(ic->sd->obj));
   evas_object_show(ic->obj);

   if (ic->selected)
     {
        const char *selectraise;

        /* FIXME: need new signal to INSTANTLY activate - no anim */
        /* FIXME: while listing dirs need to use icons in-place and not
         * unrealize and re-realize */
        edje_object_signal_emit(ic->obj, "e,state,selected", "e");
        selectraise = edje_object_data_get(ic->obj, "selectraise");
        if ((selectraise) && (!strcmp(selectraise, "on")))
          evas_object_stack_below(ic->obj, ic->sd->drop);
     }

   if (ic->info.removable)
     _e_fm2_icon_removable_update(ic);
new_file:
   if (ic->sd->new_file.thread && (!ic->sd->new_file.filename))
     {
        /* we got the file through the scanner :/ */
        const char *file = ecore_thread_global_data_find("efm_pending_filename");

        if (!e_util_strcmp(ic->info.file, file))
          {
             _e_fm2_file_rename(ic, NULL, NULL);
             ic->sd->new_file.done = 1;
          }
        if (file)
          ecore_thread_global_data_del("efm_pending_filename");
     }
   if (ic->sd->new_file.filename)
     {
        if (ic->info.file == ic->sd->new_file.filename)
          {
             _e_fm2_file_rename(ic, NULL, NULL);
             eina_stringshare_replace(&ic->sd->new_file.filename, NULL);
          }
     }
}

static void
_e_fm2_icon_unrealize(E_Fm2_Icon *ic)
{
   if (!ic->realized) return;
   /* delete evas objects */
   ic->realized = EINA_FALSE;
   evas_object_event_callback_del_full(ic->obj, EVAS_CALLBACK_MOUSE_DOWN, _e_fm2_cb_icon_mouse_down, ic);
   evas_object_event_callback_del_full(ic->obj, EVAS_CALLBACK_MOUSE_UP, _e_fm2_cb_icon_mouse_up, ic);
   evas_object_event_callback_del_full(ic->obj, EVAS_CALLBACK_MOUSE_MOVE, _e_fm2_cb_icon_mouse_move, ic);
   evas_object_event_callback_del_full(ic->obj, EVAS_CALLBACK_MOUSE_IN, _e_fm2_cb_icon_mouse_in, ic);
   evas_object_event_callback_del_full(ic->obj, EVAS_CALLBACK_MOUSE_OUT, _e_fm2_cb_icon_mouse_out, ic);
   evas_object_del(ic->obj);
   ic->obj = NULL;
   evas_object_del(ic->obj_icon);
   ic->obj_icon = NULL;
}

static Eina_Bool
_e_fm2_icon_visible(const E_Fm2_Icon *ic)
{
   /* return if the icon is visible */
   if (
     ((ic->x - ic->sd->pos.x) < (ic->sd->w + OVERCLIP)) &&
     ((ic->x + ic->w - ic->sd->pos.x) > (-OVERCLIP)) &&
     ((ic->y - ic->sd->pos.y) < (ic->sd->h + OVERCLIP)) &&
     ((ic->y + ic->h - ic->sd->pos.y) > (-OVERCLIP))
     )
     return 1;
   return 0;
}

static void
_e_fm2_icon_label_set(E_Fm2_Icon *ic, Evas_Object *obj)
{
   char buf[4096], *p;
   int len;
   const char *lbl, *type;

   if (ic->info.label)
     lbl = ic->info.label;
   else if ((ic->sd->config->icon.extension.show) || ((!ic->info.link) && (S_ISDIR(ic->info.statinfo.st_mode))))
     lbl = ic->info.file;
   else
     {
        /* remove extension. handle double extensions like .tar.gz too
         * also be fuzzy - up to 4 chars of extn is ok - eg .html but 5 or
         * more is considered part of the name
         */
        eina_strlcpy(buf, ic->info.file, sizeof(buf));

        len = strlen(buf);
        p = strrchr(buf, '.');
        if ((p) && ((len - (p - buf)) < 6))
          {
             *p = 0;

             len = strlen(buf);
             p = strrchr(buf, '.');
             if ((p) && ((len - (p - buf)) < 6)) *p = 0;
          }
        lbl = buf;
     }
   type = evas_object_type_get(edje_object_part_object_get(obj, "e.text.label"));
   if (!e_util_strcmp(type, "textblock"))
     {
        p = evas_textblock_text_utf8_to_markup(NULL, lbl);
        edje_object_part_text_set(obj, "e.text.label", p);
        free(p);
     }
   else
     edje_object_part_text_set(obj, "e.text.label", lbl);
}

static Evas_Object *
_e_fm2_icon_icon_direct_set(E_Fm2_Icon *ic, Evas_Object *o, Evas_Smart_Cb gen_func, void *data, int force_gen)
{
   Evas_Object *oic;

   oic = e_fm2_icon_get(evas_object_evas_get(o), ic,
                        gen_func, data, force_gen, NULL);
   if (oic)
     {
        edje_object_part_swallow(o, "e.swallow.icon", oic);
        evas_object_show(oic);
     }
   return oic;
}

static void
_e_fm2_icon_icon_set(E_Fm2_Icon *ic)
{
   if (!ic->realized) return;
   ic->obj_icon = _e_fm2_icon_icon_direct_set(ic, ic->obj,
                                              _e_fm2_cb_icon_thumb_gen,
                                              ic, 0);
}

static void
_e_fm2_icon_thumb(const E_Fm2_Icon *ic, Evas_Object *oic, int force)
{
   if ((force) ||
       ((_e_fm2_icon_visible(ic)) &&
        (!ic->sd->queue) &&
        (!ic->sd->sort_idler) &&
        (!ic->sd->listing)))
     e_thumb_icon_begin(oic);
}

static void
_e_fm2_icon_select(E_Fm2_Icon *ic)
{
   E_Fm2_Icon *prev;
   if (ic->selected) return;
   prev = eina_list_last_data_get(ic->sd->selected_icons);
   if (prev) prev->last_selected = EINA_FALSE;
   ic->selected = EINA_TRUE;
   ic->sd->range_selected = NULL;
   ic->sd->last_selected = ic;
   ic->sd->selected_icons = eina_list_append(ic->sd->selected_icons, ic);
   ic->last_selected = EINA_TRUE;
   ic->selected_time = ecore_loop_time_get();
   if (ic->realized)
     {
        const char *selectraise;

        if (ic->sd->iop_icon)
          _e_fm2_icon_entry_widget_accept(ic->sd->iop_icon);

        edje_object_signal_emit(ic->obj, "e,state,selected", "e");
        evas_object_stack_below(ic->obj, ic->sd->drop);
        selectraise = edje_object_data_get(ic->obj, "selectraise");
        if ((selectraise) && (!strcmp(selectraise, "on")))
          evas_object_stack_below(ic->obj, ic->sd->drop);
     }
}

static void
_e_fm2_icon_deselect(E_Fm2_Icon *ic)
{
   if (!ic->selected) return;
   ic->selected = EINA_FALSE;
   ic->last_selected = EINA_FALSE;
   ic->sd->range_selected = NULL;
   if (ic->sd->last_selected == ic) ic->sd->last_selected = NULL;
   ic->sd->selected_icons = eina_list_remove(ic->sd->selected_icons, ic);
   ic->selected_time = 0.0;
   if (ic->realized)
     {
        const char *stacking, *selectraise;

        if (ic->entry_widget)
          _e_fm2_icon_entry_widget_del(ic);

        edje_object_signal_emit(ic->obj, "e,state,unselected", "e");
        stacking = edje_object_data_get(ic->obj, "stacking");
        selectraise = edje_object_data_get(ic->obj, "selectraise");
        if ((selectraise) && (!strcmp(selectraise, "on")))
          {
             if ((stacking) && (!strcmp(stacking, "below")))
               evas_object_stack_above(ic->obj, ic->sd->underlay);
          }
     }
}

static const char *
_e_fm2_icon_desktop_url_eval(const char *val)
{
   const char *s;
   char *path, *p;

   if (strlen(val) < 6) return NULL;
   if (strncmp(val, "file:", 5)) return NULL;
   path = (char *)val + 5;
   p = e_util_shell_env_path_eval(path);
   if (!p) return NULL;
   path = p;
   while (*path == '/')
     path++;
   path--;
   s = eina_stringshare_add(path);
   free(p);
   return s;
}

E_API const char *
e_fm2_desktop_url_eval(const char *val)
{
   return _e_fm2_icon_desktop_url_eval(val);
}

static void
_e_fm2_cb_eio_stat(void *data, Eio_File *handler EINA_UNUSED, const Eina_Stat *st)
{
   E_Fm2_Icon *ic = data;
   ic->eio = NULL;
#define FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(member) \
  ic->info.statinfo.st_##member = st->member

   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(dev);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(ino);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(mode);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(nlink);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(uid);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(gid);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(rdev);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(size);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(blksize);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(blocks);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(atime);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(mtime);
   FUCK_EINA_STAT_WHY_IS_IT_NOT_THE_SAME_AS_STAT(ctime);
}

static void
_e_fm2_cb_eio_err(void *data, Eio_File *handler EINA_UNUSED, int error EINA_UNUSED)
{
   E_Fm2_Icon *ic = data;
   ic->eio = NULL;
}

static int
_e_fm2_icon_desktop_load(E_Fm2_Icon *ic)
{
   char buf[PATH_MAX];
   Efreet_Desktop *desktop;

   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf)))
     return 0;

   desktop = efreet_desktop_new(buf);
//   printf("efreet_desktop_new(%s) = %p\n", buf, desktop);
   if (!desktop) goto error;
//   if (desktop->type != EFREET_DESKTOP_TYPE_LINK) goto error;

   ic->info.removable = EINA_FALSE;
   ic->info.removable_full = EINA_FALSE;
   ic->info.label = eina_stringshare_add(desktop->name);
   ic->info.generic = eina_stringshare_add(desktop->generic_name);
   ic->info.comment = eina_stringshare_add(desktop->comment);
   ic->info.icon = eina_stringshare_add(desktop->icon);
   if (desktop->url)
     ic->info.link = _e_fm2_icon_desktop_url_eval(desktop->url);
   if (ic->info.link)
     {
        if (!ic->eio)
          ic->eio = eio_file_direct_stat(ic->info.link, _e_fm2_cb_eio_stat, _e_fm2_cb_eio_err, ic);
     }
   if (desktop->x)
     {
        const char *type;

        type = eina_hash_find(desktop->x, "X-Enlightenment-Type");
        if (type)
          {
             if (!strcmp(type, "Mount")) ic->info.mount = EINA_TRUE;
             else if (!strcmp(type, "Removable"))
               {
                  ic->info.removable = EINA_TRUE;
                  if ((!e_fm2_device_storage_find(ic->info.link)) &&
                      (!e_fm2_device_volume_find(ic->info.link)))
                    {
                       /* delete .desktop for non existing device */
                       if (ecore_file_remove(buf))
                         _e_fm2_live_file_del(ic->sd->obj, ic->info.file);
                       else /* ignore */
                         _e_fm2_file_del(ic->sd->obj, ic->info.file);

                       efreet_desktop_free(desktop);
                       goto error;
                    }
               }
             type = eina_hash_find(desktop->x, "X-Enlightenment-Removable-State");
             if (type)
               {
                  if (!strcmp(type, "Full"))
                    ic->info.removable_full = EINA_TRUE;
               }
          }
     }
   /* FIXME: get category */
   ic->info.category = NULL;
   efreet_desktop_free(desktop);

   return 1;
error:
   eina_stringshare_del(ic->info.label);
   eina_stringshare_del(ic->info.comment);
   eina_stringshare_del(ic->info.generic);
   eina_stringshare_del(ic->info.icon);
   eina_stringshare_del(ic->info.link);
   eina_stringshare_del(ic->info.category);
   ic->info.label = NULL;
   ic->info.comment = NULL;
   ic->info.generic = NULL;
   ic->info.icon = NULL;
   ic->info.link = NULL;
   ic->info.category = NULL;
   //Hack
   if (!strncmp(ic->info.file, "|storage_", 9)) ecore_file_unlink(buf);
   return 0;
}

/**************************/
static E_Fm2_Region *
_e_fm2_region_new(E_Fm2_Smart_Data *sd)
{
   E_Fm2_Region *rg;

   rg = E_NEW(E_Fm2_Region, 1);
   rg->sd = sd;
   return rg;
}

static void
_e_fm2_region_free(E_Fm2_Region *rg)
{
   E_Fm2_Icon *ic;

   EINA_LIST_FREE(rg->list, ic)
     ic->region = NULL;
   free(rg);
}

static void
_e_fm2_region_realize(E_Fm2_Region *rg)
{
   const Eina_List *l;
   E_Fm2_Icon *ic;

   if (rg->realized) return;
   /* actually create evas objects etc. */
   rg->realized = 1;
   edje_freeze();
   EINA_LIST_FOREACH(rg->list, l, ic)
     _e_fm2_icon_realize(ic);
   EINA_LIST_FOREACH(rg->list, l, ic)
     {
        if (ic->selected)
          evas_object_stack_below(ic->obj, ic->sd->drop);
     }
   edje_thaw();
}

static void
_e_fm2_region_unrealize(E_Fm2_Region *rg)
{
   Eina_List *l;
   E_Fm2_Icon *ic;

   if (!rg->realized) return;
   /* delete evas objects */
   rg->realized = 0;
   edje_freeze();
   EINA_LIST_FOREACH(rg->list, l, ic)
     _e_fm2_icon_unrealize(ic);
   edje_thaw();
}

static int
_e_fm2_region_visible(E_Fm2_Region *rg)
{
   /* return if the icon is visible */
   if (
     ((rg->x - rg->sd->pos.x) < (rg->sd->w + OVERCLIP)) &&
     ((rg->x + rg->w - rg->sd->pos.x) > (-OVERCLIP)) &&
     ((rg->y - rg->sd->pos.y) < (rg->sd->h + OVERCLIP)) &&
     ((rg->y + rg->h - rg->sd->pos.y) > (-OVERCLIP))
     )
     return 1;
   return 0;
}

static void
_e_fm2_icon_make_visible(E_Fm2_Icon *ic)
{
   if (_e_fm2_view_mode_get(ic->sd) == E_FM2_VIEW_MODE_LIST)
     {
        if (
          ((ic->y - ic->sd->pos.y) >= 0) &&
          ((ic->y + ic->h - ic->sd->pos.y) <= (ic->sd->h))
          )
          return;
        if ((ic->y - ic->sd->pos.y) < 0)
          e_fm2_pan_set(ic->sd->obj, ic->sd->pos.x, ic->y);
        else
          e_fm2_pan_set(ic->sd->obj, ic->sd->pos.x, ic->y - ic->sd->h + ic->h);
     }
   else
     {
        Evas_Coord x, y;

        if (
          ((ic->y - ic->sd->pos.y) >= 0) &&
          ((ic->y + ic->h /* + ICON_BOTTOM_SPACE*/ - ic->sd->pos.y) <= (ic->sd->h)) &&
          ((ic->x - ic->sd->pos.x) >= 0) &&
          ((ic->x + ic->w - ic->sd->pos.x) <= (ic->sd->w))
          )
          return;
        x = ic->sd->pos.x;
        if ((ic->x - ic->sd->pos.x) < 0)
          x = ic->x;
        else if ((ic->x + ic->w - ic->sd->pos.x) > (ic->sd->w))
          x = ic->x + ic->w - ic->sd->w;
        y = ic->sd->pos.y;
        if ((ic->y - ic->sd->pos.y) < 0)
          y = ic->y;
        else if ((ic->y + ic->h /* + ICON_BOTTOM_SPACE*/ - ic->sd->pos.y) > (ic->sd->h))
          y = ic->y + ic->h /* + ICON_BOTTOM_SPACE*/ - ic->sd->h;
        e_fm2_pan_set(ic->sd->obj, x, y);
     }
   evas_object_smart_callback_call(ic->sd->obj, "pan_changed", NULL);
}

static void
_e_fm2_icon_desel_any(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l, *ll;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   EINA_LIST_FOREACH_SAFE(sd->selected_icons, l, ll, ic)
     _e_fm2_icon_deselect(ic);
}

static E_Fm2_Icon *
_e_fm2_icon_first_selected_find(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   return eina_list_data_get(sd->selected_icons);
}

static void
_e_fm2_icon_sel_first(Evas_Object *obj, Eina_Bool add)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->icons) return;
   if ((!add) || sd->config->selection.single)
     _e_fm2_icon_desel_any(obj);
   ic = eina_list_data_get(sd->icons);
   _e_fm2_icon_select(ic);
   evas_object_smart_callback_call(sd->obj, "selection_change", NULL);
   _e_fm2_icon_make_visible(ic);
}

static void
_e_fm2_icon_sel_last(Evas_Object *obj, Eina_Bool add)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->icons) return;
   if ((!add) || sd->config->selection.single)
     _e_fm2_icon_desel_any(obj);
   ic = eina_list_last_data_get(sd->icons);
   _e_fm2_icon_select(ic);
   evas_object_smart_callback_call(sd->obj, "selection_change", NULL);
   _e_fm2_icon_make_visible(ic);
}

static void
_e_fm2_icon_sel_any(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;
   Eina_List *l;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->icons) return;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     if (!ic->selected) _e_fm2_icon_select(ic);
}

static E_Fm2_Icon *
_e_fm2_icon_next_find(Evas_Object *obj, int next, int (*match_func)(E_Fm2_Icon *ic, void *data), void *data)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;
   E_Fm2_Icon *ic, *ic_next;
   char view_mode;
   int x = 0, y = 0, custom = 0;
   int dist = 0, min = 65535;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   if (!sd->icons) return NULL;
   
   l = eina_list_data_find_list(sd->icons, (sd->range_selected) ?
                                     sd->range_selected :
                                     sd->last_selected);
                                     
   if (!next) return eina_list_data_get(l);
   
   if (!l) return (next == 1) ? eina_list_data_get(sd->icons) : 
                                eina_list_last_data_get(sd->icons);

   view_mode = _e_fm2_view_mode_get(sd);
   if ((view_mode == E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS) ||
       (view_mode == E_FM2_VIEW_MODE_CUSTOM_GRID_ICONS) ||
       (view_mode == E_FM2_VIEW_MODE_CUSTOM_ICONS))
     custom = 1;
   
   ic_next = NULL;
   
   if (custom || match_func)
     {
        ic = eina_list_data_get(l);
        x = ic->x;
        y = ic->y;
     
        EINA_LIST_FOREACH(sd->icons, l, ic)
          {
             if (match_func && !match_func(ic, data)) continue;
             
             int dx = (ic->x - x);
             int dy = (ic->y - y);
             int sgnx = (dx) ? dx / abs(dx) : 0;
             
             if ((next == sgnx) && (abs(dx) >= abs(dy)))
               {
                  dist = abs(dy) + abs(dx);
                  if (dist < min)
                    {
                       min = dist;
                       ic_next = ic;
                    }
               }
          }
     }
   else
     {
        ic_next = eina_list_data_get((next == 1) ? eina_list_next(l) :
                                                   eina_list_prev(l));
     }

   return ic_next;
}

static void
_e_fm2_icon_sel_prev(Evas_Object *obj, Eina_Bool add)
{
   E_Fm2_Icon *ic_prev;

   ic_prev = _e_fm2_icon_next_find(obj, -1, NULL, NULL);

   if (!ic_prev) return;

   if ((!add) || ic_prev->sd->config->selection.single)
     {
        _e_fm2_icon_desel_any(obj);
        _e_fm2_icon_select(ic_prev);
     }
   else
     {
        _e_fm2_icon_range_select(ic_prev);
     }
   
   evas_object_smart_callback_call(obj, "selection_change", NULL);
   _e_fm2_icon_make_visible(ic_prev);
}

static void
_e_fm2_icon_sel_next(Evas_Object *obj, Eina_Bool add)
{
   E_Fm2_Icon *ic_next;

   ic_next = _e_fm2_icon_next_find(obj, 1, NULL, NULL);
   
   if (!ic_next) return;

   if ((!add) || ic_next->sd->config->selection.single)
     {
        _e_fm2_icon_desel_any(obj);
        _e_fm2_icon_select(ic_next);
     }
   else
     {
        _e_fm2_icon_range_select(ic_next);
     }
   evas_object_smart_callback_call(obj, "selection_change", NULL);
   _e_fm2_icon_make_visible(ic_next);
}

static void
_e_fm2_icon_sel_down(Evas_Object *obj, Eina_Bool add)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;
   E_Fm2_Icon *ic, *ic2, *ic_down;
   int dist, min = 65535;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->icons) return;

   ic = (sd->range_selected) ? sd->range_selected : sd->last_selected;
  
   if (ic)
     {
       ic_down = ic;
       EINA_LIST_FOREACH(sd->icons, l, ic2)
         {
            int dx = (ic2->x - ic->x);
            int dy = (ic2->y - ic->y);
            
            if ((dy > 0) && (abs(dy) > abs(dx)))
              {
                 dist = abs(dx)+abs(dy);
                 if (dist < min)
                   {
                      min = dist;
                      ic_down = ic2;
                   }
              }
         }
     }
   else 
     {
        ic_down = eina_list_data_get(sd->icons);
     }
   
   if ((!add) || ic_down->sd->config->selection.single)
     {
        _e_fm2_icon_desel_any(obj);
        _e_fm2_icon_select(ic_down);
     }
   else
     {
        _e_fm2_icon_range_select(ic_down);
     }

   evas_object_smart_callback_call(sd->obj, "selection_change", NULL);
   _e_fm2_icon_make_visible(ic_down);
}

static void
_e_fm2_icon_sel_up(Evas_Object *obj, Eina_Bool add)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;
   E_Fm2_Icon *ic, *ic2, *ic_down;
   int dist, min = 65535;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->icons) return;

   ic = (sd->range_selected) ? sd->range_selected : sd->last_selected;
   
   if (ic)
     {
        ic_down = ic;
        EINA_LIST_FOREACH(sd->icons, l, ic2)
          {
             int dx = (ic2->x - ic->x);
             int dy = (ic2->y - ic->y);
              
             if ((dy < 0) && (abs(dy) > abs(dx)))
               {
                  dist = abs(dx) + abs(dy);

                  if (dist < min)
                    {
                       min = dist;
                       ic_down = ic2;
                    }
               }
           }
     }
   else
     {
        ic_down = eina_list_last_data_get(sd->icons);
     }
   
   if ((!add) || ic_down->sd->config->selection.single)
     {
        _e_fm2_icon_desel_any(obj);
        _e_fm2_icon_select(ic_down);
     }
   else
     {
        _e_fm2_icon_range_select(ic_down);
     }

   evas_object_smart_callback_call(sd->obj, "selection_change", NULL);
   _e_fm2_icon_make_visible(ic_down);
}

static void
_e_fm2_icon_range_select(E_Fm2_Icon *ic)
{
   const Eina_List *l;
   E_Fm2_Icon *ic2;
   E_Fm2_Icon *last;
   
   char view_mode = _e_fm2_view_mode_get(ic->sd);
   
   if (!ic->sd->range_selected)
     {
        last = ic->sd->last_selected;
        ic->sd->range_select_anchor = last; 
     }
   else
     {
        last = ic->sd->range_select_anchor;
     }
     
   _e_fm2_icon_desel_any(ic->sd->obj);
   
   if ((!last) || (last == ic))
     {
        _e_fm2_icon_select(ic);
        ic->sd->range_select_anchor = ic; 
     }
   else if ((view_mode == E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS) ||
            (view_mode == E_FM2_VIEW_MODE_CUSTOM_GRID_ICONS) ||
            (view_mode == E_FM2_VIEW_MODE_CUSTOM_ICONS))
     {
        int topx = (ic->x <= last->x) ? ic->x : last->x;
        int boundx = (ic->x < last->x) ? (last->x + last->w) : (ic->x + ic->w);
        int topy = (ic->y <= last->y) ? ic->y : last->y;
        int boundy = (ic->y < last->y) ? (last->y + last->h) : (ic->y + ic->h);
         
        EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
          {
             if ((ic2->x >= topx) && (ic2->x < boundx) &&
                 (ic2->y >= topy) && (ic2->y < boundy))
               _e_fm2_icon_select(ic2);
          }
     }
   else
     {
        int trig = 2;
        EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
          {
             if ((ic2 == last) || (ic2 == ic)) trig--;
             if ((trig < 2)) _e_fm2_icon_select(ic2);
             if (!trig) break;
          }
     }
  
   ic->sd->range_selected = ic;
}

/* FIXME: prototype */
static void
_e_fm2_typebuf_show(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->typebuf.disabled) return;
   E_FREE(sd->typebuf.buf);
   sd->typebuf.buf = strdup("");
   edje_object_part_text_set(sd->overlay, "e.text.typebuf_label", sd->typebuf.buf);
   edje_object_signal_emit(sd->overlay, "e,state,typebuf,start", "e");
   sd->typebuf_visible = EINA_TRUE;
}

static void
_e_fm2_typebuf_hide(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->typebuf_visible) return;
   if (sd->typebuf.setting) return;
   E_FREE(sd->typebuf.buf);
   edje_object_signal_emit(sd->overlay, "e,state,typebuf,stop", "e");
   eina_stringshare_replace(&sd->typebuf.start, NULL);
   sd->typebuf_visible = EINA_FALSE;
   sd->typebuf.wildcard = 0;
   if (sd->typebuf.timer) ecore_timer_del(sd->typebuf.timer);
   sd->typebuf.timer = NULL;
}

#if 0
static void
_e_fm2_typebuf_history_prev(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* FIXME: do */
}

static void
_e_fm2_typebuf_history_next(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   /* FIXME: do */
}

#endif

static int
_e_fm2_inplace_open(const E_Fm2_Icon *ic)
{
   char buf[PATH_MAX];

   if (((!S_ISDIR(ic->info.statinfo.st_mode)) ||
        (ic->info.link && (!S_ISDIR(ic->info.statinfo.st_mode))) ||
        (!ic->sd->config->view.open_dirs_in_place) ||
        (ic->sd->config->view.no_subdir_jump)))
     return 0;

   if (!_e_fm2_icon_path(ic, buf, sizeof(buf)))
     return -1;

   e_fm2_path_set(ic->sd->obj,
                  ic->info.real_link ? "/" : ic->sd->dev,
                  buf);

   return 1;
}

static void
_e_fm2_typebuf_run(Evas_Object *obj)
{
   E_Fm2_Icon *ic;

   _e_fm2_typebuf_hide(obj);
   ic = _e_fm2_icon_first_selected_find(obj);
   if (ic)
     {
        if (_e_fm2_inplace_open(ic) == 0)
          evas_object_smart_callback_call(ic->sd->obj, "selected", NULL);
     }
}

static int
_e_fm2_typebuf_match_func(E_Fm2_Icon *ic, void *data)
{
   char *s, *tb = data;
   s = strrchr(tb, '/');
   if (s) tb = s + 1;
   return ((ic->info.label) &&
           (e_util_glob_case_match(ic->info.label, tb))) ||
          ((ic->info.file) &&
           (e_util_glob_case_match(ic->info.file, tb)));
}

static Eina_Bool
_e_fm_typebuf_timer_cb(void *data)
{
   E_Fm2_Smart_Data *sd = data;

   if (!sd) return ECORE_CALLBACK_CANCEL;
   sd->typebuf.timer = NULL;
   if (!sd->typebuf_visible) return ECORE_CALLBACK_CANCEL;

   _e_fm2_typebuf_hide(sd->obj);
   return ECORE_CALLBACK_CANCEL;
}

static E_Fm2_Icon *
_e_fm2_typebuf_match(Evas_Object *obj, int next)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic, *ic_match = NULL;
   Eina_List *l, *sel = NULL;
   char *tb;
   int tblen, x;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return NULL;
   if (sd->typebuf.disabled) return NULL;
   if (!sd->typebuf.buf) return NULL;
   if (!sd->icons) return NULL;

   tblen = strlen(sd->typebuf.buf);
   if (sd->typebuf.buf[tblen - 1] != '*')
     {
        tb = alloca(tblen + 2);
        memcpy(tb, sd->typebuf.buf, tblen);
        tb[tblen] = '*';
        tb[tblen + 1] = '\0';
     }
   else
     {
        size_t blen = strlen(sd->typebuf.buf);
        tb = memcpy(alloca(blen + 1), sd->typebuf.buf, blen + 1);
     }

   if (!next)
     {
        EINA_LIST_FOREACH(sd->icons, l, ic)
          {
             if (_e_fm2_typebuf_match_func(ic, tb))
               {
                  sel = eina_list_append(sel, ic);
                  if (!sd->typebuf.wildcard) break;
               }
          }
        if (eina_list_count(sel) == 1)
          ic_match = eina_list_data_get(sel);
     }
   else
     {
        ic_match = _e_fm2_icon_next_find(obj, next, &_e_fm2_typebuf_match_func, tb);
     }

   for (x = 0; x < 2; x++)
     {
        switch (x)
          {
           case 0:
             if (!sd->selected_icons) continue;
             if (eina_list_count(sel) != eina_list_count(sd->selected_icons)) continue;
             EINA_LIST_FOREACH(sd->selected_icons, l, ic)
               if (!eina_list_data_find(sel, ic))
                 {
                    x++;
                    break;
                 }
             if (!x)
               {
                  /* selections are identical, don't change */
                  _e_fm2_icon_make_visible(eina_list_data_get(sel));
                  sel = eina_list_free(sel);
                  x++;
                  break;
               }
             EINA_FALLTHROUGH;
             /* no break */

           case 1:
             _e_fm2_icon_desel_any(obj);
             if (sel)
               {
                  if (!ic_match) ic_match = eina_list_data_get(sel);
                  _e_fm2_icon_make_visible(eina_list_data_get(sel));
                  EINA_LIST_FREE(sel, ic)
                    _e_fm2_icon_select(ic);
               }
             evas_object_smart_callback_call(obj, "selection_change", NULL);
          }
     } while (0)
     ;

   if (sd->typebuf.timer) ecore_timer_loop_reset(sd->typebuf.timer);
   else sd->typebuf.timer = ecore_timer_loop_add(3.5, _e_fm_typebuf_timer_cb, sd);
   return ic_match;
}

static void
_e_fm2_typebuf_complete(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->typebuf.disabled) return;
   if ((!sd->typebuf.buf) || (!sd->typebuf.buf[0])) return;
   ic = _e_fm2_typebuf_match(obj, 0);
   if (!ic) return;
   if ((sd->typebuf.buf[0] == '/') || (!memcmp(sd->typebuf.buf, "~/", 2)))
     {
        char *buf, *s;
        size_t size;

        s = strrchr(sd->typebuf.buf, '/');
        if (!s) return;
        s++;
        s[0] = 0;
        size = s - sd->typebuf.buf + strlen(ic->info.file) + 1;
        buf = malloc(size);
        if (!buf) return;
        snprintf(buf, size, "%s%s", sd->typebuf.buf, ic->info.file);
        free(sd->typebuf.buf);
        sd->typebuf.buf = buf;
        edje_object_part_text_set(sd->overlay, "e.text.typebuf_label", sd->typebuf.buf);
        evas_object_smart_callback_call(sd->obj, "typebuf_changed", sd->typebuf.buf);
     }
   else
     {
        free(sd->typebuf.buf);
        sd->typebuf.buf = strdup(ic->info.file);
        if (!sd->typebuf.buf)
          {
             edje_object_part_text_set(sd->overlay, "e.text.typebuf_label", "");
             return;
          }
        eina_stringshare_replace(&sd->typebuf.start, sd->realpath);
        edje_object_part_text_set(sd->overlay, "e.text.typebuf_label", sd->typebuf.buf);
        evas_object_smart_callback_call(sd->obj, "typebuf_changed", sd->typebuf.buf);
     }
}

static void
_e_fm2_typebuf_char_append(Evas_Object *obj, const char *ch)
{
   E_Fm2_Smart_Data *sd;
   char *ts;
   size_t len;

   if ((!ch) || (!ch[0])) return;
   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->typebuf.disabled) return;
   if (!sd->typebuf.buf) return;
   len = strlen(sd->typebuf.buf) + strlen(ch);
   ts = malloc(len + 1);
   if (!ts) return;
   strcpy(ts, sd->typebuf.buf);
   strcat(ts, ch);
   free(sd->typebuf.buf);
   sd->typebuf.buf = ts;
   if (ch[0] == '*')
     sd->typebuf.wildcard++;
   _e_fm2_typebuf_match(obj, 0);
   if ((!sd->config->view.no_typebuf_set) && (ch[0] == '/'))
     {
        if (sd->typebuf.buf[0] == '/')
          {
             if (ecore_file_is_dir(sd->typebuf.buf))
               {
                  sd->typebuf.setting = EINA_TRUE;
                  e_fm2_path_set(obj, "/", sd->typebuf.buf);
                  sd->typebuf.setting = EINA_FALSE;
               }
          }
        else if (sd->typebuf.buf[0] != '~')
          {
             char buf[PATH_MAX];

             if (!sd->typebuf.start) sd->typebuf.start = eina_stringshare_ref(sd->realpath);
             snprintf(buf, sizeof(buf), "%s/%s", sd->typebuf.start, sd->typebuf.buf);
             if (ecore_file_is_dir(buf))
               {
                  sd->typebuf.setting = EINA_TRUE;
                  e_fm2_path_set(obj, "/", buf);
                  sd->typebuf.setting = EINA_FALSE;
               }
          }
        else if (!memcmp(sd->typebuf.buf, "~/", 2))
          {
             char buf[PATH_MAX];

             snprintf(buf, sizeof(buf), "%s/%s", getenv("HOME"), sd->typebuf.buf + 2);
             if (ecore_file_is_dir(buf))
               {
                  sd->typebuf.setting = EINA_TRUE;
                  e_fm2_path_set(obj, "~/", sd->typebuf.buf + 1);
                  sd->typebuf.setting = EINA_FALSE;
               }
          }
     }
   edje_object_part_text_set(sd->overlay, "e.text.typebuf_label", sd->typebuf.buf);
   evas_object_smart_callback_call(sd->obj, "typebuf_changed", sd->typebuf.buf);
}

static void
_e_fm2_typebuf_char_backspace(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   int len, p, dec;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->typebuf.disabled) return;
   if (!sd->typebuf.buf) return;
   len = strlen(sd->typebuf.buf);
   if (len == 0)
     {
        _e_fm2_typebuf_hide(obj);
        return;
     }
   p = evas_string_char_prev_get(sd->typebuf.buf, len, &dec);
   if (p < 0) return;
   sd->typebuf.buf[p] = 0;
   len--;
   if (dec == '*')
     sd->typebuf.wildcard--;
   if (len) _e_fm2_typebuf_match(obj, 0);
   if ((!sd->config->view.no_typebuf_set) && (dec == '/'))
     {
        if ((len > 1) || (sd->typebuf.buf[0] == '/'))
          {
             if (ecore_file_is_dir(sd->typebuf.buf))
               {
                  sd->typebuf.setting = EINA_TRUE;
                  e_fm2_path_set(obj, "/", sd->typebuf.buf);
                  sd->typebuf.setting = EINA_FALSE;
               }
          }
        else if ((len > 1) || (sd->typebuf.buf[0] != '~'))
          {
             char buf[PATH_MAX];

             if (!sd->typebuf.start) sd->typebuf.start = eina_stringshare_ref(sd->realpath);
             snprintf(buf, sizeof(buf), "%s/%s", sd->typebuf.start, sd->typebuf.buf);
             if (ecore_file_is_dir(buf))
               {
                  sd->typebuf.setting = EINA_TRUE;
                  e_fm2_path_set(obj, "/", buf);
                  sd->typebuf.setting = EINA_FALSE;
               }
          }
        else if (!memcmp(sd->typebuf.buf, "~/", 2))
          {
             char buf[PATH_MAX];

             snprintf(buf, sizeof(buf), "%s/%s", getenv("HOME"), sd->typebuf.buf + 2);
             if (ecore_file_is_dir(buf))
               {
                  sd->typebuf.setting = EINA_TRUE;
                  e_fm2_path_set(obj, "~/", sd->typebuf.buf + 1);
                  sd->typebuf.setting = EINA_FALSE;
               }
          }
     }
   edje_object_part_text_set(sd->overlay, "e.text.typebuf_label", sd->typebuf.buf);
   evas_object_smart_callback_call(sd->obj, "typebuf_changed", sd->typebuf.buf);
}

/**************************/

/* FIXME: prototype + reposition + implement */
static void
_e_fm2_dnd_drop_configure(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->drop_icon) return;
   if (sd->drop_after == -1)
     {
        evas_object_move(sd->drop_in,
                         sd->x + sd->drop_icon->x - sd->pos.x,
                         sd->y + sd->drop_icon->y - sd->pos.y);
        evas_object_resize(sd->drop_in, sd->drop_icon->w, sd->drop_icon->h);
     }
   else if (sd->drop_after)
     {
        evas_object_move(sd->drop,
                         sd->x + sd->drop_icon->x - sd->pos.x,
                         sd->y + sd->drop_icon->y - sd->pos.y + sd->drop_icon->h - 1);
        evas_object_resize(sd->drop, sd->drop_icon->w, 2);
     }
   else
     {
        evas_object_move(sd->drop,
                         sd->x + sd->drop_icon->x - sd->pos.x,
                         sd->y + sd->drop_icon->y - sd->pos.y - 1);
        evas_object_resize(sd->drop, sd->drop_icon->w, 2);
     }
}

/* FIXME: prototype + reposition + implement */
static void
_e_fm2_dnd_drop_all_show(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->drop_show)
     {
        edje_object_signal_emit(sd->drop, "e,state,unselected", "e");
        sd->drop_show = EINA_FALSE;
     }
   if (sd->drop_in_show)
     {
        edje_object_signal_emit(sd->drop_in, "e,state,unselected", "e");
        sd->drop_in_show = EINA_FALSE;
     }
   if (!sd->drop_all)
     {
        edje_object_signal_emit(sd->overlay, "e,state,drop,start", "e");
        sd->drop_all = EINA_TRUE;
     }
   sd->drop_icon = NULL;
   sd->drop_after = EINA_FALSE;
}

/* FIXME: prototype + reposition + implement */
static void
_e_fm2_dnd_drop_all_hide(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->drop_all)
     {
        edje_object_signal_emit(sd->overlay, "e,state,drop,stop", "e");
        sd->drop_all = EINA_FALSE;
     }
}

/* FIXME: prototype + reposition + implement */
static void
_e_fm2_dnd_drop_show(E_Fm2_Icon *ic, int after)
{
   int emit = 0;

   if ((ic->sd->drop_icon == ic) &&
       (ic->sd->drop_after == after)) return;
   if (((!ic->sd->drop_icon)) ||
       ((after < 0) && (ic->sd->drop_after >= 0)) ||
       ((after >= 0) && (ic->sd->drop_after < 0)))
     emit = 1;
   evas_object_smart_callback_call(ic->sd->obj, "dnd_changed", &ic->info);
   ic->sd->drop_icon = ic;
   ic->sd->drop_after = after;
   if (emit)
     {
        if (ic->sd->drop_after != -1)
          {
             edje_object_signal_emit(ic->sd->drop_in, "e,state,unselected", "e");
             edje_object_signal_emit(ic->sd->drop, "e,state,selected", "e");
             ic->sd->drop_in_show = EINA_FALSE;
             ic->sd->drop_show = EINA_TRUE;
          }
        else
          {
             edje_object_signal_emit(ic->sd->drop, "e,state,unselected", "e");
             edje_object_signal_emit(ic->sd->drop_in, "e,state,selected", "e");
             ic->sd->drop_in_show = EINA_TRUE;
             ic->sd->drop_show = EINA_FALSE;
          }
     }
   _e_fm2_dnd_drop_all_hide(ic->sd->obj);
   _e_fm2_dnd_drop_configure(ic->sd->obj);
}

/* FIXME: prototype + reposition + implement */
static void
_e_fm2_dnd_drop_hide(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (sd->drop_show)
     {
        edje_object_signal_emit(sd->drop, "e,state,unselected", "e");
        sd->drop_show = EINA_FALSE;
     }
   if (sd->drop_in_show)
     {
        edje_object_signal_emit(sd->drop_in, "e,state,unselected", "e");
        sd->drop_in_show = EINA_FALSE;
     }
   sd->drop_icon = NULL;
   sd->drop_after = EINA_FALSE;
}

/* FIXME: prototype + reposition + implement */
static Eina_Bool
_e_fm2_dnd_type_implemented(const char *type)
{
   const char ***t;

   for (t = _e_fm2_dnd_types; *t; t++)
     {
        if (type == **t) return EINA_TRUE;
     }
   return EINA_FALSE;
}

static void
_e_fm2_dnd_finish(Evas_Object *obj, int refresh)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;
   const Eina_List *l;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if (!sd->drag) return;
   sd->drag = EINA_FALSE;
   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        ic->drag.src = EINA_FALSE;
        if (ic->drag.hidden) continue;
        if (ic->obj) evas_object_show(ic->obj);
        if (ic->obj_icon) evas_object_show(ic->obj_icon);
     }
   if (refresh) e_fm2_refresh(obj);
}

static Eina_Bool
_e_fm2_cb_dnd_scroller(E_Fm2_Smart_Data *sd)
{
   int cx = 0, cy = 0;
   int x = 0, y = 0, w = 0, h = 0;
   int mx = 0, my = 0;
   double px = 0.0;

#undef EFM_MAX_PIXEL_DRAG
#define EFM_MAX_PIXEL_DRAG 25

   px = EFM_MAX_PIXEL_DRAG * ecore_animator_frametime_get();
   x = y = 500;

   if (sd->w < 100)
     w = sd->w / 5;
   else if (sd->w < 500)
     w = sd->w / 8;
   else
     w = sd->w / 10;

   if (sd->h < 100)
     h = sd->h / 5;
   else if (sd->h < 500)
     h = sd->h / 8;
   else
     h = sd->h / 10;

   if (sd->dnd_current.x < w)
     {
        /* left scroll */
        x = lround((-px) * (double)(w - sd->dnd_current.x));
     }
   else if (sd->dnd_current.x > sd->w - w)
     {
        /* right scroll */
        x = lround((px) * (double)(sd->dnd_current.x - (sd->w - w)));
     }

   if (sd->dnd_current.y < h)
     {
        /* up scroll */
        y = lround((-px) * (double)(h - sd->dnd_current.y));
     }
   else if (sd->dnd_current.y > sd->h - h)
     {
        /* down scroll */
        y = lround((px) * (double)(sd->dnd_current.y - (sd->h - h)));
     }

   if ((x == 500) && (y == 500)) return EINA_TRUE;

   e_fm2_pan_get(sd->obj, &cx, &cy);
   e_fm2_pan_max_get(sd->obj, &mx, &my);
   x = MIN(cx + x, mx);
   y = MIN(cy + y, my);
   x = MAX(0, x);
   y = MAX(0, y);
   e_fm2_pan_set(sd->obj, x, y);
   if ((sd->drop_icon) &&
       (!E_INSIDE(sd->dnd_current.x, sd->dnd_current.y,
                  sd->drop_icon->x - sd->drop_icon->sd->pos.x,
                  sd->drop_icon->y - sd->drop_icon->sd->pos.y,
                  sd->drop_icon->w, sd->drop_icon->h)))
     _e_fm2_dnd_drop_hide(sd->obj);
/*
 * FIXME: this is slow and doesn't do much, need a better way...
   if (!sd->drop_icon)
     _e_fm2_cb_dnd_move(sd, NULL, NULL);
 */
#undef EFM_MAX_PIXEL_DRAG
   return EINA_TRUE;
}

static void
_e_fm2_cb_dnd_enter(void *data, const char *type, void *event)
{
   E_Event_Dnd_Enter *ev;
   E_Fm2_Smart_Data *sd = data;

   if (!_e_fm2_dnd_type_implemented(type)) return;
   ev = (E_Event_Dnd_Enter *)event;
   e_drop_handler_action_set(ev->action);
   if (!sd->dnd_scroller)
     sd->dnd_scroller = ecore_animator_add((Ecore_Task_Cb)_e_fm2_cb_dnd_scroller, sd);
   evas_object_smart_callback_call(sd->obj, "dnd_enter", NULL);
}

static Eina_Bool
_e_fm2_cb_dnd_move_helper(E_Fm2_Smart_Data *sd, E_Fm2_Icon *ic, int dx, int dy)
{
   if (!E_INSIDE(dx, dy, ic->x - ic->sd->pos.x, ic->y - ic->sd->pos.y, ic->w, ic->h)) return EINA_FALSE;
   if (ic->drag.dnd) return EINA_FALSE;
   if (!S_ISDIR(ic->info.statinfo.st_mode))
     {
        if (ic->sd->drop_icon)
          _e_fm2_dnd_drop_hide(sd->obj);
        _e_fm2_dnd_drop_all_show(sd->obj);
        return EINA_TRUE;
     }
   /* if list view */
   if (_e_fm2_view_mode_get(ic->sd) == E_FM2_VIEW_MODE_LIST)
     {
        /* if there is a .order file - we can re-order files */
        if (ic->sd->order_file)
          {
             /* if dir: */
             if (!ic->sd->config->view.no_subdir_drop)
               {
                  /* if bottom 25% or top 25% then insert between prev or next */
                  /* if in middle 50% then put in dir */
                  if (dy <= (ic->y - ic->sd->pos.y + (ic->h / 4)))
                    _e_fm2_dnd_drop_show(ic, 0);
                  else if (dy > (ic->y - ic->sd->pos.y + ((ic->h * 3) / 4)))
                    _e_fm2_dnd_drop_show(ic, 1);
                  else
                    _e_fm2_dnd_drop_show(ic, -1);
               }
             else
               {
                  /* if top 50% or bottom 50% then insert between prev or next */
                  if (dy <= (ic->y - ic->sd->pos.y + (ic->h / 2)))
                    _e_fm2_dnd_drop_show(ic, 0);
                  else
                    _e_fm2_dnd_drop_show(ic, 1);
               }
          }
        /* if we are over subdirs or files */
        else
          {
             /*
              * if it's over a dir - hilight as it will be dropped info
              * FIXME: should there be a separate highlighting function for files?
              * */
             if (!ic->sd->config->view.no_subdir_drop)
               _e_fm2_dnd_drop_show(ic, -1);
          }
     }
   else
     {
        /* if it's over a dir - hilight as it will be dropped in */
        if (!ic->sd->config->view.no_subdir_drop)
          _e_fm2_dnd_drop_show(ic, -1);
     }
   return EINA_TRUE;
}

static void
_e_fm2_cb_dnd_move(void *data, const char *type, void *event)
{
   E_Fm2_Smart_Data *sd;
   E_Event_Dnd_Move *ev;
   E_Fm2_Icon *ic;
   Eina_List *l;
   int dx, dy;

   sd = data;
   /* also called from the scroll animator with only the first param */
   if (type && (!_e_fm2_dnd_type_implemented(type))) return;
   ev = (E_Event_Dnd_Move *)event;
   if (ev)
     {
        dx = ev->x - sd->x;
        dy = ev->y - sd->y;
        sd->dnd_current.x = dx, sd->dnd_current.y = dy;
        e_drop_handler_action_set(ev->action);
     }
   else
     dx = sd->dnd_current.x, dy = sd->dnd_current.y;

   EINA_LIST_FOREACH(sd->icons, l, ic)
     if (_e_fm2_cb_dnd_move_helper(sd, ic, dx, dy)) return;
   /* FIXME: not over icon - is it within the fm view? if so drop there */
   if (E_INSIDE(dx, dy, 0, 0, sd->w, sd->h))
     {
#if 0 //this is broken since we don't allow custom list sorting
      /* if listview - it is now after last file */
        if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_LIST)
          {
             /* if there is a .order file - we can re-order files */
             if (sd->order_file)
               {
                  ic = eina_list_last_data_get(sd->icons);
                  if (ic)
                    {
                       if (!ic->drag.dnd)
                         _e_fm2_dnd_drop_show(ic, 1);
                       else
                         _e_fm2_dnd_drop_all_show(sd->obj);
                    }
                  else
                    _e_fm2_dnd_drop_all_show(sd->obj);
               }
             else
               _e_fm2_dnd_drop_all_show(sd->obj);
          }
        else
#endif
        _e_fm2_dnd_drop_all_show(sd->obj);
        return;
     }
   /* outside fm view */
   _e_fm2_dnd_drop_hide(sd->obj);
}

static void
_e_fm2_cb_dnd_leave(void *data, const char *type, void *event EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   if (!_e_fm2_dnd_type_implemented(type)) return;
   _e_fm2_dnd_drop_hide(sd->obj);
   _e_fm2_dnd_drop_all_hide(sd->obj);
   if (sd->dnd_scroller) ecore_animator_del(sd->dnd_scroller);
   sd->dnd_scroller = NULL;
   sd->dnd_current.x = sd->dnd_current.y = 0;
   evas_object_smart_callback_call(sd->obj, "dnd_leave", NULL);
   /* NOTE: DO NOT PERFORM ANY OPERATIONS USING sd AT THIS POINT!
    * things use this callback to delete the efm object
    */
}

static void
_e_fm_file_reorder(const char *file, const char *dst, const char *relative, int after)
{
   unsigned int length = strlen(file) + 1 + strlen(dst) + 1 + strlen(relative) + 1 + sizeof(after);
   char *data, *p;

   data = alloca(length);
   if (!data) return;

   p = data;

#define P(s) memcpy(p, s, strlen(s) + 1); p += strlen(s) + 1
   P(file);
   P(dst);
   P(relative);
#undef P

   memcpy(p, &after, sizeof(int));

   _e_fm_client_send_new(E_FM_OP_REORDER, data, length);
}

static void
_e_fm_icon_save_position(const char *file, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
   E_Fm2_Custom_File *cf, new;

   if (!file) return;

   cf = e_fm2_custom_file_get(file);
   if (!cf)
     {
        memset(&new, 0, sizeof(E_Fm2_Custom_File));
        cf = &new;
     }

   cf->geom.x = x;
   cf->geom.y = y;
   cf->geom.res_w = w;
   cf->geom.res_h = h;

   cf->geom.valid = 1;
   e_fm2_custom_file_set(file, cf);
   e_fm2_custom_file_flush();
}

static Eina_Bool
_e_fm_drop_menu_queue(Evas_Object *e_fm, void *args, int op)
{
   E_Volume *vol;
   E_Fm2_Device_Mount_Op *mop = args;

   vol = evas_object_data_del(e_fm, "dnd_queue");
   if (!vol) return EINA_FALSE;
   if (!vol->mounted)
     {
        /* this should get picked up by the post-mount callback */
#ifndef HAVE_WAYLAND_ONLY
        switch (op)
          {
           case 0: //copy
             mop->action = ECORE_X_ATOM_XDND_ACTION_COPY;
             break;

           case 1: //move
             mop->action = ECORE_X_ATOM_XDND_ACTION_MOVE;
             break;

           case 2: //link
             mop->action = ECORE_X_ATOM_XDND_ACTION_LINK;
             break;
          }
#endif
        return EINA_TRUE;
     }
   switch (op)
     {
      case 0: //copy
        e_fm2_client_file_copy(e_fm, mop->args);
        break;

      case 1: //move
        e_fm2_client_file_move(e_fm, mop->args);
        break;

      case 2: //link
        e_fm2_client_file_symlink(e_fm, mop->args);
        break;
     }
   vol->mount_ops = eina_inlist_remove(vol->mount_ops, EINA_INLIST_GET(mop));
   free(mop->args);
   free(mop);
   return EINA_TRUE;
}

static void
_e_fm_drop_menu_copy_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   char *args;

   args = evas_object_data_del(data, "drop_menu_data");
   if (_e_fm_drop_menu_queue(data, args, 0)) return;
   e_fm2_client_file_copy(data, args);
   free(args);
}

static void
_e_fm_drop_menu_move_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   char *args;

   args = evas_object_data_del(data, "drop_menu_data");
   if (_e_fm_drop_menu_queue(data, args, 1)) return;
   e_fm2_client_file_move(data, args);
   free(args);
}

static void
_e_fm_drop_menu_symlink_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   char *args;

   args = evas_object_data_del(data, "drop_menu_data");
   if (_e_fm_drop_menu_queue(data, args, 2)) return;
   e_fm2_client_file_symlink(data, args);
   free(args);
}

static void
_e_fm_drop_menu_abort_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   free(evas_object_data_del(data, "drop_menu_data"));
}

static void
_e_fm_drop_menu_free(void *data)
{
   Evas_Object *e_fm;

   e_fm = e_object_data_get(data);
   if (!e_fm) return;
   evas_object_data_del(e_fm, "drop_menu_data");
}

static void
_e_fm2_cb_dnd_selection_notify_post_mount_fail(E_Volume *vol)
{
   E_Fm2_Device_Mount_Op *mop;

   while (vol->mount_ops)
     {
        E_Fm2_Icon *ic;

        mop = (E_Fm2_Device_Mount_Op *)vol->mount_ops;
        ic = mop->ic;
        vol->mount_ops = eina_inlist_remove(vol->mount_ops, EINA_INLIST_GET(mop));
        /* FIXME: this can be made more clear */
        e_util_dialog_show(_("Error"), _("The recent DND operation requested for '%s' has failed."), vol->label ? : vol->udi);
        evas_object_data_del(ic->sd->obj, "dnd_queue");
        free(mop->args);
        ic->sd->mount_ops = eina_list_remove(ic->sd->mount_ops, mop);
        free(mop);
     }
}

static void
_e_fm2_cb_dnd_selection_notify_post_mount(E_Volume *vol)
{
   E_Fm2_Device_Mount_Op *mop = NULL;
   const char *mp;
   Eina_Inlist *l;

   mp = e_fm2_device_volume_mountpoint_get(vol);
   EINA_INLIST_FOREACH_SAFE(vol->mount_ops, l, mop)
     {
        E_Fm2_Icon *ic = mop->ic;

        if (mp)
          {
             mop->args = e_util_string_append_quoted(mop->args, &mop->size, &mop->length, mp);
#ifndef HAVE_WAYLAND_ONLY
             if (mop->action == ECORE_X_ATOM_XDND_ACTION_ASK)
               continue;
             else if (mop->action == ECORE_X_ATOM_XDND_ACTION_MOVE)
               e_fm2_client_file_move(ic->sd->obj, mop->args);
             else if (mop->action == ECORE_X_ATOM_XDND_ACTION_COPY)
               e_fm2_client_file_copy(ic->sd->obj, mop->args);
             else if (mop->action == ECORE_X_ATOM_XDND_ACTION_LINK)
               e_fm2_client_file_symlink(ic->sd->obj, mop->args);
#endif
          }
        else
          e_util_dialog_show(_("Error"), _("The recent DND operation requested for '%s' has failed."), vol->label ? : vol->udi);
        free(mop->args);
        vol->mount_ops = eina_inlist_remove(vol->mount_ops, EINA_INLIST_GET(mop));
        ic->sd->mount_ops = eina_list_remove(ic->sd->mount_ops, mop);
        free(mop);
     }
   eina_stringshare_del(mp);
}

static void
_e_fm2_cb_dnd_selection_notify_post_umount(E_Volume *vol)
{
   E_Fm2_Device_Mount_Op *mop;

   EINA_INLIST_FOREACH(vol->mount_ops, mop)
     {
        E_Fm2_Icon *ic = mop->ic;

        if (!ic) continue;
        if (ic->mount_timer) ecore_timer_del(ic->mount_timer);
        ic->mount_timer = NULL;
        ic->mount = NULL;
        ic->sd->mount_ops = eina_list_remove(ic->sd->mount_ops, mop);
     }
}

static Eina_Bool
_e_fm2_cb_dnd_selection_notify_post_mount_timer(E_Fm2_Icon *ic)
{
   e_fm2_device_unmount(ic->mount);
   ic->mount = NULL;
   ic->mount_timer = NULL;
   return EINA_FALSE;
}

static void
_e_fm2_cb_dnd_selection_notify(void *data, const char *type, void *event)
{
   E_Fm2_Smart_Data *sd = data;
   E_Event_Dnd_Drop *ev = event;
   E_Fm2_Icon *ic = NULL;
   Eina_List *fsel, *l, *ll, *il, *isel = NULL;
   char buf[PATH_MAX];
   const char *fp;
   Evas_Coord ox, oy;
   Evas_Object *obj;
   int adjust_icons = 0;
   char dirpath[PATH_MAX];
   char *args = NULL;
   size_t size = 0;
   size_t length = 0;
   Eina_Bool lnk = EINA_FALSE, memerr = EINA_FALSE, mnt = EINA_FALSE;
   E_Fm2_Device_Mount_Op *mop = NULL;

   if (!_e_fm2_dnd_type_implemented(type)) return;
   if (type == _e_fm2_mime_xmozurl)
     {
        const char **name, *s;
        Efreet_Desktop *desktop;
        E_Dnd_X_Moz_Url *moz = ev->data;
        unsigned int x = 0;

        EINA_INARRAY_FOREACH(moz->link_names, name)
          {
             int p;

             s = *name;
             for (p = 0; p < 7; p++)
               {
                  Eina_Bool done = EINA_FALSE;

                  if (!s[p]) break;
                  if ((s[p] == ':') && (s[p + 1] == '/'))
                    {
                       s = ecore_file_file_get(s);
                       done = EINA_TRUE;
                    }
                  if (done) break;
               }
             if (!s[0]) s = ecore_file_file_get(*name);
             if (!s)
               {
                  s = *(char**)eina_inarray_nth(moz->links, x);
                  s = ecore_file_file_get(s);
               }
             /* FIXME: should this filename be sanitized somehow? */
             if (sd->drop_icon && sd->drop_after == -1)
               {
                  //into drop_icon
                  if (S_ISDIR(sd->drop_icon->info.statinfo.st_mode))
                    {
                       if (sd->drop_icon->info.link)
                         snprintf(dirpath, sizeof(dirpath), "%s/Link to %s.desktop", sd->drop_icon->info.link, s);
                       else
                         snprintf(dirpath, sizeof(dirpath), "%s/%s/Link to %s.desktop", sd->realpath, sd->drop_icon->info.file, s);
                    }
                  else
                    snprintf(buf, sizeof(buf), "%s/Link to %s.desktop", sd->realpath, s);
               }
             else
               snprintf(buf, sizeof(buf), "%s/Link to %s.desktop", sd->realpath, s);
             if (ecore_file_exists(buf))
               {
                  e_util_dialog_show(_("Error"), _("A link to the requested URL already exists!"));
                  continue;
               }
             desktop = efreet_desktop_empty_new(buf);
             desktop->type = EFREET_DESKTOP_TYPE_LINK;
             snprintf(buf, sizeof(buf), "Link to %s", *name);
             desktop->name = strdup(buf);
             desktop->icon = strdup("text-html");
             desktop->url = strdup(*(char**)eina_inarray_nth(moz->links, x));
             efreet_desktop_save(desktop);
             efreet_desktop_free(desktop);
             x++;
          }
        return;
     }
   fsel = e_fm2_uri_path_list_get(ev->data);
   fp = eina_list_data_get(fsel);
   if (fp && sd->realpath && ((sd->drop_all) || (!sd->drop_icon)))
     {
        const char *f;

        /* if we're dropping into a link_drop view, we need to stop here so we
         * don't accidentally replace an original file with a link!
         */
        f = ecore_file_file_get(fp);
        if ((((f - fp - 1 == 0) && (!strcmp(sd->realpath, "/"))) ||
             ((f - fp - 1 > 0) && (!strncmp(sd->realpath, fp, f - fp - 1)))) &&
            ((size_t)(f - fp - 1) == strlen(sd->realpath)))
          {
#ifndef HAVE_WAYLAND_ONLY
             if ((e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_MOVE) || (sd->config->view.link_drop))
               {
                  lnk = EINA_TRUE;
                  if (_e_fm2_view_mode_get(sd) != E_FM2_VIEW_MODE_CUSTOM_ICONS)
                    goto end;
                  memerr = EINA_TRUE; // prevent actual file move op
               }
#endif
          }
     }
   if (!fsel)
     {
#ifndef HAVE_WAYLAND_ONLY
        if (e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_COPY)
          {
             /* most likely someone is trying to dnd some text to us */
             _e_fm2_new_file(sd, NULL, NULL);
             EINA_LIST_FOREACH(ev->data, l, fp)
               isel = eina_list_append(isel, strdup(fp));
             ecore_thread_global_data_add("efm_text_uri_list", isel, (Eina_Free_Cb)e_util_string_list_free, 0);
          }
#endif
        /* no files, abort! */
        return;
     }

   isel = _e_fm2_uri_selected_icon_list_get(fsel);
   ox = 0; oy = 0;
   EINA_LIST_FOREACH(isel, l, ic)
     {
        if (!ic) continue;
        if (ic->drag.src)
          {
             ox = ic->x;
             oy = ic->y;
             break;
          }
     }

   /* note - logic.
    * if drop file prefix path matches extra_file_source then it can be
    * and indirect link - dont MOVE the file just add filename to list.
    * if not literally move the file in. if move can't work - try a copy.
    * on a literal move find any fm views for the dir of the dropped file
    * and refresh those, as well as refresh current target fm dir
    */
   if (sd->drop_all) /* drop arbitrarily into the dir */
     {
        Evas_Coord x, y;

        /* move file into this fm dir */
        for (ll = fsel, il = isel; ll; ll = eina_list_next(ll), il = eina_list_next(il))
          {
             ic = eina_list_data_get(il);
             fp = eina_list_data_get(ll);
             if (!fp) continue;

             if ((ic) && (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_CUSTOM_ICONS))
               {
                  /* dnd doesn't tell me all the co-ords of the icons being dragged so i can't place them accurately.
                   * need to fix this. ev->data probably needs to become more compelx than a list of url's
                   */

                  x = ev->x + (ic->x - ox) - ic->drag.x + sd->pos.x - sd->x;
                  y = ev->y + (ic->y - oy) - ic->drag.y + sd->pos.y - sd->y;

                  if (x < 0) x = 0;
                  if (y < 0) y = 0;

                  if (sd->config->view.fit_custom_pos)
                    {
                       if ((x + ic->w) > sd->w) x = (sd->w - ic->w);
                       if ((y + ic->h) > sd->h) y = (sd->h - ic->h);
                    }

                  if (ic->sd == sd)
                    {
                       ic->x = x;
                       ic->y = y;
                       ic->saved_pos = EINA_TRUE;
                       adjust_icons = 1;
                    }

                  snprintf(buf, sizeof(buf), "%s/%s",
                           sd->realpath, ecore_file_file_get(fp));
                  _e_fm_icon_save_position(buf, x, y, sd->w, sd->h);
               }

             if (!memerr)
               {
                  args = e_util_string_append_quoted(args, &size, &length, fp);
                  if (!args) memerr = EINA_TRUE;
                  else
                    {
                       args = e_util_string_append_char(args, &size, &length, ' ');
                       if (!args) memerr = EINA_TRUE;
                       else if (ic) ic->drag.hidden = EINA_TRUE;
                    }
               }
             eina_stringshare_del(fp);
          }
        if (adjust_icons)
          {
             sd->max.w = 0;
             sd->max.h = 0;
             EINA_LIST_FOREACH(sd->icons, l, ic)
               {
                  if ((ic->x + ic->w) > sd->max.w) sd->max.w = ic->x + ic->w;
                  if ((ic->y + ic->h) > sd->max.h) sd->max.h = ic->y + ic->h;
               }
             _e_fm2_obj_icons_place(sd);
             evas_object_smart_callback_call(sd->obj, "changed", NULL);
          }

        if (!memerr)
          args = e_util_string_append_quoted(args, &size, &length, sd->realpath);
     }
   else if (sd->drop_icon) /* into or before/after an icon */
     {
        if (sd->drop_after == -1) /* put into subdir/file in icon */
          {
             /* move file into dir that this icon is for */
             for (ll = fsel, il = isel; ll && il; ll = eina_list_next(ll), il = eina_list_next(il))
               {
                  fp = eina_list_data_get(ll);
                  if (!fp) continue;
                  if (!memerr)
                    {
                       args = e_util_string_append_quoted(args, &size, &length, fp);
                       if (!args) memerr = EINA_TRUE;
                       else
                         {
                            args = e_util_string_append_char(args, &size, &length, ' ');
                            if (!args) memerr = EINA_TRUE;
                            else ic->drag.hidden = EINA_TRUE;
                         }
                    }
                  eina_stringshare_del(fp);
               }

             if (!memerr)
               {
                  while (sd->drop_icon->info.removable)
                    {
                       /* we're dropping onto a device
                        * cross your fingers and hope for good luck
                        */
                       E_Volume *vol;
                       const char *mp;

                       vol = e_fm2_device_volume_find_fast(sd->drop_icon->info.link);
                       if (!vol) break;
                       if (vol->mounted)
                         {
                            mp = e_fm2_device_volume_mountpoint_get(vol);
                            if (mp)
                              {
                                 /* luuuuuuckkyyyyyyyyy */
                                 args = e_util_string_append_quoted(args, &size, &length, mp);
                                 if (!args) memerr = EINA_TRUE;
                                 eina_stringshare_del(mp);
                                 mnt = EINA_TRUE;
                                 break;
                              }
                         }
                       else if (!sd->drop_icon->mount)
                         sd->drop_icon->mount = e_fm2_device_mount(vol, (Ecore_Cb)_e_fm2_cb_dnd_selection_notify_post_mount,
                                                                   (Ecore_Cb)_e_fm2_cb_dnd_selection_notify_post_mount_fail, (Ecore_Cb)_e_fm2_cb_dnd_selection_notify_post_umount,
                                                                   NULL, vol);

                       if (sd->drop_icon->mount_timer) ecore_timer_loop_reset(sd->drop_icon->mount_timer);
                       else sd->drop_icon->mount_timer = ecore_timer_loop_add(15., (Ecore_Task_Cb)_e_fm2_cb_dnd_selection_notify_post_mount_timer, sd->drop_icon);
#ifndef HAVE_WAYLAND_ONLY
                       if ((e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_ASK) ||
                           ((sd->config->view.link_drop) || (!sd->drop_icon)))
                         /* this here's some buuuullshit */
                         evas_object_data_set(sd->obj, "dnd_queue", vol);
                       else if (e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_MOVE)
                         /* set copy if we're over a device */
                         e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_COPY);
#endif
                       mop = e_fm2_device_mount_op_add(sd->drop_icon->mount, args, size, length);
                       mop->ic = sd->drop_icon;
                       mop->mnt = sd->drop_icon->mount;
                       sd->mount_ops = eina_list_append(sd->mount_ops, mop);
                       /*
                        * set lnk here to prevent deleting the show timer
                        */
                       mnt = lnk = EINA_TRUE;
                       break;
                    }
                  if (!mnt)
                    {
                       if (S_ISDIR(sd->drop_icon->info.statinfo.st_mode))
                         {
                            if (sd->drop_icon->info.link)
                              snprintf(dirpath, sizeof(dirpath), "%s", sd->drop_icon->info.link);
                            else
                              snprintf(dirpath, sizeof(dirpath), "%s/%s", sd->realpath, sd->drop_icon->info.file);
                         }
                       else
                         snprintf(dirpath, sizeof(dirpath), "%s", sd->realpath);

                       args = e_util_string_append_quoted(args, &size, &length, dirpath);
                       if (!args) memerr = EINA_TRUE;
                    }
               }
          }
        else if (sd->realpath)
          {
             if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_LIST && sd->order_file) /* list */
               {
                  for (ll = fsel, il = isel; ll && il; ll = eina_list_next(ll), il = eina_list_next(il))
                    {
                       fp = eina_list_data_get(ll);
                       if (!fp) continue;
                       if (!memerr)
                         {
                            snprintf(buf, sizeof(buf), "%s/%s", sd->realpath, ecore_file_file_get(fp));
                            args = e_util_string_append_quoted(args, &size, &length, fp);
                            if (!args) memerr = EINA_TRUE;
                            else
                              {
                                 args = e_util_string_append_char(args, &size, &length, ' ');
                                 if (!args) memerr = EINA_TRUE;
                                 else ic->drag.hidden = EINA_TRUE;
                              }
                         }

                       _e_fm_file_reorder(ecore_file_file_get(fp), sd->realpath, sd->drop_icon->info.file, sd->drop_after);

                       eina_stringshare_del(fp);
                    }

                  if (!memerr)
                    {
                       args = e_util_string_append_quoted(args, &size, &length, sd->realpath);
                       if (!args) memerr = EINA_TRUE;
                    }
               }
             else
               {
                  for (ll = fsel, il = isel; ll && il; ll = eina_list_next(ll), il = eina_list_next(il))
                    {
                       fp = eina_list_data_get(ll);
                       if (!fp) continue;

                       if (!memerr)
                         {
                            args = e_util_string_append_quoted(args, &size, &length, fp);
                            if (!args) memerr = EINA_TRUE;
                            else
                              {
                                 args = e_util_string_append_char(args, &size, &length, ' ');
                                 if (!args) memerr = EINA_TRUE;
                                 else ic->drag.hidden = EINA_TRUE;
                              }
                         }
                       eina_stringshare_del(fp);
                    }
                  if (!memerr)
                    {
                       args = e_util_string_append_quoted(args, &size, &length, sd->realpath);
                       if (!args) memerr = EINA_TRUE;
                    }
               }
          }
     }

   if (args)
     {
        Eina_Bool do_lnk = EINA_FALSE, do_move = EINA_FALSE, do_copy = EINA_FALSE;

#ifndef HAVE_WAYLAND_ONLY
        if (e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_COPY)
          {
             lnk = EINA_TRUE;
             if (sd->config->view.link_drop && (!sd->drop_icon))
               do_lnk = EINA_TRUE;
             else
               do_copy = EINA_TRUE;
          }
        else if (e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_MOVE)
          {
             if (sd->config->view.link_drop)
               lnk = do_lnk = EINA_TRUE;
             else
               do_move = EINA_TRUE;
          }
        else if (e_drop_handler_action_get() == ECORE_X_ATOM_XDND_ACTION_ASK)
          {
             if (sd->config->view.link_drop && (!sd->drop_icon))
               do_lnk = lnk = EINA_TRUE;
          }
        if (mnt && mop)
          mop->action = (do_lnk * ECORE_X_ATOM_XDND_ACTION_LINK) +
            (do_copy * ECORE_X_ATOM_XDND_ACTION_COPY) + (do_move * ECORE_X_ATOM_XDND_ACTION_MOVE) +
            (((!do_copy) && (!do_move) && (!do_lnk)) * ECORE_X_ATOM_XDND_ACTION_ASK);
        else if (do_lnk)
          e_fm2_client_file_symlink(sd->obj, args);
        else if (do_copy)
          e_fm2_client_file_copy(sd->obj, args);
        else if (do_move)
          e_fm2_client_file_move(sd->obj, args);
#endif
        if ((!do_lnk) && (!do_copy) && (!do_move))
          {
             e_fm2_drop_menu(sd->obj, args);
             if (mnt && mop)
               evas_object_data_set(sd->obj, "drop_menu_data", mop);
             E_LIST_FOREACH(isel, _e_fm2_cb_drag_finished_show);
          }
        if (((!mnt) || (!mop)) && (do_lnk || do_copy || do_move))
          free(args);
     }
end:
   _e_fm2_dnd_drop_hide(sd->obj);
   _e_fm2_dnd_drop_all_hide(sd->obj);
   _e_fm2_list_walking++;
   EINA_LIST_FOREACH(_e_fm2_list, l, obj)
     {
        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, obj))) continue;
        _e_fm2_dnd_finish(obj, 0);
     }
   _e_fm2_list_walking--;
   if (_e_fm2_list_walking == 0)
     {
        EINA_LIST_FREE(_e_fm2_list_remove, obj)
          {
             _e_fm2_list = eina_list_remove(_e_fm2_list, obj);
          }
     }
   eina_list_free(fsel);
   EINA_LIST_FREE(isel, ic)
     if (ic->drag.dnd_end_timer && (!lnk))
       {
          ecore_timer_del(ic->drag.dnd_end_timer);
          ic->drag.dnd_end_timer = NULL;
       }
}

static void
_e_fm2_mouse_2_handler(E_Fm2_Icon *ic, void *evas_event)
{
   int multi_sel = 1;
   Eina_List *l;
   E_Fm2_Icon *ic2;

   if (!evas_event) return;

   if (ic->sd->config->selection.single)
     multi_sel = 0;

   EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
     ic2->last_selected = 0;
   if (ic->selected)
     _e_fm2_icon_deselect(ic);
   else
     {
        if (!multi_sel)
          {
             ic2 = eina_list_data_get(ic->sd->selected_icons);
             if (ic2) _e_fm2_icon_deselect(ic2);
          }
        _e_fm2_icon_select(ic);
        _e_fm2_icon_make_visible(ic);
        ic->last_selected = EINA_TRUE;
     }
   evas_object_smart_callback_call(ic->sd->obj, "selection_change", NULL);
}

/* FIXME: prototype */
static int
_e_fm2_mouse_1_handler(E_Fm2_Icon *ic, int up, void *evas_event)
{
   Evas_Event_Mouse_Down *ed = NULL;
   Evas_Event_Mouse_Up *eu = NULL;
   Evas_Modifier *modifiers;
   int multi_sel = 0, range_sel = 0, sel_change = 0;
   static unsigned int down_timestamp = 0;

   if (!evas_event) return 0;

   if (!up)
     {
        ed = evas_event;
        modifiers = ed->modifiers;
     }
   else
     {
        eu = evas_event;
        modifiers = eu->modifiers;
     }
   if (ed && ic->sd->config->view.single_click_delay)
     down_timestamp = ed->timestamp;

   if (!ic->sd->config->selection.windows_modifiers)
     {
        if (evas_key_modifier_is_set(modifiers, "Shift"))
          range_sel = 1;
        else if (evas_key_modifier_is_set(modifiers, "Control"))
          multi_sel = 1;
     }
   else
     {
        if (evas_key_modifier_is_set(modifiers, "Control"))
          range_sel = 1;
        else if (evas_key_modifier_is_set(modifiers, "Shift"))
          multi_sel = 1;
     }
   if (ic->sd->config->selection.single)
     {
        multi_sel = 0;
        range_sel = 0;
     }

   /*
    * On mouse up, check if we want to do in place open
    */
   if ((eu) &&
       (!multi_sel) &&
       (!range_sel) &&
       (ic->sd->config->view.single_click) &&
       ((eu->timestamp - down_timestamp) > ic->sd->config->view.single_click_delay))
     {
        if (_e_fm2_inplace_open(ic) == 1) return 1;
     }

   if (range_sel)
     {
        if (!up) _e_fm2_icon_range_select(ic);
     }
   else if ((!multi_sel) && ((up) || ((!up) && (!ic->selected))))
     {
        const Eina_List *l;
        E_Fm2_Icon *ic2;
        /* desel others */
        EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
          {
             if (ic2 != ic)
               {
                  if (ic2->selected)
                    {
                       _e_fm2_icon_deselect(ic2);
                       sel_change = 1;
                    }
               }
          }
     }
   else
     {
        if (!up)
          {
             const Eina_List *l;
             E_Fm2_Icon *ic2;
             EINA_LIST_FOREACH(ic->sd->icons, l, ic2)
               ic2->last_selected = 0;
          }
     }
   if ((multi_sel) && (ic->selected))
     {
        if ((up) && (!ic->drag.dnd) && (!ic->down_sel))
          {
             sel_change = 1;
             _e_fm2_icon_deselect(ic);
          }
     }
   else
     {
        if (!up)
          {
             if (!ic->selected) sel_change = EINA_TRUE;
             _e_fm2_icon_select(ic);
             _e_fm2_icon_make_visible(ic);
             ic->down_sel = EINA_TRUE;
             ic->last_selected = EINA_TRUE;
          }
     }
   if (sel_change)
     evas_object_smart_callback_call(ic->sd->obj, "selection_change", NULL);
   if (ic->sd->config->view.single_click && (!range_sel) && (!multi_sel))
     {
        if (eu && (eu->timestamp - down_timestamp) > ic->sd->config->view.single_click_delay)
          {
             int icon_pos_x = ic->x + ic->sd->x - ic->sd->pos.x;
             int icon_pos_y = ic->y + ic->sd->y - ic->sd->pos.y;

             if (eu->output.x >= icon_pos_x && eu->output.x <= (icon_pos_x + ic->w) &&
                 eu->output.y >= icon_pos_y && eu->output.y <= (icon_pos_y + ic->h))
               evas_object_smart_callback_call(ic->sd->obj, "selected", NULL);
          }
     }
   return 0;
}

static void
_e_fm2_cb_icon_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Down *ev;
   E_Fm2_Icon *ic;

   ic = data;
   ev = event_info;

   if (ic->entry_widget)
     return;

   _e_fm2_typebuf_hide(ic->sd->obj);
   if ((ev->button == 1) && (ev->flags & EVAS_BUTTON_DOUBLE_CLICK))
     {
        /* if its a directory && open dirs in-place is set then change the dir
         * to be the dir + file */
        if (ic->sd->config->view.single_click) return;
        if (_e_fm2_inplace_open(ic) == 0)
          evas_object_smart_callback_call(ic->sd->obj, "selected", NULL);
        /* if its in file selector mode then signal that a selection has
         * taken place and dont do anything more */

        /* do the below per selected file */
        /* if its a directory and open dirs in-place is not set, then
         * signal owner that a new dir should be opened */
        /* if its a normal file - do what the mime type says to do with
         * that file type */
     }
   else if (ev->button < 3)
     {
        if (ev->button == 1)
          {
             if ((ic->sd->eobj || ic->sd->win))
               {
                  ic->drag.x = ev->output.x - ic->x - ic->sd->x + ic->sd->pos.x;
                  ic->drag.y = ev->output.y - ic->y - ic->sd->y + ic->sd->pos.y;
                  ic->drag.start = EINA_TRUE;
                  ic->drag.dnd = EINA_FALSE;
                  ic->drag.src = EINA_TRUE;
               }

             _e_fm2_mouse_1_handler(ic, 0, ev);
          }
        else
          _e_fm2_mouse_2_handler(ic, ev);
     }
   else if (ev->button == 3)
     {
        if (!ic->selected)
          {
             if (_e_fm2_mouse_1_handler(ic, 0, ev)) return;
          }
        _e_fm2_icon_menu(ic, ic->sd->obj, ev->timestamp);
     }
}

static void
_e_fm2_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;
   E_Fm2_Icon *ic = data;

   if (ic->sd->selecting) return;
   edje_object_message_signal_process(ic->obj);
   edje_object_message_signal_process(ic->obj);

   _e_fm2_typebuf_hide(ic->sd->obj);
   if ((ev->button == 1) && (!ic->drag.dnd))
     {
        if (!(ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD))
          {
             if (!ic->entry_widget)
               {
                  if (_e_fm2_mouse_1_handler(ic, 1, ev)) return;
               }
          }
        ic->drag.start = EINA_FALSE;
        ic->drag.dnd = EINA_FALSE;
        ic->drag.src = EINA_FALSE;
        ic->down_sel = EINA_FALSE;
     }
}

static Eina_Bool
_e_fm2_cb_drag_finished_show(E_Fm2_Icon *ic)
{
   ic->drag.dnd = ic->drag.src = EINA_FALSE;
   if (ic->obj) evas_object_show(ic->obj);
   if (ic->obj_icon) evas_object_show(ic->obj_icon);
   ic->drag.dnd_end_timer = NULL;
   return EINA_FALSE;
}

static void
_e_fm2_cb_drag_finished(E_Drag *drag, int dropped EINA_UNUSED)
{
   E_Fm2_Uri *uri;
   const char *p;
   char buf[PATH_MAX * 3 + 7];
   Eina_List *fms;
   Evas_Object *fm;
   int i;

   memset(buf, 0, sizeof(buf));
   for (p = drag->data, i = 0; p && *p != '\0'; p++, i++)
     {
        if (*p == '\r')
          {
             p++;
             i = -1;
             uri = _e_fm2_uri_parse(buf);
             memset(buf, 0, sizeof(buf));
             if (!uri) continue;

             fms = _e_fm2_file_fm2_find(uri->path);
             if (fms)
               {
                  const char *file;
                  E_Fm2_Icon *ic;

                  file = ecore_file_file_get(uri->path);
                  if (file)
                    {
                       EINA_LIST_FREE(fms, fm)
                         {
                            ic = _e_fm2_icon_find(fm, file);
                            if (ic && ic->drag.dnd)
                              {
                                 ic->drag.dnd = EINA_FALSE;
                                 if (ic->sd->dnd_scroller) ecore_animator_del(ic->sd->dnd_scroller);
                                 ic->sd->dnd_scroller = NULL;
                                 if (ic->drag.dnd_end_timer) ecore_timer_loop_reset(ic->drag.dnd_end_timer);
                                 else ic->drag.dnd_end_timer = ecore_timer_loop_add(0.2, (Ecore_Task_Cb)_e_fm2_cb_drag_finished_show, ic);
                                 /* NOTE:
                                  * do not touch ic after this callback; it's possible that it may have been deleted
                                  */
                                 evas_object_smart_callback_call(ic->sd->obj, "dnd_end", &ic->info);
                                 break;
                              }
                         }
                       eina_list_free(fms);
                    }
               }

             if (uri->hostname) eina_stringshare_del(uri->hostname);
             eina_stringshare_del(uri->path);
             E_FREE(uri);
          }
        else
          buf[i] = *p;
     }
   free(drag->data);
}

static void
_e_fm_drag_key_down_cb(E_Drag *drag, Ecore_Event_Key *e)
{
#ifndef HAVE_WAYLAND_ONLY
   if (!strncmp(e->keyname, "Alt", 3))
     {
        ecore_x_dnd_source_action_set(ECORE_X_ATOM_XDND_ACTION_ASK);
        e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_ASK);
        edje_object_signal_emit(drag->object, "e,state,ask", "e");
     }
   else if (!strncmp(e->key, "Shift", 5))
     {
        if (e->modifiers == ECORE_EVENT_MODIFIER_CTRL)
          {
             ecore_x_dnd_source_action_set(ECORE_X_ATOM_XDND_ACTION_ASK);
             e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_ASK);
             edje_object_signal_emit(drag->object, "e,state,ask", "e");
          }
        else
          {
             ecore_x_dnd_source_action_set(ECORE_X_ATOM_XDND_ACTION_MOVE);
             e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_MOVE);
             edje_object_signal_emit(drag->object, "e,state,move", "e");
          }
     }
   else if (!strncmp(e->key, "Control", 7))
     {
        if (e->modifiers == ECORE_EVENT_MODIFIER_SHIFT)
          {
             ecore_x_dnd_source_action_set(ECORE_X_ATOM_XDND_ACTION_ASK);
             e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_ASK);
             edje_object_signal_emit(drag->object, "e,state,ask", "e");
          }
        else
          {
             ecore_x_dnd_source_action_set(ECORE_X_ATOM_XDND_ACTION_COPY);
             e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_COPY);
             edje_object_signal_emit(drag->object, "e,state,copy", "e");
          }
     }
#endif
}

static void
_e_fm_drag_key_up_cb(E_Drag *drag, Ecore_Event_Key *e)
{
#ifndef HAVE_WAYLAND_ONLY
   Ecore_X_Atom act = ECORE_X_ATOM_XDND_ACTION_MOVE;
   /* Default action would be move. ;) */

   if (!strncmp(e->key, "Alt", 3))
     act = ECORE_X_ATOM_XDND_ACTION_MOVE;
   else if (!strncmp(e->key, "Shift", 5))
     act = ECORE_X_ATOM_XDND_ACTION_MOVE;
   else if (!strncmp(e->key, "Control", 7))
     act = ECORE_X_ATOM_XDND_ACTION_MOVE;

   ecore_x_dnd_source_action_set(act);
   e_drop_handler_action_set(act);
#endif

   edje_object_signal_emit(drag->object, "e,state,move", "e");
}

static void
_e_fm2_cb_icon_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_In *ev;
   E_Fm2_Icon *ic;

   ic = data;
   ev = event_info;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (e_comp_util_mouse_grabbed()) return;
   evas_object_smart_callback_call(ic->sd->obj, "icon_mouse_in", &ic->info);
}

static void
_e_fm2_cb_icon_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Out *ev;
   E_Fm2_Icon *ic;

   ic = data;
   ev = event_info;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   evas_object_smart_callback_call(ic->sd->obj, "icon_mouse_out", &ic->info);
}

static void
_e_fm2_cb_icon_mouse_move(void *data, Evas *e, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Move *ev;
   E_Fm2_Icon *ic;
   E_Fm2_Icon_Info *ici;

   ic = data;
   ev = event_info;

   if (ic->entry_widget) return;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (ic->sd->selecting)
     {
        /* happens when clicking precisely between icons */
        ic->drag.x = ic->drag.y = 0;
        ic->drag.start = ic->drag.dnd = ic->drag.src = EINA_FALSE;
        return;
     }
   if ((ic->drag.start) && (ic->sd->eobj || ic->sd->win))
     {
        int dx, dy;

        dx = ev->cur.output.x - (ic->drag.x + ic->x + ic->sd->x - ic->sd->pos.x);
        dy = ev->cur.output.y - (ic->drag.y + ic->y + ic->sd->y - ic->sd->pos.y);
        if (((dx * dx) + (dy * dy)) >
            (e_config->drag_resist * e_config->drag_resist))
          {
             E_Drag *d;
             Evas_Object *o = NULL, *o2 = NULL, *layout = NULL;
             const char *drag_types[] = { "text/uri-list" }, *real_path;
             char buf[PATH_MAX + 8], *p, *sel = NULL;
             Eina_Binbuf *sbuf;
             Eina_List *sl, *icons = NULL;
             size_t sel_length = 0, p_offset, p_length;
             int wx = 0, wy = 0;

             ic->sd->drag = EINA_TRUE;
             ic->drag.start = EINA_FALSE;
             real_path = e_fm2_real_path_get(ic->sd->obj);
             p_offset = eina_strlcpy(buf, real_path, sizeof(buf));
             if ((p_offset < 1) || (p_offset >= (int)sizeof(buf) - 2)) return;
             if (buf[p_offset - 1] != '/')
               {
                  buf[p_offset] = '/';
                  p_offset++;
               }
             p = buf + p_offset;
             p_length = sizeof(buf) - p_offset - 1;

             sl = e_fm2_selected_list_get(ic->sd->obj);
             if (eina_list_count(sl) > 1)
               {
                  layout = e_layout_add(e_comp->evas);
                  e_layout_freeze(layout);
                  e_layout_virtual_size_set(layout, ic->sd->w, ic->sd->h);
               }
             sbuf = eina_binbuf_new();
             EINA_LIST_FREE(sl, ici)
               {
                  const char *s;
                  size_t s_len;

                  if (eina_strlcpy(p, ici->file, p_length) >= p_length)
                    continue;
                  s = _e_fm2_uri_escape(buf);
                  if (!s) continue;
                  s_len = strlen(s);
                  eina_binbuf_append_length(sbuf, (void*)s, s_len);
                  eina_binbuf_append_length(sbuf, (void*)"\r\n", 2);
                  eina_stringshare_del(s);

                  ici->ic->drag.dnd = EINA_TRUE;
                  if (ici->ic->obj) evas_object_hide(ici->ic->obj);
                  if (ici->ic->obj_icon) evas_object_hide(ici->ic->obj_icon);

                  o = edje_object_add(e_comp->evas);
                  if (_e_fm2_view_mode_get(ici->ic->sd) == E_FM2_VIEW_MODE_LIST)
                    {
                       if (ici->ic->sd->config->icon.fixed.w)
                         {
                            if (ici->ic->odd)
                              _e_fm2_theme_edje_object_set(ici->ic->sd, o,
                                                           "base/theme/widgets",
                                                           "list_odd/fixed");
                            else
                              _e_fm2_theme_edje_object_set(ici->ic->sd, o,
                                                           "base/theme/widgets",
                                                           "list/fixed");
                         }
                       else
                         {
                            if (ici->ic->odd)
                              _e_fm2_theme_edje_object_set(ici->ic->sd, o,
                                                           "base/theme/widgets",
                                                           "list_odd/variable");
                            else
                              _e_fm2_theme_edje_object_set(ici->ic->sd, o,
                                                           "base/theme/widgets",
                                                           "list/variable");
                         }
                    }
                  else
                    {
                       if (ici->ic->sd->config->icon.fixed.w)
                         _e_fm2_theme_edje_object_set(ici->ic->sd, o,
                                                      "base/theme/fileman",
                                                      "icon/fixed");
                       else
                         _e_fm2_theme_edje_object_set(ici->ic->sd, o,
                                                      "base/theme/fileman",
                                                      "icon/variable");
                    }
                  _e_fm2_icon_label_set(ici->ic, o);
                  o2 = _e_fm2_icon_icon_direct_set(ici->ic, o,
                                                   _e_fm2_cb_icon_thumb_dnd_gen, o,
                                                   1);
                  edje_object_signal_emit(o, "e,state,selected", "e");
                  if (isedje(o2))
                    edje_object_signal_emit(o2, "e,state,selected", "e");
                  edje_object_signal_emit(o, "e,state,move", "e");
                  if (layout)
                    {
                       e_layout_pack(layout, o);
                       e_layout_child_move(o, ici->ic->x, ici->ic->y);
                       e_layout_child_resize(o, ici->ic->w, ici->ic->h);
                       evas_object_show(o);
                       icons = eina_list_append(icons, o);
                    }
                  icons = eina_list_append(icons, o2);
               }
             eina_binbuf_append_char(sbuf, 0);
             sel_length = eina_binbuf_length_get(sbuf) - 1;
             sel = (char*)eina_binbuf_string_steal(sbuf);
             eina_binbuf_free(sbuf);

             d = e_drag_new(0, 0, drag_types, 1,
                            sel, sel_length, NULL, _e_fm2_cb_drag_finished);
             d->button_mask = evas_pointer_button_down_mask_get(e);
             if (ic->sd->win)
               evas_object_geometry_get(ic->sd->win, &wx, &wy, NULL, NULL);
             if (layout)
               d->x = ic->sd->x - ic->sd->pos.x, d->y = ic->sd->y - ic->sd->pos.y;
             else
               d->x = ic->x + ic->sd->x - ic->sd->pos.x, d->y = ic->y + ic->sd->y - ic->sd->pos.y;
             d->x += wx, d->y += wy;
#ifndef HAVE_WAYLAND_ONLY
             e_drop_handler_action_set(ECORE_X_ATOM_XDND_ACTION_MOVE);
#endif
             e_drag_object_set(d, layout ?: o);
             if (layout)
               {
                  e_layout_thaw(layout);
                  e_drag_resize(d, ic->sd->w, ic->sd->h);
                  EINA_LIST_FREE(icons, o)
                    e_comp_object_util_del_list_append(d->comp_object, o);
               }
             else
               {
                  e_drag_resize(d, ic->w, ic->h);
                  e_comp_object_util_del_list_append(d->comp_object, o2);
               }
             evas_object_smart_callback_call(ic->sd->obj, "dnd_begin", &ic->info);

             e_drag_key_down_cb_set(d, _e_fm_drag_key_down_cb);
             e_drag_key_up_cb_set(d, _e_fm_drag_key_up_cb);

             e_drag_xdnd_start(d,
                               ic->drag.x + wx + ic->x + ic->sd->x - ic->sd->pos.x,
                               ic->drag.y + wy + ic->y + ic->sd->y - ic->sd->pos.y);
          }
     }
}

static void
_e_fm2_cb_icon_thumb_dnd_gen(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   Evas_Object *o;
   Evas_Coord w = 0, h = 0;
   int have_alpha;

   o = data;
   e_icon_size_get(obj, &w, &h);
   if (w + h == 0) return;
   have_alpha = e_icon_alpha_get(obj);
//   if (_e_fm2_view_mode_get(ic->sd) == E_FM2_VIEW_MODE_LIST)
   {
      evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, w, h);
   }
   edje_object_part_swallow(o, "e.swallow.icon", obj);
   if (have_alpha)
     edje_object_signal_emit(o, "e,action,thumb,gen,alpha", "e");
   else
     edje_object_signal_emit(o, "e,action,thumb,gen", "e");
}

static void
_e_fm2_cb_icon_thumb_gen(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Fm2_Icon *ic;
   const char *file;

   ic = data;

   if (e_icon_file_get(obj, &file, NULL))
     {
        int w = 0, h = 0;

        if (!ic->realized)
          return;

        e_icon_size_get(obj, &w, &h);
        if (w && h)
          {
             Eina_Bool have_alpha;
             have_alpha = e_icon_alpha_get(obj);
     //	if (_e_fm2_view_mode_get(ic->sd) == E_FM2_VIEW_MODE_LIST)
             {
                evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, w, h);
             }
             edje_object_part_swallow(ic->obj, "e.swallow.icon", obj);
             if (have_alpha)
               edje_object_signal_emit(ic->obj, "e,action,thumb,gen,alpha", "e");
             else
               edje_object_signal_emit(ic->obj, "e,action,thumb,gen", "e");
             return;
          }
     }
   ic->thumb_failed = EINA_TRUE;
   evas_object_del(obj);

   if (ic->realized)
     _e_fm2_icon_icon_set(ic);
}

#if 0
static void
_e_fm2_cb_focus_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   if (sd->iop_icon && sd->iop_icon->entry_widget)
     e_widget_focus_set(sd->iop_icon->entry_widget, 1);
}
#endif

static void
_e_fm2_cb_key_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
{
   Evas_Event_Key_Down *ev = event_info;
   E_Fm2_Smart_Data *sd = data;
   E_Fm2_Icon *ic;

   if (e_comp_util_kbd_grabbed()) return;
   if (sd->iop_icon) return;

   if (evas_key_modifier_is_set(ev->modifiers, "Control"))
     {
        if (!strcmp(ev->key, "x"))
          {
             _e_fm2_file_cut(obj);
             return;
          }
        else if (!strcmp(ev->key, "c"))
          {
             _e_fm2_file_copy(obj);
             return;
          }
        else if (!strcmp(ev->key, "v"))
          {
             _e_fm2_file_paste(obj);
             return;
          }
        else if (!strcmp(ev->key, "h"))
          {
             if (sd->show_hidden_files)
               sd->show_hidden_files = EINA_FALSE;
             else
               sd->show_hidden_files = EINA_TRUE;
             sd->inherited_dir_props = EINA_FALSE;
             _e_fm2_refresh(data, NULL, NULL);
             return;
          }
        else if (!strcmp(ev->key, "1"))
          {
             if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_GRID_ICONS)
               return;
             sd->view_mode = E_FM2_VIEW_MODE_GRID_ICONS;
             sd->inherited_dir_props = EINA_FALSE;
             _e_fm2_refresh(sd, NULL, NULL);
             return;
          }
        else if (!strcmp(ev->key, "2"))
          {
             if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_LIST)
               return;
             sd->view_mode = E_FM2_VIEW_MODE_LIST;
             sd->inherited_dir_props = EINA_FALSE;
             _e_fm2_refresh(sd, NULL, NULL);
             return;
          }
        else if (!strcmp(ev->key, "3"))
          {
             if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_CUSTOM_ICONS)
               return;
             sd->view_mode = E_FM2_VIEW_MODE_CUSTOM_ICONS;
             sd->inherited_dir_props = EINA_FALSE;
             _e_fm2_refresh(sd, NULL, NULL);
             return;
          }
     }

   if (!strcmp(ev->key, "Left"))
     {
        /* FIXME: icon mode, typebuf extras */
        /* list mode: scroll left n pix
         * icon mode: prev icon
         * typebuf mode: cursor left
         */
        _e_fm2_icon_sel_prev(obj, evas_key_modifier_is_set(ev->modifiers, "Shift"));
     }
   else if (!strcmp(ev->key, "Right"))
     {
        /* FIXME: icon mode, typebuf extras */
        /* list mode: scroll right n pix
         * icon mode: next icon
         * typebuf mode: cursor right
         */
        _e_fm2_icon_sel_next(obj, evas_key_modifier_is_set(ev->modifiers, "Shift"));
     }
   else if (!strcmp(ev->key, "Up"))
     {
        if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_LIST)
          _e_fm2_icon_sel_prev(obj, evas_key_modifier_is_set(ev->modifiers, "Shift"));
        else
          _e_fm2_icon_sel_up(obj, evas_key_modifier_is_set(ev->modifiers, "Shift"));
     }
   else if (!strcmp(ev->key, "Down"))
     {
        if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_LIST)
          _e_fm2_icon_sel_next(obj, evas_key_modifier_is_set(ev->modifiers, "Shift"));
        else
          _e_fm2_icon_sel_down(obj, evas_key_modifier_is_set(ev->modifiers, "Shift"));
     }
   else if (!strcmp(ev->key, "Home"))
     {
        /* FIXME: typebuf extras */
        /* go to first icon
         * typebuf mode: cursor to start
         */
        _e_fm2_icon_sel_first(obj, EINA_FALSE);
     }
   else if (!strcmp(ev->key, "End"))
     {
        /* FIXME: typebuf extras */
        /* go to last icon
         * typebuf mode: cursor to end
         */
        _e_fm2_icon_sel_last(obj, EINA_FALSE);
     }
   else if (!strcmp(ev->key, "Prior"))
     {
        /* up h * n pixels */
        e_fm2_pan_set(obj, sd->pos.x, sd->pos.y - sd->h);
        evas_object_smart_callback_call(sd->obj, "pan_changed", NULL);
     }
   else if (!strcmp(ev->key, "Next"))
     {
        /* down h * n pixels */
        e_fm2_pan_set(obj, sd->pos.x, sd->pos.y + sd->h);
        evas_object_smart_callback_call(sd->obj, "pan_changed", NULL);
     }
   else if (!strcmp(ev->key, "Escape"))
     {
        /* typebuf mode: end typebuf mode */
        if (sd->typebuf_visible)
          _e_fm2_typebuf_hide(obj);
        else if (sd->dev && (!strcmp(sd->dev, "desktop")))
          return;
        else
          {
             ic = _e_fm2_icon_first_selected_find(obj);
             if (ic)
               _e_fm2_icon_desel_any(obj);
             else
               {
                  if (e_fm2_has_parent_get(obj))
                    e_fm2_parent_go(obj);
               }
          }
     }
   else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
     {
        /* if selected - select callback.
         * typebuf mode: if nothing selected - run cmd
         */
        if (sd->typebuf_visible)
          _e_fm2_typebuf_run(obj);
        else
          {
             ic = _e_fm2_icon_first_selected_find(obj);
             if (ic)
               {
                  if (_e_fm2_inplace_open(ic) == 0)
                    evas_object_smart_callback_call(ic->sd->obj, "selected", NULL);
               }
          }
     }
   else if (!strcmp(ev->key, "F5"))
     e_fm2_refresh(obj);
   else if (!strcmp(ev->key, "F2"))
     {
        if (eina_list_count(sd->selected_icons) == 1)
          _e_fm2_file_rename(eina_list_data_get(sd->selected_icons), NULL, NULL);
     }
   else if (!strcmp(ev->key, "Insert"))
     {
        /* dunno what to do with this yet */
     }
   else if (!strcmp(ev->key, "Tab"))
     {
        /* typebuf mode: tab complete */
        if (sd->typebuf_visible)
          _e_fm2_typebuf_complete(obj);
     }
   else if (!strcmp(ev->key, "BackSpace"))
     {
        /* typebuf mode: backspace */
        if (sd->typebuf_visible)
          _e_fm2_typebuf_char_backspace(obj);
        else if (!sd->config->view.no_typebuf_set)
          {
             /* only allow this action when typebuf navigation is allowed in config */
             if (e_fm2_has_parent_get(obj))
               e_fm2_parent_go(obj);
          }
     }
   else if (!strcmp(ev->key, "Delete"))
     _e_fm2_file_delete(obj);
   else if (!evas_key_modifier_is_set(ev->modifiers, "Control") &&
            !evas_key_modifier_is_set(ev->modifiers, "Alt"))
     {
        if (ev->string)
          {
             if (!sd->typebuf_visible) _e_fm2_typebuf_show(obj);
             _e_fm2_typebuf_char_append(obj, ev->string);
          }
     }
}

static void
_e_fm2_cb_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Down *ev;
   E_Fm2_Smart_Data *sd;

   sd = data;
   ev = event_info;
   _e_fm2_typebuf_hide(sd->obj);
   if (sd->iop_icon)
     _e_fm2_icon_entry_widget_del(sd->iop_icon);
   if (ev->button == 1)
     {
        Eina_List *l;
        int multi_sel = 0, range_sel = 0, sel_change = 0;

        if (!sd->config->selection.windows_modifiers)
          {
             if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
               range_sel = 1;
             else if (evas_key_modifier_is_set(ev->modifiers, "Control"))
               multi_sel = 1;
          }
        else
          {
             if (evas_key_modifier_is_set(ev->modifiers, "Control"))
               range_sel = 1;
             else if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
               multi_sel = 1;
          }
        if (sd->config->selection.single)
          {
             multi_sel = 0;
             range_sel = 0;
          }
        if ((!multi_sel) && (!range_sel))
          {
             E_Fm2_Icon *ic;
             EINA_LIST_FOREACH(sd->icons, l, ic)
               {
                  if (ic->selected)
                    {
                       _e_fm2_icon_deselect(ic);
                       sel_change = 1;
                    }
               }
          }
        if (sel_change)
          evas_object_smart_callback_call(sd->obj, "selection_change", NULL);

        if (!(ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD))
          {
             if (!sd->config->selection.single)
               {
                  sd->selrect.ox = ev->canvas.x;
                  sd->selrect.oy = ev->canvas.y;
                  sd->selecting = EINA_TRUE;
               }
          }
        if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
          evas_object_smart_callback_call(sd->obj, "double_clicked", NULL);
     }
   else if (ev->button == 3)
     {
        _e_fm2_menu(sd->obj, ev->timestamp);
     }
}

static void
_e_fm2_cb_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   _e_fm2_typebuf_hide(sd->obj);
   sd->selecting = EINA_FALSE;
   sd->selrect.ox = 0;
   sd->selrect.oy = 0;
   evas_object_hide(sd->sel_rect);
}

static void
_e_fm2_cb_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Move *ev;
   E_Fm2_Smart_Data *sd;
   E_Fm2_Icon *ic;
   Eina_List *l = NULL;
   int x, y, w, h;
   int sel_change = 0;

   sd = data;
   ev = event_info;
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     {
        if (sd->selecting)
          {
             sd->selecting = EINA_FALSE;
             sd->selrect.ox = 0;
             sd->selrect.oy = 0;
             evas_object_hide(sd->sel_rect);
          }
        return;
     }
   if (!sd->selecting) return;

   if (ev->cur.canvas.x < sd->selrect.ox)
     {
        sd->selrect.x = ev->cur.canvas.x;
        sd->selrect.w = (sd->selrect.ox - sd->selrect.x);
     }
   else
     {
        sd->selrect.x = MIN(sd->selrect.ox, ev->cur.canvas.x);
        sd->selrect.w = abs(sd->selrect.x - ev->cur.canvas.x);
     }
   if (ev->cur.canvas.y < sd->selrect.oy)
     {
        sd->selrect.y = ev->cur.canvas.y;
        sd->selrect.h = (sd->selrect.oy - sd->selrect.y);
     }
   else
     {
        sd->selrect.y = MIN(sd->selrect.oy, ev->cur.canvas.y);
        sd->selrect.h = abs(sd->selrect.y - ev->cur.canvas.y);
     }
   _e_fm2_sel_rect_update(sd);

   evas_object_geometry_get(sd->sel_rect, &x, &y, &w, &h);

/*
 * Leave commented for now. Start of scrolling the sel_rect
 *
   int nx, ny, nw, nh;

   nx = sd->pos.x;
   if ((x - sd->pos.x) < 0)
     nx = x;
   else if ((x + w - sd->pos.x) > (sd->w))
     nx = x + w - sd->w;
   ny = sd->pos.y;
   if ((y - sd->pos.y) < 0)
     ny = y;
   else if ((y + h - sd->pos.y) > (sd->h))
     ny = y + h - sd->h;
   e_fm2_pan_set(sd->obj, nx, ny);
   evas_object_smart_callback_call(sd->obj, "pan_changed", NULL);
 */

   EINA_LIST_FOREACH(sd->icons, l, ic)
     {
        int ix, iy, iw, ih;
        int ix_t, iy_t, iw_t, ih_t;

        if (!ic) continue;
        evas_object_geometry_get(ic->obj_icon, &ix, &iy, &iw, &ih);
        evas_object_geometry_get(edje_object_part_object_get(ic->obj,
                                                             "e.text.label"),
                                 &ix_t, &iy_t, &iw_t, &ih_t);
        if (E_INTERSECTS(x, y, w, h, ix, iy, iw, ih) ||
            E_INTERSECTS(x, y, w, h, ix_t, iy_t, iw_t, ih_t))
          {
             if (!ic->selected)
               {
                  _e_fm2_icon_select(ic);
                  sel_change = 1;
               }
          }
        else
          {
             if (ic->selected)
               {
                  _e_fm2_icon_deselect(ic);
                  sel_change = 1;
               }
          }
     }
   if (sel_change)
     evas_object_smart_callback_call(sd->obj, "selection_change", NULL);
}

static void
_e_fm2_sel_rect_update(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   evas_object_move(sd->sel_rect, sd->selrect.x, sd->selrect.y);
   evas_object_resize(sd->sel_rect, sd->selrect.w, sd->selrect.h);
   evas_object_show(sd->sel_rect);
}

static void
_e_fm2_cb_scroll_job(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return;
   sd->scroll_job = NULL;
   evas_event_freeze(evas_object_evas_get(sd->obj));
   edje_freeze();
   _e_fm2_regions_eval(sd->obj);
   _e_fm2_obj_icons_place(sd);
   edje_thaw();
   evas_event_thaw(evas_object_evas_get(sd->obj));
   _e_fm2_dir_save_props(sd);
}

static void
_e_fm2_cb_resize_job(void *data)
{
   E_Fm2_Smart_Data *sd;
   Eina_List *l;

   sd = evas_object_smart_data_get(data);
   if (!sd) return;
   sd->resize_job = NULL;
   evas_event_freeze(evas_object_evas_get(sd->obj));
   edje_freeze();
   switch (_e_fm2_view_mode_get(sd))
     {
      case E_FM2_VIEW_MODE_ICONS:
        _e_fm2_regions_free(sd->obj);
        _e_fm2_icons_place(sd->obj);
        _e_fm2_regions_populate(sd->obj);
        break;

      case E_FM2_VIEW_MODE_GRID_ICONS:
        _e_fm2_regions_free(sd->obj);
        _e_fm2_icons_place(sd->obj);
        _e_fm2_regions_populate(sd->obj);
        break;

      case E_FM2_VIEW_MODE_CUSTOM_ICONS:
        if (sd->config->view.fit_custom_pos)
          {
             E_Fm2_Icon *ic;
             EINA_LIST_FOREACH(sd->icons, l, ic)
               {
                  ic->region = NULL;
                  _e_fm2_icon_geom_adjust(ic, ic->x, ic->y, ic->w, ic->h, sd->pw, sd->ph);
               }
          }
        _e_fm2_regions_free(sd->obj);
//	_e_fm2_regions_eval(sd->obj);
        _e_fm2_icons_place(sd->obj);
        _e_fm2_regions_populate(sd->obj);
        break;

      case E_FM2_VIEW_MODE_CUSTOM_GRID_ICONS:
        /* FIXME: not going to implement this at this stage */
        _e_fm2_regions_free(sd->obj);
//	_e_fm2_regions_eval(sd->obj);
        _e_fm2_icons_place(sd->obj);
        _e_fm2_regions_populate(sd->obj);
        break;

      case E_FM2_VIEW_MODE_CUSTOM_SMART_GRID_ICONS:
        /* FIXME: not going to implement this at this stage */
        _e_fm2_regions_free(sd->obj);
//	_e_fm2_regions_eval(sd->obj);
        _e_fm2_icons_place(sd->obj);
        _e_fm2_regions_populate(sd->obj);
        break;

      case E_FM2_VIEW_MODE_LIST:
        if (sd->iconlist_changed)
          {
             E_Fm2_Icon *ic;
             EINA_LIST_FOREACH(sd->icons, l, ic)
               {
                  ic->region = NULL;
//		  _e_fm2_icon_unrealize(ic);
               }
          }
        _e_fm2_regions_free(sd->obj);
        _e_fm2_icons_place(sd->obj);
        _e_fm2_regions_populate(sd->obj);
        break;

      default:
        break;
     }
   edje_thaw();
   evas_event_thaw(evas_object_evas_get(sd->obj));
   sd->iconlist_changed = EINA_FALSE;
   sd->pw = sd->w;
   sd->ph = sd->h;

   if ((sd->max.w > 0) && (sd->max.h > 0) && (sd->w > 0) && (sd->h > 0) && (sd->view_flags & E_FM2_VIEW_SAVE_DIR_CUSTOM))
     {
        E_Fm2_Custom_File *cf = e_fm2_custom_file_get(sd->realpath);
        if ((cf) && (cf->dir))
          {
             sd->pos.x = cf->dir->pos.x * (sd->max.w - sd->w);
             sd->pos.y = cf->dir->pos.y * (sd->max.h - sd->h);
             evas_object_smart_callback_call(sd->obj, "pan_changed", NULL);
          }
     }
}

static int
_e_fm2_cb_icon_sort(const void *data1, const void *data2)
{
   const E_Fm2_Icon *ic1, *ic2;
   char *l1, *l2;

   ic1 = data1;
   ic2 = data2;
   l1 = (char *)ic1->info.file;
   if (ic1->info.label) l1 = (char *)ic1->info.label;
   l2 = (char *)ic2->info.file;
   if (ic2->info.label) l2 = (char *)ic2->info.label;
   if (ic1->sd->config->list.sort.dirs.first)
     {
        if ((S_ISDIR(ic1->info.statinfo.st_mode)) !=
            (S_ISDIR(ic2->info.statinfo.st_mode)))
          {
             if (S_ISDIR(ic1->info.statinfo.st_mode)) return -1;
             return 1;
          }
     }
   else if (ic1->sd->config->list.sort.dirs.last)
     {
        if ((S_ISDIR(ic1->info.statinfo.st_mode)) !=
            (S_ISDIR(ic2->info.statinfo.st_mode)))
          {
             if (S_ISDIR(ic1->info.statinfo.st_mode)) return 1;
             return -1;
          }
     }
   if (ic1->sd->config->list.sort.mtime)
     {
        if (ic1->info.statinfo.st_mtime > ic2->info.statinfo.st_mtime)
          return -1;
        if (ic1->info.statinfo.st_mtime < ic2->info.statinfo.st_mtime)
          return 1;
     }
   if (ic1->sd->config->list.sort.extension)
     {
        int cmp;
        const char *f1, *f2;
        f1 = ecore_file_file_get(l1);
        f1 = strrchr(f1, '.');
        f2 = ecore_file_file_get(l2);
        f2 = strrchr(f2, '.');
        if (f1 && f2)
          {
             cmp = strcasecmp(f1, f2);
             if (cmp) return cmp;
          }
        else if (f1)
          return 1;
        else if (f2)
          return -1;
     }
   if (ic1->sd->config->list.sort.size)
     {
        if (ic1->info.link)
          {
             if (!ic2->info.link) return 1;
          }
        else
          {
             if (ic2->info.link) return -1;
             if (ic1->info.statinfo.st_size > ic2->info.statinfo.st_size)
               return -1;
             else if (ic1->info.statinfo.st_size < ic2->info.statinfo.st_size)
               return 1;
          }
     }
   if (ic1->sd->config->list.sort.no_case)
     return strcasecmp(l1, l2);
   return strcmp(l1, l2);
}

static Eina_Bool
_e_fm2_cb_scan_timer(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return ECORE_CALLBACK_CANCEL;
   _e_fm2_queue_process(data);
   sd->scan_timer = NULL;
   if (!sd->listing)
     {
        _e_fm2_client_monitor_list_end(data);
        return ECORE_CALLBACK_CANCEL;
     }
   if (sd->busy_count > 0)
     sd->scan_timer = ecore_timer_loop_add(0.2, _e_fm2_cb_scan_timer, sd->obj);
   else
     {
        if (!sd->sort_idler)
          sd->sort_idler = ecore_idler_add(_e_fm2_cb_sort_idler, data);
     }
   return ECORE_CALLBACK_CANCEL;
}

static Eina_Bool
_e_fm2_cb_sort_idler(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return ECORE_CALLBACK_CANCEL;
   _e_fm2_queue_process(data);
   if (!sd->listing)
     {
        sd->sort_idler = NULL;
        _e_fm2_client_monitor_list_end(data);
        return ECORE_CALLBACK_CANCEL;
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_fm2_cb_theme(void *data, int type EINA_UNUSED, void *event EINA_UNUSED)
{
   e_fm2_refresh(data);
   return ECORE_CALLBACK_RENEW;
}

/**************************/
static void
_e_fm2_obj_icons_place(E_Fm2_Smart_Data *sd)
{
   const Eina_List *l;
   E_Fm2_Region *rg;

   evas_event_freeze(evas_object_evas_get(sd->obj));
   edje_freeze();
   EINA_LIST_FOREACH(sd->regions.list, l, rg)
     {
        if (rg->realized)
          {
             const Eina_List *ll;
             E_Fm2_Icon *ic;

             EINA_LIST_FOREACH(rg->list, ll, ic)
               {
                  const char *th[] =
                  {
                   "list/fixed",
                   "list_odd/fixed",
                  };
                  Evas_Object *prev;

                  if (!ic->realized) continue;
                  if (!_e_fm2_icon_visible(ic))
                    {
                       e_thumb_icon_end(ic->obj_icon);
                    }
                  evas_object_move(ic->obj,
                                   sd->x + ic->x - sd->pos.x,
                                   sd->y + ic->y - sd->pos.y);
                  evas_object_resize(ic->obj, ic->w, ic->h);
                  _e_fm2_icon_thumb(ic, ic->obj_icon, 0);
                  if (_e_fm2_view_mode_get(ic->sd) != E_FM2_VIEW_MODE_LIST) continue;
                  /* FIXME: this is probably something that should be unnecessary,
                   * but currently we add icons in semi-randomly and it messes up ordering.
                   * current process is:
                   * 1 receive file info from slave
                   * 2 create icon struct
                   * 3 either directly add icon or queue it (usually queue)
                   * 4 process queue on timer, determining even/odd for all icons at this time
                   * 4.5 icons are realized, theme is set here <-- where bug occurs
                   * 5 goto 1
                   * 6 if icon is inserted into current viewport during 4.5, even/odd for surrounding items
                   *   will always be wrong (ticket #1579)
                   */

                   prev = edje_object_part_swallow_get(ic->obj, "e.swallow.icon");
                   _e_fm2_theme_edje_object_set(ic->sd, ic->obj,
                                                "base/theme/widgets",
                                                th[ic->odd]);
                   edje_object_part_swallow(ic->obj, "e.swallow.icon", prev);
                   _e_fm2_icon_label_set(ic, ic->obj);
                   if (ic->selected)
                     edje_object_signal_emit(ic->obj, "e,state,selected", "e");
               }
          }
     }
   edje_thaw();
   evas_event_thaw(evas_object_evas_get(sd->obj));
}

/**************************/

static void
_e_fm2_smart_add(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = E_NEW(E_Fm2_Smart_Data, 1);
   if (!sd) return;

   sd->view_mode = -1; /* unset */
   sd->icon_size = -1; /* unset */

   sd->obj = obj;
   sd->clip = evas_object_rectangle_add(evas_object_evas_get(obj));
   evas_object_smart_member_add(sd->clip, obj);
   evas_object_color_set(sd->clip, 255, 255, 255, 255);

   sd->underlay = evas_object_rectangle_add(evas_object_evas_get(obj));
   evas_object_clip_set(sd->underlay, sd->clip);
   evas_object_smart_member_add(sd->underlay, obj);
   evas_object_color_set(sd->underlay, 0, 0, 0, 0);
   evas_object_show(sd->underlay);

   evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, _e_fm2_cb_key_down, sd);
   //evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_IN, _e_fm2_cb_focus_in, sd);
   evas_object_event_callback_add(sd->underlay, EVAS_CALLBACK_MOUSE_DOWN, _e_fm2_cb_mouse_down, sd);
   evas_object_event_callback_add(sd->underlay, EVAS_CALLBACK_MOUSE_UP, _e_fm2_cb_mouse_up, sd);
   evas_object_event_callback_add(sd->underlay, EVAS_CALLBACK_MOUSE_MOVE, _e_fm2_cb_mouse_move, sd);

   sd->drop = edje_object_add(evas_object_evas_get(obj));
   evas_object_clip_set(sd->drop, sd->clip);
   _e_fm2_theme_edje_object_set(sd, sd->drop,
                                "base/theme/fileman",
                                "list/drop_between");
   evas_object_smart_member_add(sd->drop, obj);
   evas_object_show(sd->drop);

   sd->drop_in = edje_object_add(evas_object_evas_get(obj));
   evas_object_clip_set(sd->drop_in, sd->clip);
   _e_fm2_theme_edje_object_set(sd, sd->drop_in,
                                "base/theme/fileman",
                                "list/drop_in");
   evas_object_smart_member_add(sd->drop_in, obj);
   evas_object_show(sd->drop_in);

   sd->overlay = edje_object_add(evas_object_evas_get(obj));
   evas_object_clip_set(sd->overlay, sd->clip);
   _e_fm2_theme_edje_object_set(sd, sd->overlay,
                                "base/theme/fileman",
                                "overlay");
   evas_object_smart_member_add(sd->overlay, obj);
   evas_object_show(sd->overlay);

   sd->sel_rect = edje_object_add(evas_object_evas_get(obj));
   evas_object_clip_set(sd->sel_rect, sd->clip);
   _e_fm2_theme_edje_object_set(sd, sd->sel_rect, "base/theme/fileman",
                                "rubberband");
   evas_object_smart_member_add(sd->sel_rect, obj);

   evas_object_smart_data_set(obj, sd);
   evas_object_move(obj, 0, 0);
   evas_object_resize(obj, 0, 0);

   E_LIST_HANDLER_APPEND(sd->handlers, E_EVENT_CONFIG_ICON_THEME, _e_fm2_cb_theme, sd->obj);
   E_LIST_HANDLER_APPEND(sd->handlers, EFREET_EVENT_ICON_CACHE_UPDATE, _e_fm2_icon_cache_update, sd);

   _e_fm2_list = eina_list_append(_e_fm2_list, sd->obj);
}

static void
_e_fm2_smart_del(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;

   E_FREE_LIST(sd->handlers, ecore_event_handler_del);

   _e_fm2_client_monitor_list_end(obj);
   if (sd->realpath) _e_fm2_client_monitor_del(sd->id, sd->realpath);
   _e_fm2_live_process_end(obj);
   _e_fm2_queue_free(obj);
   _e_fm2_regions_free(obj);
   _e_fm2_icons_free(obj);
   if (sd->selected_icons) eina_list_free(sd->selected_icons);
   if (sd->menu)
     {
        e_menu_post_deactivate_callback_set(sd->menu, NULL, NULL);
        e_object_del(E_OBJECT(sd->menu));
        sd->menu = NULL;
     }
   if (sd->entry_dialog)
     {
        e_object_del(E_OBJECT(sd->entry_dialog));
        sd->entry_dialog = NULL;
     }
   if (sd->image_dialog)
     {
        e_object_del(E_OBJECT(sd->image_dialog));
        sd->image_dialog = NULL;
     }
   E_FREE_LIST(sd->rename_dialogs, e_object_del);
   if (sd->scroll_job) ecore_job_del(sd->scroll_job);
   if (sd->resize_job) ecore_job_del(sd->resize_job);
   if (sd->refresh_job) ecore_job_del(sd->refresh_job);
   eina_stringshare_del(sd->custom_theme);
   eina_stringshare_del(sd->custom_theme_content);
   sd->custom_theme = sd->custom_theme_content = NULL;
   eina_stringshare_del(sd->dev);
   eina_stringshare_del(sd->path);
   eina_stringshare_del(sd->realpath);
   eina_stringshare_del(sd->new_file.filename);
   sd->dev = sd->path = sd->realpath = NULL;
   if (sd->mount)
     {
        e_fm2_device_unmount(sd->mount);
        sd->mount = NULL;
     }
   if (sd->config) _e_fm2_config_free(sd->config);

   E_FREE(sd->typebuf.buf);
   if (sd->typebuf.timer) ecore_timer_del(sd->typebuf.timer);
   sd->typebuf.timer = NULL;
   eina_list_free(sd->mount_ops);

   evas_object_del(sd->underlay);
   evas_object_del(sd->overlay);
   evas_object_del(sd->drop);
   evas_object_del(sd->drop_in);
   evas_object_del(sd->sel_rect);
   evas_object_del(sd->clip);
   if (sd->drop_handler) e_drop_handler_del(sd->drop_handler);
   if (_e_fm2_list_walking == 0)
     _e_fm2_list = eina_list_remove(_e_fm2_list, sd->obj);
   else
     _e_fm2_list_remove = eina_list_append(_e_fm2_list_remove, sd->obj);
   if (sd->desktop) efreet_desktop_free(sd->desktop);
   sd->desktop = NULL;
   free(sd);
   e_fm2_custom_file_flush();
}

static void
_e_fm2_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if ((sd->x == x) && (sd->y == y)) return;
   sd->x = x;
   sd->y = y;
   evas_object_move(sd->underlay, sd->x, sd->y);
   if (!sd->overlay_clip)
     evas_object_move(sd->overlay, sd->x, sd->y);
   _e_fm2_dnd_drop_configure(sd->obj);
   evas_object_move(sd->clip, sd->x - OVERCLIP, sd->y - OVERCLIP);
   _e_fm2_obj_icons_place(sd);
   if (sd->drop_handler)
     e_drop_handler_geometry_set(sd->drop_handler, sd->x, sd->y, sd->w, sd->h);
}

static void
_e_fm2_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
{
   E_Fm2_Smart_Data *sd;
   Eina_Bool wch = EINA_FALSE;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   if ((sd->w == w) && (sd->h == h)) return;
   if (w != sd->w) wch = EINA_TRUE;
   sd->w = w;
   sd->h = h;
   evas_object_resize(sd->underlay, sd->w, sd->h);
   if (!sd->overlay_clip)
     evas_object_resize(sd->overlay, sd->w, sd->h);
   _e_fm2_dnd_drop_configure(sd->obj);
   evas_object_resize(sd->clip, sd->w + (OVERCLIP * 2), sd->h + (OVERCLIP * 2));

   /* for automatic layout - do this - NB; we could put this on a timer delay */
   if ((_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_LIST) ||
       (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_GRID_ICONS))
     {
        if (wch)
          {
             if (sd->resize_job) ecore_job_del(sd->resize_job);
             sd->resize_job = ecore_job_add(_e_fm2_cb_resize_job, obj);
          }
        else
          {
             if (sd->scroll_job) ecore_job_del(sd->scroll_job);
             sd->scroll_job = ecore_job_add(_e_fm2_cb_scroll_job, obj);
          }
     }
   else if (_e_fm2_view_mode_get(sd) == E_FM2_VIEW_MODE_CUSTOM_ICONS)
     {
        if (sd->config->view.fit_custom_pos)
          {
             if (sd->resize_job) ecore_job_del(sd->resize_job);
             sd->resize_job = ecore_job_add(_e_fm2_cb_resize_job, obj);
          }
        else
          {
             if (sd->scroll_job) ecore_job_del(sd->scroll_job);
             sd->scroll_job = ecore_job_add(_e_fm2_cb_scroll_job, obj);
          }
     }
   if (sd->drop_handler)
     e_drop_handler_geometry_set(sd->drop_handler, sd->x, sd->y, sd->w, sd->h);
}

static void
_e_fm2_smart_show(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_show(sd->clip);
}

static void
_e_fm2_smart_hide(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_hide(sd->clip);
}

static void
_e_fm2_smart_color_set(Evas_Object *obj, int r, int g, int b, int a)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_color_set(sd->clip, r, g, b, a);
}

static void
_e_fm2_smart_clip_set(Evas_Object *obj, Evas_Object *clip)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_clip_set(sd->clip, clip);
}

static void
_e_fm2_smart_clip_unset(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   evas_object_clip_unset(sd->clip);
}

static void
_e_fm2_overlay_clip_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   int w, h;

   evas_object_geometry_get(obj, NULL, NULL, &w, &h);
   evas_object_resize(sd->overlay, w, h);
}

static void
_e_fm2_overlay_clip_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   int x, y;

   evas_object_geometry_get(obj, &x, &y, NULL, NULL);
   evas_object_move(sd->overlay, x, y);
}

static void
_e_fm2_view_menu_sorting_change_case(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->list.sort.no_case = !mi->toggle;
   _e_fm2_refresh(sd, NULL, NULL);
}

static void
_e_fm2_view_menu_sorting_change_size(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->list.sort.size = mi->toggle;
   _e_fm2_refresh(sd, NULL, NULL);
}

static void
_e_fm2_view_menu_sorting_change_mtime(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->list.sort.mtime = mi->toggle;
   _e_fm2_refresh(sd, NULL, NULL);
}

static void
_e_fm2_view_menu_sorting_change_extension(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->list.sort.extension = mi->toggle;
   _e_fm2_refresh(sd, NULL, NULL);
}

static void
_e_fm2_view_menu_sorting_change_dirs_first(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->list.sort.dirs.first = mi->toggle;
   if (mi->toggle && sd->config->list.sort.dirs.last)
     sd->config->list.sort.dirs.last = 0;
   _e_fm2_refresh(sd, NULL, NULL);
}

static void
_e_fm2_view_menu_sorting_change_dirs_last(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->list.sort.dirs.last = mi->toggle;
   if (mi->toggle && sd->config->list.sort.dirs.first)
     sd->config->list.sort.dirs.first = 0;
   _e_fm2_refresh(sd, NULL, NULL);
}

static void
_e_fm2_view_menu_sorting_pre(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   E_Menu *subm;

   subm = e_menu_new();
   e_menu_item_submenu_set(mi, subm);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Case Sensitive"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, !sd->config->list.sort.no_case);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_sorting_change_case, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Sort By Extension"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, sd->config->list.sort.extension);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_sorting_change_extension, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Sort By Modification Time"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, sd->config->list.sort.mtime);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_sorting_change_mtime, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Sort By Size"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, sd->config->list.sort.size);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_sorting_change_size, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_separator_set(mi, 1);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Directories First"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, sd->config->list.sort.dirs.first);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_sorting_change_dirs_first, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Directories Last"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, sd->config->list.sort.dirs.last);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_sorting_change_dirs_last, sd);
}

static void
_e_fm2_menu(Evas_Object *obj, unsigned int timestamp)
{
   E_Fm2_Smart_Data *sd;
   E_Menu *mn, *sub;
   E_Menu_Item *mi;
   E_Zone *zone;
   int x, y;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;

   mn = e_menu_new();
   e_object_data_set(E_OBJECT(mn), obj);
   e_menu_category_set(mn, "e/fileman/action");

   if (sd->icon_menu.replace.func)
     sd->icon_menu.replace.func(sd->icon_menu.replace.data, sd->obj, mn, NULL);
   else
     {
        if (sd->icon_menu.start.func)
          sd->icon_menu.start.func(sd->icon_menu.start.data, sd->obj, mn, NULL);
        if (!(sd->icon_menu.flags & E_FM2_MENU_NO_VIEW_MENU))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("View Mode"));
             e_util_menu_item_theme_icon_set(mi, "preferences-look");
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_unref(E_OBJECT(sub));
             e_object_data_set(E_OBJECT(sub), sd);
             e_menu_pre_activate_callback_set(sub, _e_fm2_view_menu_pre, sd);

             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Sorting"));
             e_menu_item_submenu_pre_callback_set(mi, _e_fm2_view_menu_sorting_pre, sd);
          }
        if (!(sd->icon_menu.flags &
              (E_FM2_MENU_NO_SHOW_HIDDEN | E_FM2_MENU_NO_REMEMBER_ORDERING | E_FM2_MENU_NO_ACTIVATE_CHANGE)))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Options"));
             e_util_menu_item_theme_icon_set(mi, "preferences-system");
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_unref(E_OBJECT(sub));
             e_object_data_set(E_OBJECT(sub), sd);
             e_menu_pre_activate_callback_set(sub, _e_fm2_options_menu_pre, sd);
          }
        if (!(sd->icon_menu.flags & E_FM2_MENU_NO_REFRESH))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Refresh View"));
             e_util_menu_item_theme_icon_set(mi, "view-refresh");
             e_menu_item_callback_set(mi, _e_fm2_refresh, sd);
          }

        if (!(sd->icon_menu.flags & E_FM2_MENU_NO_NEW))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_separator_set(mi, 1);

             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("New..."));
             e_util_menu_item_theme_icon_set(mi, "add");
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_unref(E_OBJECT(sub));
             e_object_data_set(E_OBJECT(sub), sd);
             e_menu_pre_activate_callback_set(sub, _e_fm2_add_menu_pre, sd);
          }

        if (sd->realpath)
          {
             const Eina_List *ll;
             /* see if we have any mime handlers registered for this file */
             ll = e_fm2_mime_handler_mime_handlers_get("inode/directory");
             if (ll)
               {
                  mi = e_menu_item_new(mn);
                  e_menu_item_separator_set(mi, 1);

                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Actions..."));
                  e_util_menu_item_theme_icon_set(mi, "preferences-plugin");
                  sub = e_menu_new();
                  e_menu_item_submenu_set(mi, sub);
                  _e_fm2_context_menu_append(sd, sd->realpath, ll, sub, NULL);
               }
          }

        if (((!(sd->icon_menu.flags & E_FM2_MENU_NO_PASTE)) ||
             (!(sd->icon_menu.flags & E_FM2_MENU_NO_SYMLINK))) &&
            (eina_list_count(_e_fm_file_buffer) > 0) &&
            ecore_file_can_write(sd->realpath))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_separator_set(mi, 1);

             if (!(sd->icon_menu.flags & E_FM2_MENU_NO_PASTE))
               {
                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Paste"));
                  e_util_menu_item_theme_icon_set(mi, "edit-paste");
                  e_menu_item_callback_set(mi, _e_fm2_file_paste_menu, sd);
               }

             if (!(sd->icon_menu.flags & E_FM2_MENU_NO_SYMLINK))
               {
                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Link"));
                  e_util_menu_item_theme_icon_set(mi, "emblem-symbolic-link");
                  e_menu_item_callback_set(mi, _e_fm2_file_symlink_menu, sd);
               }
          }

        if (sd->icon_menu.end.func)
          sd->icon_menu.end.func(sd->icon_menu.end.data, sd->obj, mn, NULL);
     }

   ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   zone = e_zone_current_get();
   if (!zone)
     {
        e_object_del(E_OBJECT(mn));
        return;
     }
   sd->menu = mn;
   e_menu_post_deactivate_callback_set(mn, _e_fm2_menu_post_cb, sd);
   e_menu_activate_mouse(mn, zone,
                         x, y, 1, 1,
                         E_MENU_POP_DIRECTION_DOWN, timestamp);
}

static void
_e_fm2_menu_post_cb(void *data, E_Menu *m EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   sd->menu = NULL;
}

static void
_e_fm2_icon_menu(E_Fm2_Icon *ic, Evas_Object *obj, unsigned int timestamp)
{
   E_Fm2_Smart_Data *sd;
   E_Menu *mn, *sub;
   E_Menu_Item *mi;
   E_Zone *zone;
   Eina_List *sel;
   Eina_List *l = NULL;
   int x, y, can_w, can_w2, protect;
   char buf[PATH_MAX], *ext;
   Eina_Bool writable;

   sd = ic->sd;
   if (ic->menu) return;

   mn = e_menu_new();
   e_object_data_set(E_OBJECT(mn), obj);
   e_menu_category_set(mn, "e/fileman/action");

   if (sd->icon_menu.replace.func)
     sd->icon_menu.replace.func(sd->icon_menu.replace.data, sd->obj, mn, &ic->info);
   else
     {
        writable = ecore_file_can_write(sd->realpath);

        if (sd->icon_menu.start.func)
          sd->icon_menu.start.func(sd->icon_menu.start.data, sd->obj, mn, &ic->info);
        if (!(sd->icon_menu.flags & E_FM2_MENU_NO_VIEW_MENU))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("View Mode"));
             e_util_menu_item_theme_icon_set(mi, "preferences-look");
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_data_set(E_OBJECT(sub), sd);
             e_object_unref(E_OBJECT(sub));
             e_menu_pre_activate_callback_set(sub, _e_fm2_icon_view_menu_pre, sd);

             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Sorting"));
             e_menu_item_submenu_pre_callback_set(mi, _e_fm2_view_menu_sorting_pre, sd);
          }
        if (!(sd->icon_menu.flags &
              (E_FM2_MENU_NO_SHOW_HIDDEN | E_FM2_MENU_NO_REMEMBER_ORDERING | E_FM2_MENU_NO_ACTIVATE_CHANGE)))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Options"));
             e_util_menu_item_theme_icon_set(mi, "preferences-system");
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_unref(E_OBJECT(sub));
             e_object_data_set(E_OBJECT(sub), sd);
             e_menu_pre_activate_callback_set(sub, _e_fm2_options_menu_pre, sd);
          }
        if (!(sd->icon_menu.flags & E_FM2_MENU_NO_REFRESH))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Refresh View"));
             e_util_menu_item_theme_icon_set(mi, "view-refresh");
             e_menu_item_callback_set(mi, _e_fm2_refresh, sd);
          }

        /* FIXME: stat the dir itself - move to e_fm_main */
        if (writable && !(sd->icon_menu.flags & E_FM2_MENU_NO_NEW))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_separator_set(mi, 1);

             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("New..."));
             e_util_menu_item_theme_icon_set(mi, "add");
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_unref(E_OBJECT(sub));
             e_object_data_set(E_OBJECT(sub), sd);
             e_menu_pre_activate_callback_set(sub, _e_fm2_add_menu_pre, sd);
          }
        {
           const Eina_List *ll = NULL;
           E_Menu *subm = NULL;
           if (ic->info.mime)
             {
                /* see if we have any mime handlers registered for this file */
                ll = e_fm2_mime_handler_mime_handlers_get(ic->info.mime);
             }
           if (ll || sd->realpath)
             {
                mi = e_menu_item_new(mn);
                e_menu_item_separator_set(mi, 1);

                mi = e_menu_item_new(mn);
                e_menu_item_label_set(mi, _("Actions..."));
                e_util_menu_item_theme_icon_set(mi, "preferences-plugin");
                subm = e_menu_new();
                e_menu_item_submenu_set(mi, subm);
                _e_fm2_icon_realpath(ic, buf, sizeof(buf));
             }
           if (ll)
             _e_fm2_context_menu_append(sd, buf, ll, subm, ic);
           if (sd->realpath && (ic->info.mime != _e_fm2_mime_inode_directory))
             {
                ll = e_fm2_mime_handler_mime_handlers_get("inode/directory");
                _e_fm2_context_menu_append(sd, sd->realpath, ll, subm, ic);
             }

           /* see if we have any glob handlers registered for this file */
           ext = strrchr(ic->info.file, '.');
           if (ext)
             {
                snprintf(buf, sizeof(buf), "*%s", ext);
                l = e_fm2_mime_handler_glob_handlers_get(buf);
                if (l)
                  {
                     if (subm)
                       {
                          if (subm->items)
                            {
                               mi = e_menu_item_new(mn);
                               e_menu_item_separator_set(mi, 1);
                            }
                       }
                     else
                       {
                          mi = e_menu_item_new(mn);
                          e_menu_item_separator_set(mi, 1);

                          mi = e_menu_item_new(mn);
                          e_menu_item_label_set(mi, _("Actions..."));
                          e_util_menu_item_theme_icon_set(mi, "preferences-plugin");
                          subm = e_menu_new();
                          e_menu_item_submenu_set(mi, subm);
                       }
                     _e_fm2_icon_realpath(ic, buf, sizeof(buf));
                     _e_fm2_context_menu_append(sd, buf, l, subm, ic);
                     eina_list_free(l);
                  }
             }
        }
        if (!ic->info.removable)
          {
             if (!(sd->icon_menu.flags & E_FM2_MENU_NO_CUT))
               {
                  if (writable)
                    {
                       mi = e_menu_item_new(mn);
                       e_menu_item_separator_set(mi, 1);

                       mi = e_menu_item_new(mn);
                       e_menu_item_label_set(mi, _("Cut"));
                       e_util_menu_item_theme_icon_set(mi, "edit-cut");
                       e_menu_item_callback_set(mi, _e_fm2_file_cut_menu, sd);
                    }
               }
             if (!(sd->icon_menu.flags & E_FM2_MENU_NO_COPY))
               {
                  if (!writable)
                    {
                       mi = e_menu_item_new(mn);
                       e_menu_item_separator_set(mi, 1);
                    }

                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Copy"));
                  e_util_menu_item_theme_icon_set(mi, "edit-copy");
                  e_menu_item_callback_set(mi, _e_fm2_file_copy_menu, sd);
               }

             if (((!(sd->icon_menu.flags & E_FM2_MENU_NO_PASTE)) ||
                  (!(sd->icon_menu.flags & E_FM2_MENU_NO_SYMLINK))) &&
                 (eina_list_count(_e_fm_file_buffer) > 0) &&
                 writable)
               {
                  if (!(sd->icon_menu.flags & E_FM2_MENU_NO_PASTE))
                    {
                       mi = e_menu_item_new(mn);
                       e_menu_item_label_set(mi, _("Paste"));
                       e_util_menu_item_theme_icon_set(mi, "edit-paste");
                       e_menu_item_callback_set(mi, _e_fm2_file_paste_menu, sd);
                    }

                  if (!(sd->icon_menu.flags & E_FM2_MENU_NO_SYMLINK))
                    {
                       mi = e_menu_item_new(mn);
                       e_menu_item_label_set(mi, _("Link"));
                       e_util_menu_item_theme_icon_set(mi, "emblem-symbolic-link");
                       e_menu_item_callback_set(mi, _e_fm2_file_symlink_menu, sd);
                    }
               }
          }

        can_w2 = 1;
        if (ic->sd->order_file)
          {
             snprintf(buf, sizeof(buf), "%s/.order", sd->realpath);
             /* FIXME: stat the .order itself - move to e_fm_main */
//	     can_w2 = ecore_file_can_write(buf);
          }
        if (ic->info.link)
          {
             can_w = 1;
/*	     struct stat st;

             if (_e_fm2_icon_realpath(ic, buf, sizeof(buf)) &&
                 (lstat(buf, &st) == 0))
               {
                  if (st.st_uid == getuid())
                    {
                       if (st.st_mode & S_IWUSR) can_w = 1;
                    }
                  else if (st.st_gid == getgid())
                    {
                       if (st.st_mode & S_IWGRP) can_w = 1;
                    }
                  else
                    {
                       if (st.st_mode & S_IWOTH) can_w = 1;
                    }
               }
 */       }
        else
          can_w = 1;

        sel = e_fm2_selected_list_get(ic->sd->obj);
        if ((!sel) || eina_list_count(sel) == 1)
          {
             _e_fm2_icon_realpath(ic, buf, sizeof(buf));
             protect = e_filereg_file_protected(buf);
          }
        else
          protect = 0;
        eina_list_free(sel);

        if ((can_w) && (can_w2) && !(protect) && !ic->info.removable)
          {
             if (!(sd->icon_menu.flags & E_FM2_MENU_NO_DELETE))
               {
                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Delete"));
                  e_util_menu_item_theme_icon_set(mi, "edit-delete");
                  e_menu_item_callback_set(mi, _e_fm2_file_delete_menu, ic);
               }

             if (!(sd->icon_menu.flags & E_FM2_MENU_NO_RENAME))
               {
                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Rename"));
                  e_util_menu_item_theme_icon_set(mi, "edit-rename");
                  e_menu_item_callback_set(mi, _e_fm2_file_rename, ic);
               }
          }

        if (ic->info.removable)
          {
             E_Volume *v;

             v = e_fm2_device_volume_find(ic->info.link);
             if (v)
               {
                  mi = e_menu_item_new(mn);
                  e_menu_item_separator_set(mi, 1);

                  mi = e_menu_item_new(mn);
                  if (v->mounted)
                    {
                       e_menu_item_label_set(mi, _("Unmount"));
                       e_menu_item_callback_set(mi, _e_fm2_volume_unmount, v);
                    }
                  else
                    {
                       e_menu_item_label_set(mi, _("Mount"));
                       e_menu_item_callback_set(mi, _e_fm2_volume_mount, v);
                    }

                  mi = e_menu_item_new(mn);
                  e_menu_item_label_set(mi, _("Eject"));
                  e_util_menu_item_theme_icon_set(mi, "media-eject");
                  e_menu_item_callback_set(mi, _e_fm2_volume_eject, v);

                  mi = e_menu_item_new(mn);
                  e_menu_item_separator_set(mi, 1);
               }
          }
        mi = e_menu_item_new(mn);
        e_menu_item_separator_set(mi, 1);

        if ((!ic->info.removable) && ic->info.file && (ic->info.file[0] != '|') && ic->info.mime && (!strcmp(ic->info.mime, "application/x-desktop")))
          {
             mi = e_menu_item_new(mn);
             e_menu_item_label_set(mi, _("Properties"));
             e_util_menu_item_theme_icon_set(mi, "document-properties");
             e_menu_item_callback_set(mi, _e_fm2_file_properties, ic);
             sub = e_menu_new();
             e_menu_item_submenu_set(mi, sub);
             e_object_unref(E_OBJECT(sub));

             mi = e_menu_item_new(sub);
             e_menu_item_label_set(mi, _("Application Properties"));
             e_util_menu_item_theme_icon_set(mi, "configure");
             e_menu_item_callback_set(mi, _e_fm2_file_application_properties, ic);
          }
        else
          sub = NULL;

        mi = e_menu_item_new(sub ? : mn);
        e_menu_item_label_set(mi, _("File Properties"));
        e_util_menu_item_theme_icon_set(mi, "document-properties");
        e_menu_item_callback_set(mi, _e_fm2_file_properties, ic);

        if (sd->icon_menu.end.func)
          sd->icon_menu.end.func(sd->icon_menu.end.data, sd->obj, mn, &(ic->info));
     }

   ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   zone = e_zone_current_get();
   if (!zone)
     {
        e_object_del(E_OBJECT(mn));
        return;
     }
   ic->menu = mn;
   e_menu_post_deactivate_callback_set(mn, _e_fm2_icon_menu_post_cb, ic);
   e_menu_activate_mouse(mn, zone,
                         x, y, 1, 1,
                         E_MENU_POP_DIRECTION_DOWN, timestamp);
}

static void
_e_fm2_context_menu_append(E_Fm2_Smart_Data *sd, const char *path, const Eina_List *list, E_Menu *mn, E_Fm2_Icon *ic)
{
   E_Fm2_Mime_Handler *handler;
   Eina_List *l;

   if (!list) return;

   l = eina_list_clone(list);
   l = eina_list_sort(l, -1, _e_fm2_context_list_sort);

   EINA_LIST_FREE(l, handler)
     {
        E_Fm2_Context_Menu_Data *md = NULL;
        E_Menu_Item *mi;

        if ((!handler) || (!handler->label) || (!e_fm2_mime_handler_test(handler, sd->obj, path)))
          continue;

        md = E_NEW(E_Fm2_Context_Menu_Data, 1);
        if (!md) continue;
        md->icon = ic;
        md->handler = handler;
        md->sd = sd;
        _e_fm2_menu_contexts = eina_list_append(_e_fm2_menu_contexts, md);

        mi = e_menu_item_new(mn);
        e_menu_item_label_set(mi, handler->label);
        if (handler->icon_group)
          {
             if (handler->icon_group[0] == '/')
               e_menu_item_icon_file_set(mi, handler->icon_group);
             else
               e_util_menu_item_theme_icon_set(mi, handler->icon_group);
          }
        e_menu_item_callback_set(mi, _e_fm2_icon_menu_item_cb, md);
     }
}

static int
_e_fm2_context_list_sort(const void *data1, const void *data2)
{
   const E_Fm2_Mime_Handler *d1, *d2;

   if (!data1) return 1;
   if (!data2) return -1;
   d1 = data1;
   if (!d1->label) return 1;
   d2 = data2;
   if (!d2->label) return -1;
   return strcmp(d1->label, d2->label);
}

static void
_e_fm2_icon_menu_post_cb(void *data, E_Menu *m EINA_UNUSED)
{
   E_Fm2_Context_Menu_Data *md;
   E_Fm2_Icon *ic;

   ic = data;
   ic->menu = NULL;
   EINA_LIST_FREE(_e_fm2_menu_contexts, md)
     E_FREE(md);
}

static void
_e_fm2_icon_menu_item_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Context_Menu_Data *md = NULL;
   Evas_Object *obj = NULL;
   char buf[PATH_MAX];

   md = data;
   if (!md) return;
   if (md->icon)
     {
        obj = md->icon->info.fm;
        if (!obj) return;
        snprintf(buf, sizeof(buf), "%s/%s",
                 e_fm2_real_path_get(obj), md->icon->info.file);
        e_fm2_mime_handler_call(md->handler, obj, buf);
     }
   else
     e_fm2_mime_handler_call(md->handler, md->sd->obj, md->sd->realpath);
}

struct e_fm2_view_menu_icon_size_data
{
   E_Fm2_Smart_Data *sd;
   short size;
};

static void
_e_fm2_view_menu_icon_size_data_free(void *obj)
{
   struct e_fm2_view_menu_icon_size_data *d = e_object_data_get(obj);
   free(d);
}

static void
_e_fm2_view_menu_icon_size_change(void *data, E_Menu *m, E_Menu_Item *mi)
{
   struct e_fm2_view_menu_icon_size_data *d = data;
   short current_size = _e_fm2_icon_w_get(d->sd);
   d->sd->icon_size = d->size;
   d->sd->inherited_dir_props = EINA_FALSE;
   if (current_size == d->size)
     return;
   _e_fm2_refresh(d->sd, m, mi);
}

static void
_e_fm2_view_menu_icon_size_use_default(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   short old, new;

   old = _e_fm2_icon_w_get(sd);

   if (sd->icon_size == -1)
     sd->icon_size = sd->config->icon.icon.w;
   else
     sd->icon_size = -1;

   new = _e_fm2_icon_w_get(sd);
   sd->inherited_dir_props = EINA_FALSE;

   if (new == old)
     return;

   _e_fm2_refresh(sd, m, mi);
}

static void
_e_fm2_view_menu_icon_size_pre(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   E_Menu *subm;
   const short *itr, sizes[] =
   {
      22, 32, 48, 64, 96, 128, 256, -1
   };
   short current_size = _e_fm2_icon_w_get(sd);

   if (e_scale > 0.0)
     current_size /= e_scale;

   subm = e_menu_new();
   e_menu_item_submenu_set(mi, subm);

   for (itr = sizes; *itr > -1; itr++)
     {
        char buf[32];
        struct e_fm2_view_menu_icon_size_data *d;

        d = malloc(sizeof(*d));
        if (!d)
          continue;
        d->sd = sd;
        d->size = *itr;

        snprintf(buf, sizeof(buf), "%hd", *itr);

        mi = e_menu_item_new(subm);
        e_object_data_set(E_OBJECT(mi), d);
        e_object_del_attach_func_set
          (E_OBJECT(mi), _e_fm2_view_menu_icon_size_data_free);

        e_menu_item_label_set(mi, buf);
        e_menu_item_radio_group_set(mi, 1);
        e_menu_item_radio_set(mi, 1);

        if (current_size == *itr)
          e_menu_item_toggle_set(mi, 1);

        e_menu_item_callback_set(mi, _e_fm2_view_menu_icon_size_change, d);
     }

   mi = e_menu_item_new(subm);
   e_menu_item_separator_set(mi, 1);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Use default"));
   e_menu_item_check_set(mi, 1);
   e_menu_item_toggle_set(mi, sd->icon_size == -1);
   e_menu_item_callback_set(mi, _e_fm2_view_menu_icon_size_use_default, sd);
}

static void
_e_fm2_toggle_inherit_dir_props(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;

   sd->inherited_dir_props = !sd->inherited_dir_props;
   _e_fm2_dir_save_props(sd);
   _e_fm2_dir_load_props(sd);
   _e_fm2_refresh(sd, m, mi);
}

static void
_e_fm2_view_menu_common(E_Menu *subm, E_Fm2_Smart_Data *sd)
{
   char buf[64];
   E_Menu_Item *mi;
   char view_mode;
   int icon_size;

   view_mode = _e_fm2_view_mode_get(sd);
   if (!(sd->icon_menu.flags & E_FM2_MENU_NO_VIEW_CHANGE))
     {
        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Grid Icons"));
        e_menu_item_radio_group_set(mi, 1);
        e_menu_item_radio_set(mi, 1);
        if (view_mode == E_FM2_VIEW_MODE_GRID_ICONS)
          e_menu_item_toggle_set(mi, 1);
        e_menu_item_callback_set(mi, _e_fm2_view_menu_grid_icons_cb, sd);

        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Custom Icons"));
        e_menu_item_radio_group_set(mi, 1);
        e_menu_item_radio_set(mi, 1);
        if (view_mode == E_FM2_VIEW_MODE_CUSTOM_ICONS)
          e_menu_item_toggle_set(mi, 1);
        e_menu_item_callback_set(mi, _e_fm2_view_menu_custom_icons_cb, sd);

        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("List"));
        e_menu_item_radio_group_set(mi, 1);
        e_menu_item_radio_set(mi, 1);
        if (view_mode == E_FM2_VIEW_MODE_LIST)
          e_menu_item_toggle_set(mi, 1);
        e_menu_item_callback_set(mi, _e_fm2_view_menu_list_cb, sd);

        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Default View"));
        e_menu_item_check_set(mi, 1);
        e_menu_item_toggle_set(mi, sd->view_mode == -1);
        e_menu_item_callback_set(mi, _e_fm2_view_menu_use_default_cb, sd);

        mi = e_menu_item_new(subm);
        e_menu_item_separator_set(mi, 1);
     }

   if (view_mode == E_FM2_VIEW_MODE_LIST)
     return;

   mi = e_menu_item_new(subm);
   e_menu_item_separator_set(mi, 1);

   icon_size = _e_fm2_icon_w_get(sd);

   // show the icon size as selected (even if it might be influnced by e_scale)
   /* if (e_scale > 0.0)
    *   icon_size /= e_scale; */

   snprintf(buf, sizeof(buf), _("Icon Size (%d)"), icon_size);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, buf);
   e_menu_item_submenu_pre_callback_set(mi, _e_fm2_view_menu_icon_size_pre, sd);
}

static void
_e_fm2_icon_view_menu_pre(void *data, E_Menu *subm)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   if (subm->items) return;
   _e_fm2_view_menu_common(subm, sd);
}

static void
_e_fm2_new_dir_notify(void *data, Ecore_Thread *eth EINA_UNUSED, char *filename)
{
   E_Fm2_Smart_Data *sd = data;

   if (!sd->new_file.done)
     {
        if (filename)
          sd->new_file.filename = eina_stringshare_add(ecore_file_file_get(filename));
        else
          e_util_dialog_internal(_("Error"), _("Could not create a directory!"));
     }
   free(filename);
}

static void
_e_fm2_new_file_notify(void *data, Ecore_Thread *eth EINA_UNUSED, char *filename)
{
   E_Fm2_Smart_Data *sd = data;

   if (!sd->new_file.done)
     {
        if (filename)
          sd->new_file.filename = eina_stringshare_add(ecore_file_file_get(filename));
        else
          e_util_dialog_internal(_("Error"), _("Could not create a file!"));
     }
   free(filename);
}

static void
_e_fm2_new_thread_helper(Ecore_Thread *eth, Eina_Bool dir)
{
   char buf[PATH_MAX];
   char *path;
   struct stat st;
   unsigned int x;
   int fd;
   Eina_List *texts, *l;

   path = ecore_thread_global_data_wait("path", 2.0);
   snprintf(buf, sizeof(buf), "%s/%s", path, dir ? _("New Directory") : _("New File"));
   errno = 0;
   if (stat(buf, &st) && (errno == ENOENT))
     {
        if (dir)
          {
             if (ecore_file_mkdir(buf))
               {
                  ecore_thread_global_data_set("efm_pending_filename", strdup(buf), free);
                  ecore_thread_feedback(eth, strdup(buf));
                  return;
               }
          }
        else
          {
             fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
             if (fd)
               {
                  texts = ecore_thread_global_data_wait("efm_text_uri_list", 0.01);
                  EINA_LIST_FOREACH(texts, l, path)
                    {
                       if (write(fd, path, strlen(path)) < 0)
                         perror("write");
                       if (write(fd, "\n", 1) < 0)
                         perror("write");
                    }
                  close(fd);
                  ecore_thread_global_data_set("efm_pending_filename", strdup(buf), free);
                  ecore_thread_feedback(eth, strdup(buf));
                  return;
               }
          }
        goto error;
     }
   else if (errno)
     goto error;
   for (x = 0; x < UINT_MAX; x++)
     {
        snprintf(buf, sizeof(buf), "%s/%s %u", path, dir ? _("New Directory") : _("New File"), x);
        errno = 0;
        if (stat(buf, &st) && (errno == ENOENT))
          {
             if (dir)
               {
                  if (ecore_file_mkdir(buf))
                    {
                       ecore_thread_global_data_set("efm_pending_filename", strdup(buf), free);
                       ecore_thread_feedback(eth, strdup(buf));
                       return;
                    }
               }
             else
               {
                  fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
                  if (fd)
                    {
                       texts = ecore_thread_global_data_wait("efm_text_uri_list", 0.01);
                       EINA_LIST_FOREACH(texts, l, path)
                         {
                            if (write(fd, path, strlen(path)) < 0)
                              perror("write");
                            if (write(fd, "\n", 1) < 0)
                              perror("write");
                         }
                       close(fd);
                       ecore_thread_global_data_set("efm_pending_filename", strdup(buf), free);
                       ecore_thread_feedback(eth, strdup(buf));
                       return;
                    }
               }
             goto error;
          }
        else if (errno)
          goto error;
     }
error:
   ecore_thread_feedback(eth, NULL);
}

static void
_e_fm2_new_file_thread(void *data EINA_UNUSED, Ecore_Thread *eth)
{
   _e_fm2_new_thread_helper(eth, EINA_FALSE);
}

static void
_e_fm2_new_dir_thread(void *data EINA_UNUSED, Ecore_Thread *eth)
{
   _e_fm2_new_thread_helper(eth, EINA_TRUE);
}

static void
_e_fm2_new_file_end(void *data, Ecore_Thread *eth EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   sd->new_file.thread = NULL;
   ecore_thread_global_data_del("path");
   ecore_thread_global_data_del("efm_text_uri_list");
   evas_object_unref(sd->obj);
   e_fm2_refresh(sd->obj);
}

static void
_e_fm2_new_file_cancel(void *data, Ecore_Thread *eth EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;
   sd->new_file.thread = NULL;
   ecore_thread_global_data_del("path");
   ecore_thread_global_data_del("efm_text_uri_list");
   evas_object_unref(sd->obj);
}

static void
_e_fm2_new_file(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   if (sd->new_file.thread || sd->new_file.filename)
     {
        e_util_dialog_internal(_("Error"), _("Already creating a new file for this directory!"));
        return;
     }
   if (!ecore_file_can_write(sd->realpath))
     {
        e_util_dialog_show(_("Error"), _("%s can't be written to!"), sd->realpath);
        return;
     }
   sd->new_file.thread = ecore_thread_feedback_run(_e_fm2_new_file_thread, (Ecore_Thread_Notify_Cb)_e_fm2_new_file_notify,
                                                   _e_fm2_new_file_end, _e_fm2_new_file_cancel, sd, EINA_FALSE);
   ecore_thread_global_data_add("path", (void *)eina_stringshare_ref(sd->realpath), (void *)eina_stringshare_del, EINA_FALSE);
   evas_object_ref(sd->obj);
}

static void
_e_fm2_new_directory(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   if (sd->new_file.thread || sd->new_file.filename)
     {
        e_util_dialog_internal(_("Error"), _("Already creating a new file for this directory!"));
        return;
     }
   if (!ecore_file_can_write(sd->realpath))
     {
        e_util_dialog_show(_("Error"), _("%s can't be written to!"), sd->realpath);
        return;
     }
   sd->new_file.thread = ecore_thread_feedback_run(_e_fm2_new_dir_thread, (Ecore_Thread_Notify_Cb)_e_fm2_new_dir_notify,
                                                   _e_fm2_new_file_end, _e_fm2_new_file_cancel, sd, EINA_FALSE);
   ecore_thread_global_data_add("path", (void *)eina_stringshare_ref(sd->realpath), (void *)eina_stringshare_del, EINA_FALSE);
   evas_object_ref(sd->obj);
}

static void
_e_fm2_add_menu_pre(void *data, E_Menu *subm)
{
   E_Menu_Item *mi;
   E_Fm2_Smart_Data *sd;

   sd = data;
   if (subm->items) return;

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Directory"));
   e_util_menu_item_theme_icon_set(mi, "folder-new");
   e_menu_item_callback_set(mi, _e_fm2_new_directory, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("File"));
   e_util_menu_item_theme_icon_set(mi, "document-new");
   e_menu_item_callback_set(mi, _e_fm2_new_file, sd);
}

static void
_e_fm2_settings_icon_item(void *data EINA_UNUSED, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   e_configure_registry_call("fileman/file_icons", NULL, NULL);
}

static void
_e_fm2_settings_item(void *data EINA_UNUSED, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   e_configure_registry_call("fileman/fileman", NULL, NULL);
}

static void
_e_fm2_options_menu_pre(void *data, E_Menu *subm)
{
   E_Fm2_Smart_Data *sd;
   E_Menu_Item *mi;

   sd = data;

   if (subm->items) return;

   if ((!(sd->icon_menu.flags & E_FM2_MENU_NO_INHERIT_PARENT)) &&
       (sd->view_flags & E_FM2_VIEW_INHERIT_DIR_CUSTOM))
     {
        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Inherit parent settings"));
        e_util_menu_item_theme_icon_set(mi, "view-inherit");
        e_menu_item_check_set(mi, 1);
        e_menu_item_toggle_set(mi, sd->inherited_dir_props);
        e_menu_item_callback_set(mi, _e_fm2_toggle_inherit_dir_props, sd);
     }
   if (!(sd->icon_menu.flags & E_FM2_MENU_NO_SHOW_HIDDEN))
     {
        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Show Hidden Files"));
        e_util_menu_item_theme_icon_set(mi, "view-refresh");
        e_menu_item_check_set(mi, 1);
        e_menu_item_toggle_set(mi, sd->show_hidden_files);
        e_menu_item_callback_set(mi, _e_fm2_toggle_hidden_files, sd);
     }

   if (!(sd->icon_menu.flags & E_FM2_MENU_NO_REMEMBER_ORDERING))
     {
        if (!sd->config->view.always_order)
          {
             mi = e_menu_item_new(subm);
             e_menu_item_label_set(mi, _("Remember Ordering"));
             e_util_menu_item_theme_icon_set(mi, "view-order");
             e_menu_item_check_set(mi, 1);
             e_menu_item_toggle_set(mi, sd->order_file);
             e_menu_item_callback_set(mi, _e_fm2_toggle_ordering, sd);
          }
        if ((sd->order_file) || (sd->config->view.always_order))
          {
             mi = e_menu_item_new(subm);
             e_menu_item_label_set(mi, _("Sort Now"));
             e_util_menu_item_theme_icon_set(mi, "view-sort");
             e_menu_item_callback_set(mi, _e_fm2_sort, sd);
          }
     }
   if (!(sd->icon_menu.flags & E_FM2_MENU_NO_ACTIVATE_CHANGE))
     {
        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Single Click Activation"));
        e_util_menu_item_theme_icon_set(mi, "access");
        e_menu_item_check_set(mi, 1);
        e_menu_item_toggle_set(mi, sd->config->view.single_click);
        e_menu_item_callback_set(mi, _e_fm2_toggle_single_click, sd);
     }

   if (!e_config->filemanager_secure_rm)
     {
        /* can't disable this if it's globally enabled */
        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Secure Deletion"));
        e_util_menu_item_theme_icon_set(mi, "security-high");
        e_menu_item_check_set(mi, 1);
        e_menu_item_toggle_set(mi, e_config->filemanager_secure_rm | sd->config->secure_rm);
        e_menu_item_callback_set(mi, _e_fm2_toggle_secure_rm, sd);
     }

   if (!e_configure_registry_exists("fileman/fileman")) return;

   mi = e_menu_item_new(subm);
   e_menu_item_separator_set(mi, 1);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("File Manager Settings"));
   e_util_menu_item_theme_icon_set(mi, "system-file-manager");
   e_menu_item_callback_set(mi, _e_fm2_settings_item, sd);

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("File Icon Settings"));
   e_util_menu_item_theme_icon_set(mi, "preferences-file-icons");
   e_menu_item_callback_set(mi, _e_fm2_settings_icon_item, sd);
}

static void
_custom_file_key_del(E_Fm2_Smart_Data *sd, const char *key)
{
   Efreet_Desktop *ef;
   char buf[PATH_MAX];

   if (sd->desktop) ef = sd->desktop;
   else
     {
        snprintf(buf, sizeof(buf), "%s/.directory.desktop", sd->realpath);
        ef = efreet_desktop_new(buf);
     }
   if (!ef) return;

   if (efreet_desktop_x_field_del(ef, key))
     efreet_desktop_save(ef);

   if (!sd->desktop) efreet_desktop_free(ef);
}

static void
_e_fm2_view_menu_del(void *data)
{
   E_Fm2_Smart_Data *sd = e_object_data_get(data);

   if (!sd) return;
   if (sd->image_dialog) return;
   if (!sd->desktop) return;
   efreet_desktop_free(sd->desktop);
   sd->desktop = NULL;
}

static void
_clear_background_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   if (!sd) return;

   _custom_file_key_del(sd, "X-Enlightenment-Directory-Wallpaper");
   evas_object_smart_callback_call(sd->obj, "dir_changed", NULL);
}

static void
_clear_overlay_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   if (!sd) return;

   _custom_file_key_del(sd, "X-Enlightenment-Directory-Overlay");
   evas_object_smart_callback_call(sd->obj, "dir_changed", NULL);
}

static void
_e_fm2_view_menu_pre(void *data, E_Menu *subm)
{
   E_Fm2_Smart_Data *sd = data;
   E_Menu_Item *mi;

   if (subm->items) return;

   _e_fm2_view_menu_common(subm, sd);

   if (_e_fm2_desktop_open(sd) < 0) return;
   e_object_data_set(E_OBJECT(subm), sd);
   e_object_del_attach_func_set(E_OBJECT(subm), _e_fm2_view_menu_del);

   if (e_menu_item_nth(subm, 0) != NULL)
     {
        mi = e_menu_item_new(subm);
        e_menu_item_separator_set(mi, 1);
     }

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Set background..."));
   e_util_menu_item_theme_icon_set(mi, "preferences-desktop-wallpaper");
   e_menu_item_callback_set(mi, _e_fm2_view_menu_set_background_cb, sd);

   while (sd->desktop)
     {
        if (!eina_hash_find(sd->desktop->x, "X-Enlightenment-Directory-Wallpaper")) break;
        mi = e_menu_item_new(subm);
        e_menu_item_label_set(mi, _("Clear background"));
        e_util_menu_item_theme_icon_set(mi, "preferences-desktop-wallpaper");
        e_menu_item_callback_set(mi, _clear_background_cb, sd);
        break;
     }

   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Set overlay..."));
   e_menu_item_callback_set(mi, _e_fm2_view_menu_set_overlay_cb, sd);

   if (!sd->desktop) return;
   if (!eina_hash_find(sd->desktop->x, "X-Enlightenment-Directory-Overlay")) return;
   mi = e_menu_item_new(subm);
   e_menu_item_label_set(mi, _("Clear overlay"));
   e_menu_item_callback_set(mi, _clear_overlay_cb, sd);
}

static void
_e_fm2_view_menu_grid_icons_cb(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   char old;

   old = _e_fm2_view_mode_get(sd);
   sd->view_mode = E_FM2_VIEW_MODE_GRID_ICONS;
   sd->inherited_dir_props = EINA_FALSE;
   if (old == E_FM2_VIEW_MODE_GRID_ICONS)
     return;

   _e_fm2_refresh(sd, m, mi);
}

static void
_e_fm2_view_menu_custom_icons_cb(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   char old;

   old = _e_fm2_view_mode_get(sd);
   sd->view_mode = E_FM2_VIEW_MODE_CUSTOM_ICONS;
   sd->inherited_dir_props = EINA_FALSE;
   if (old == E_FM2_VIEW_MODE_CUSTOM_ICONS)
     return;

   _e_fm2_refresh(sd, m, mi);
}

static void
_e_fm2_view_menu_list_cb(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   char old;

   old = _e_fm2_view_mode_get(sd);
   sd->view_mode = E_FM2_VIEW_MODE_LIST;
   sd->inherited_dir_props = EINA_FALSE;
   if (old == E_FM2_VIEW_MODE_LIST)
     return;

   _e_fm2_refresh(sd, m, mi);
}

static void
_e_fm2_view_menu_use_default_cb(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd = data;
   char old, new;

   old = _e_fm2_view_mode_get(sd);

   if (sd->view_mode == -1)
     sd->view_mode = sd->config->view.mode;
   else
     sd->view_mode = -1;

   new = _e_fm2_view_mode_get(sd);
   sd->inherited_dir_props = EINA_FALSE;

   if (new == old)
     return;

   _e_fm2_refresh(sd, m, mi);
}

static void
_image_sel_del(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = e_object_data_get(data);
   if (!sd) return;
   sd->image_dialog = NULL;
   if (sd->desktop) efreet_desktop_free(sd->desktop);
   sd->desktop = NULL;
}

static void
_e_fm2_view_image_sel(E_Fm2_Smart_Data *sd, const char *title,
                      void (*ok_cb)(void *data, E_Dialog *dia))
{
   E_Dialog *dia;
   Evas_Object *o;
   Evas_Coord w, h;

   dia = e_dialog_new(NULL, "E", "_fm2_view_image_select_dialog");
   if (!dia) return;
   e_dialog_resizable_set(dia, 1);
   e_dialog_title_set(dia, title);

   o = e_widget_fsel_add(evas_object_evas_get(dia->win), "/", sd->realpath, NULL, NULL, NULL, sd, NULL, sd, 1);
   evas_object_show(o);
   e_widget_size_min_get(o, &w, &h);
   e_dialog_content_set(dia, o, w, h);
   dia->data = o;

   e_dialog_button_add(dia, _("OK"), NULL, ok_cb, sd);
   e_dialog_button_add(dia, _("Cancel"), NULL, _e_fm2_view_image_sel_close, sd);
   elm_win_center(dia->win, 1, 1);
   e_object_data_set(E_OBJECT(dia), sd);
   e_object_del_attach_func_set(E_OBJECT(dia), _image_sel_del);
   e_dialog_show(dia);

   sd->image_dialog = dia;
}

static void
_e_fm2_view_image_sel_close(void *data, E_Dialog *dia)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   e_object_del(E_OBJECT(dia));
   sd->image_dialog = NULL;
}

static int
_e_fm2_desktop_open(E_Fm2_Smart_Data *sd)
{
   Efreet_Desktop *ef;
   char buf[PATH_MAX];
   Eina_Bool ret;

   snprintf(buf, sizeof(buf), "%s/.directory.desktop", sd->realpath);
   if (sd->desktop)
     {
        if (!e_util_strcmp(buf, sd->desktop->orig_path)) return 1;
     }
   ret = ecore_file_exists(buf) ? ecore_file_can_write(buf)
     : ecore_file_can_write(sd->realpath);
   if (!ret) return -1;
   ef = efreet_desktop_new(buf);
   if (!ef) return 0;
   efreet_desktop_free(sd->desktop);
   sd->desktop = ef;
   return 1;
}

static void
_custom_file_key_set(E_Fm2_Smart_Data *sd, const char *key, const char *value)
{
   Efreet_Desktop *ef;
   char buf[PATH_MAX];
   int len;

   if (sd->desktop) ef = sd->desktop;
   else
     {
        snprintf(buf, sizeof(buf), "%s/.directory.desktop", sd->realpath);
        ef = efreet_desktop_new(buf);
        if (!ef)
          {
             ef = efreet_desktop_empty_new(buf);
             if (!ef) return;
             ef->type = EFREET_DESKTOP_TYPE_DIRECTORY;
             ef->name = strdup("Directory look and feel");
          }
     }

   len = strlen(sd->realpath);
   if (!strncmp(value, sd->realpath, len))
     efreet_desktop_x_field_set(ef, key, value + len + 1);
   else
     efreet_desktop_x_field_set(ef, key, value);

   efreet_desktop_save(ef);
   if (!sd->desktop) efreet_desktop_free(ef);
}

static void
_set_background_cb(void *data, E_Dialog *dia)
{
   E_Fm2_Smart_Data *sd;
   const char *file;

   sd = data;
   if (!sd) return;

   file = e_widget_fsel_selection_path_get(dia->data);

   if (file)
     _custom_file_key_set(sd, "X-Enlightenment-Directory-Wallpaper", file);

   _e_fm2_view_image_sel_close(data, dia);
   evas_object_smart_callback_call(sd->obj, "dir_changed", NULL);
}

static void
_e_fm2_view_menu_set_background_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   if (sd->image_dialog) return;

   _e_fm2_view_image_sel(sd, _("Set background..."), _set_background_cb);
}

static void
_set_overlay_cb(void *data, E_Dialog *dia)
{
   E_Fm2_Smart_Data *sd;
   const char *file;

   sd = data;
   if (!sd) return;

   file = e_widget_fsel_selection_path_get(dia->data);

   if (file)
     _custom_file_key_set(sd, "X-Enlightenment-Directory-Overlay", file);

   _e_fm2_view_image_sel_close(data, dia);
   evas_object_smart_callback_call(sd->obj, "dir_changed", NULL);
}

static void
_e_fm2_view_menu_set_overlay_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   if (sd->image_dialog) return;

   _e_fm2_view_image_sel(sd, _("Set overlay..."), _set_overlay_cb);
}

static void
_e_fm2_refresh(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   if (sd->refresh_job) ecore_job_del(sd->refresh_job);
   sd->refresh_job = ecore_job_add(_e_fm2_refresh_job_cb, sd->obj);
}

static void
_e_fm2_toggle_hidden_files(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   if (sd->show_hidden_files)
     sd->show_hidden_files = EINA_FALSE;
   else
     sd->show_hidden_files = EINA_TRUE;

   sd->inherited_dir_props = EINA_FALSE;
   _e_fm2_refresh(data, m, mi);
}

static void
_e_fm2_toggle_secure_rm(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd = data;

   sd->config->secure_rm = !sd->config->secure_rm;
}

static void
_e_fm2_toggle_single_click(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   sd->config->view.single_click = !sd->config->view.single_click;
}

static void
_e_fm2_toggle_ordering(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd;
   char buf[4096];

   sd = data;
   if (sd->order_file)
     {
        snprintf(buf, sizeof(buf), "%s/.order", sd->realpath);
        /* FIXME: move to e_fm_main */
        ecore_file_unlink(buf);
     }
   else
     {
        FILE *f;

        snprintf(buf, sizeof(buf), "%s/.order", sd->realpath);
        f = fopen(buf, "w");
        if (f) fclose(f);
     }
   sd->inherited_dir_props = EINA_FALSE;
   _e_fm2_refresh(data, m, mi);
}

static void
_e_fm2_sort(void *data, E_Menu *m, E_Menu_Item *mi)
{
   E_Fm2_Smart_Data *sd;

   sd = data;
   sd->icons = eina_list_sort(sd->icons, eina_list_count(sd->icons),
                              _e_fm2_cb_icon_sort);
   _e_fm2_refresh(data, m, mi);
}

static void
_e_fm2_file_rename(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Icon *ic;
   char text[PATH_MAX + 256];

   ic = data;
   if ((ic->entry_dialog) || (ic->entry_widget)) return;
   if (ic->sd->icon_menu.flags & E_FM2_MENU_NO_RENAME) return;

   if (!_e_fm2_icon_entry_widget_add(ic))
     {
        snprintf(text, PATH_MAX + 256,
                 _("Rename %s to:"),
                 ic->info.file);
        ic->entry_dialog = e_entry_dialog_show(_("Rename File"), "edit-rename",
                                               text, ic->info.file, NULL, NULL,
                                               _e_fm2_file_rename_yes_cb,
                                               _e_fm2_file_rename_no_cb, ic);
        E_OBJECT(ic->entry_dialog)->data = ic;
        e_object_del_attach_func_set(E_OBJECT(ic->entry_dialog),
                                     _e_fm2_file_rename_delete_cb);
     }
}

/* FIXME: this is a stupid hack because it's impossible to get a focus event
 * from e_widget_entry.
 */
static void
_e_fm2_icon_entry_widget_focus_out(void *data, E_Client *ec)
{
   E_Fm2_Icon *ic = data;
   Evas_Object *win;

   win = e_win_evas_win_get(evas_object_evas_get(ic->obj));
   if ((win != e_comp->elm) && (ec == e_win_client_get(win))) return;
   if (ic->entry_widget)
     _e_fm2_icon_entry_widget_del(ic);
}

static Evas_Object *
_e_fm2_icon_entry_widget_add(E_Fm2_Icon *ic)
{
   Evas *e;

   if (ic->sd->iop_icon)
     _e_fm2_icon_entry_widget_accept(ic->sd->iop_icon);

   if (!edje_object_part_exists(ic->obj, "e.swallow.entry"))
     return NULL;

   e = evas_object_evas_get(ic->obj);
   ic->entry_widget = e_widget_entry_add(e_win_evas_win_get(e), NULL, NULL, NULL, NULL);
   evas_object_event_callback_add(ic->entry_widget, EVAS_CALLBACK_KEY_DOWN,
                                  _e_fm2_icon_entry_widget_cb_key_down, ic);
   if (e_comp->comp_type == E_PIXMAP_TYPE_X)
     evas_event_feed_mouse_out(evas_object_evas_get(ic->obj), 0, NULL);
   if (e_comp->evas == e)
     e_comp_grab_input(0, 1);
   ic->keygrab = (e_comp->evas == e);
   edje_object_part_swallow(ic->obj, "e.swallow.entry", ic->entry_widget);
   evas_object_show(ic->entry_widget);
   edje_object_signal_emit(ic->obj, "e,state,rename,on", "e");
   e_widget_entry_text_set(ic->entry_widget, ic->info.file);
   e_widget_focus_set(ic->entry_widget, 1);
   ic->focus_hook = e_client_hook_add(E_CLIENT_HOOK_FOCUS_SET, _e_fm2_icon_entry_widget_focus_out, ic);
   e_widget_entry_select_all(ic->entry_widget);
   ic->sd->iop_icon = ic;
   ic->sd->typebuf.disabled = EINA_TRUE;
   if (e_comp->comp_type == E_PIXMAP_TYPE_X)
     evas_event_feed_mouse_in(e, 0, NULL);
   return ic->entry_widget;
}

static void
_e_fm2_icon_entry_widget_del(E_Fm2_Icon *ic)
{
   ic->sd->iop_icon = NULL;
   evas_object_focus_set(ic->sd->obj, 1);
   evas_object_del(ic->entry_widget);
   ic->entry_widget = NULL;
   E_FREE_FUNC(ic->focus_hook, e_client_hook_del);
   ic->sd->typebuf.disabled = EINA_FALSE;
   if (ic->keygrab)
     {
        if (evas_object_evas_get(ic->obj) == e_comp->evas)
          e_comp_ungrab_input(0, 1);
     }
   ic->keygrab = 0;
   _e_fm2_icon_select(ic);
   edje_object_signal_emit(ic->obj, "e,state,rename,off", "e");
}

static void
_e_fm2_icon_entry_widget_cb_key_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Key_Down *ev;
   E_Fm2_Icon *ic;

   ev = event_info;
   ic = data;

   if (!strcmp(ev->key, "Escape"))
     _e_fm2_icon_entry_widget_del(ic);
   else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
     _e_fm2_icon_entry_widget_accept(ic);
}

static void
_e_fm2_icon_entry_widget_accept(E_Fm2_Icon *ic)
{
   const char *txt;

   txt = e_widget_entry_text_get(ic->entry_widget);
   switch (_e_fm2_file_do_rename(txt, ic))
     {
      case -2:
        e_util_dialog_show(_("Error"), _("%s already exists!"), txt);
        return;

      case -1:
        e_util_dialog_show(_("Error"), _("%s could not be renamed because it is protected"), ic->info.file);
        break;

      case 0:
        e_util_dialog_show(_("Error"), _("Internal filemanager error :("));

      default: /* success! */
        break;
     }
   _e_fm2_icon_entry_widget_del(ic);
}

static void
_e_fm2_file_rename_delete_cb(void *obj)
{
   E_Fm2_Icon *ic;

   ic = E_OBJECT(obj)->data;
   ic->entry_dialog = NULL;
}

static void
_e_fm2_file_rename_yes_cb(void *data, char *text)
{
   E_Fm2_Icon *ic;

   ic = data;
   ic->entry_dialog = NULL;

   switch (_e_fm2_file_do_rename(text, ic))
     {
      case -2:
        e_util_dialog_show(_("Error"), _("%s already exists!"), text);
        _e_fm2_file_rename(ic, NULL, NULL);
        break;

      case -1:
        e_util_dialog_show(_("Error"), _("%s could not be renamed because it is protected"), ic->info.file);
        break;

      case 0:
        e_util_dialog_show(_("Error"), _("Internal filemanager error :("));

      default: /* success! */
        break;
     }
}

static void
_e_fm2_file_rename_no_cb(void *data)
{
   E_Fm2_Icon *ic;

   ic = data;
   ic->entry_dialog = NULL;
}

static int
_e_fm2_file_do_rename(const char *text, E_Fm2_Icon *ic)
{
   char oldpath[PATH_MAX];
   char newpath[PATH_MAX];
   char *args = NULL;
   size_t size = 0;
   size_t length = 0;

   if (!text) return 0;
   if (!strcmp(text, ic->info.file)) return EINA_TRUE;
   _e_fm2_icon_realpath(ic, oldpath, sizeof(oldpath));
   snprintf(newpath, sizeof(newpath), "%s/%s", ic->sd->realpath, text);
   if (e_filereg_file_protected(oldpath)) return -1;
   if (ecore_file_exists(newpath)) return -2;

   args = e_util_string_append_quoted(args, &size, &length, oldpath);
   if (!args) return 0;
   args = e_util_string_append_char(args, &size, &length, ' ');
   if (!args) return 0;
   args = e_util_string_append_quoted(args, &size, &length, newpath);
   if (!args) return 0;

   e_fm2_client_file_move(ic->sd->obj, args);
   free(args);
   return 1;
}

static E_Dialog *
_e_fm_retry_abort_dialog(int pid, const char *str)
{
   E_Dialog *dialog;
   void *id;
   char text[4096 + PATH_MAX];

   id = (intptr_t *)(long)pid;

   dialog = e_dialog_new(NULL, "E", "_fm_overwrite_dialog");
   E_OBJECT(dialog)->data = id;
   e_dialog_button_add(dialog, _("Retry"), NULL, _e_fm_retry_abort_retry_cb, NULL);
   e_dialog_button_add(dialog, _("Abort"), NULL, _e_fm_retry_abort_abort_cb, NULL);

   e_dialog_button_focus_num(dialog, 0);
   e_dialog_title_set(dialog, _("Error"));
   e_dialog_icon_set(dialog, "dialog-error", 64);
   snprintf(text, sizeof(text),
            "%s",
            str);

   e_dialog_text_set(dialog, text);
   elm_win_center(dialog->win, 1, 1);
   e_dialog_show(dialog);
   return dialog;
}

static void
_e_fm_retry_abort_retry_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_RETRY, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_retry_abort_abort_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_aborted(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_ABORT, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static E_Dialog *
_e_fm_overwrite_dialog(int pid, const char *str)
{
   E_Dialog *dialog;
   void *id;
   char text[4096 + PATH_MAX];
   E_Fm2_Op_Registry_Entry *ere;

   id = (intptr_t *)(long)pid;
   ere = e_fm2_op_registry_entry_get(pid);
   if (ere)
     {
        E_Fm2_Smart_Data *sd;
        Eina_List *fms;
        Evas_Object *fm;

        fms = _e_fm2_file_fm2_find(ere->src);
        EINA_LIST_FREE(fms, fm)
        {
           sd = evas_object_smart_data_get(fm);
           E_LIST_FOREACH(sd->icons, _e_fm2_cb_drag_finished_show);
        }
     }

   dialog = e_dialog_new(NULL, "E", "_fm_overwrite_dialog");
   E_OBJECT(dialog)->data = id;
   e_dialog_button_add(dialog, _("No"), NULL, _e_fm_overwrite_no_cb, NULL);
   e_dialog_button_add(dialog, _("No to all"), NULL, _e_fm_overwrite_no_all_cb, NULL);
   e_dialog_button_add(dialog, _("Rename"), NULL, _e_fm_overwrite_rename, NULL);
   e_dialog_button_add(dialog, _("Yes"), NULL, _e_fm_overwrite_yes_cb, NULL);
   e_dialog_button_add(dialog, _("Yes to all"), NULL, _e_fm_overwrite_yes_all_cb, NULL);

   e_dialog_button_focus_num(dialog, 0);
   e_dialog_title_set(dialog, _("Warning"));
   e_dialog_icon_set(dialog, "dialog-warning", 64);
   snprintf(text, sizeof(text),
            _("File already exists, overwrite?<ps/><hilight>%s</hilight>"), str);

   e_dialog_text_set(dialog, text);
   elm_win_center(dialog->win, 1, 1);
   e_dialog_show(dialog);
   return dialog;
}

static void
_e_fm_overwrite_no_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_OVERWRITE_RESPONSE_NO, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_overwrite_no_all_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_OVERWRITE_RESPONSE_NO_ALL, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_overwrite_rename_del(void *data)
{
   E_Fm2_Op_Registry_Entry *ere;
   E_Fm2_Smart_Data *sd;

   ere = e_object_data_get(data);
   if (!ere) return;
   sd = evas_object_smart_data_get(ere->e_fm);
   if (!sd) return;
   sd->rename_dialogs = eina_list_remove(sd->rename_dialogs, data);
   e_fm2_op_registry_entry_unref(ere);
   _e_fm_client_send(E_FM_OP_OVERWRITE_RESPONSE_NO, ere->id, NULL, 0);
}

static void
_e_fm_overwrite_rename_yes_cb(void *data, char *text)
{
   E_Fm2_Op_Registry_Entry *ere = data;
   char newpath[PATH_MAX];
   char *args = NULL;
   const char *f;
   size_t size = 0;
   size_t length = 0;

   if ((!text) || (!text[0])) return;
   if ((!ere->src) || (!ere->dst)) return;
   f = ecore_file_file_get(ere->dst);
   if (!f) return;
   length = strlen(text);
   if (f - ere->dst + length >= PATH_MAX) return;

   newpath[0] = 0;
   strncat(newpath, ere->dst, f - ere->dst);
   strcat(newpath, text);
   if (e_filereg_file_protected(newpath)) return;
   length = 0;
   args = e_util_string_append_quoted(args, &size, &length, ere->src);
   if (!args) return;
   args = e_util_string_append_char(args, &size, &length, ' ');
   if (!args) return;
   args = e_util_string_append_quoted(args, &size, &length, newpath);
   if (!args) return;

   e_fm2_client_file_copy(ere->e_fm, args);
   free(args);
}

static void
_e_fm_overwrite_rename(void *data EINA_UNUSED, E_Dialog *dialog)
{
   char text[PATH_MAX + 256];
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   E_Fm2_Op_Registry_Entry *ere;
   E_Entry_Dialog *ed;
   E_Fm2_Smart_Data *sd;
   const char *file;

   ere = e_fm2_op_registry_entry_get(id);
   if (!ere) return;
   sd = evas_object_smart_data_get(ere->e_fm);
   e_object_del(E_OBJECT(dialog));
   file = ecore_file_file_get(ere->src);
   snprintf(text, sizeof(text), _("Rename %s to:"), file);
   ed = e_entry_dialog_show(_("Rename File"), "edit-rename",
                            text, file, NULL, NULL,
                            _e_fm_overwrite_rename_yes_cb,
                            NULL, ere);
   sd->rename_dialogs = eina_list_append(sd->rename_dialogs, ed);
   e_object_del_attach_func_set(E_OBJECT(ed), _e_fm_overwrite_rename_del);
   E_OBJECT(ed)->data = ere;
   e_fm2_op_registry_entry_ref(ere);
   _e_fm2_op_registry_go_on(id);
}

static void
_e_fm_overwrite_yes_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_OVERWRITE_RESPONSE_YES, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_overwrite_yes_all_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_OVERWRITE_RESPONSE_YES_ALL, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static E_Dialog *
_e_fm_error_dialog(int pid, const char *str)
{
   E_Dialog *dialog;
   void *id;
   char text[4096 + PATH_MAX];
   E_Fm2_Op_Registry_Entry *ere;
   E_Fm2_Smart_Data *sd;
   E_Fm2_Mount *m;
   Eina_Bool devlink = EINA_FALSE;

   id = (intptr_t *)(long)pid;
   ere = e_fm2_op_registry_entry_get(pid);
   if (!ere) return NULL;
   sd = evas_object_smart_data_get(ere->e_fm);
   E_LIST_FOREACH(sd->icons, _e_fm2_cb_drag_finished_show);
   while (sd->realpath)
     {
        /* trying to make or move a link onto a device will fail, create button for
         * moving source at this point if this is what failed
         */
        struct stat st;
        if (sd->config->view.link_drop) break;
        m = e_fm2_device_mount_find(sd->realpath);
        if (!m) break;
        if (ere->op == E_FM_OP_SYMLINK)
          {
             devlink = EINA_TRUE;
             break;
          }
        if (stat(ere->src, &st)) break;
        if (S_ISLNK(st.st_mode)) devlink = EINA_TRUE;
        break;
     }

   dialog = e_dialog_new(NULL, "E", "_fm_error_dialog");
   E_OBJECT(dialog)->data = id;
   e_dialog_button_add(dialog, _("Retry"), NULL, _e_fm_error_retry_cb, NULL);
   e_dialog_button_add(dialog, _("Abort"), NULL, _e_fm_error_abort_cb, NULL);
   if (devlink)
     e_dialog_button_add(dialog, _("Move Source"), NULL, _e_fm_error_link_source, ere);
   e_dialog_button_add(dialog, _("Ignore this"), NULL, _e_fm_error_ignore_this_cb, NULL);
   e_dialog_button_add(dialog, _("Ignore all"), NULL, _e_fm_error_ignore_all_cb, NULL);

   e_dialog_button_focus_num(dialog, 0);
   e_dialog_title_set(dialog, _("Error"));
   snprintf(text, sizeof(text),
            _("An error occurred while performing an operation.<ps/>"
              "%s"),
            str);

   e_dialog_text_set(dialog, text);
   elm_win_center(dialog->win, 1, 1);
   e_dialog_show(dialog);
   return dialog;
}

static void
_e_fm_error_retry_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_RETRY, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_error_abort_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_aborted(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_ABORT, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_error_link_source(void *data, E_Dialog *dialog)
{
   E_Fm2_Op_Registry_Entry *ere = data;
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   char *file;
   char newpath[PATH_MAX];
   char *args = NULL;
   const char *f;
   size_t size = 0;
   size_t length = 0;

   file = ecore_file_readlink(ere->src);
   if (!file) file = (char *)ere->src;

   f = ecore_file_file_get(file);
   if (!f) return;
   length = strlen(f);
   if (strlen(ere->dst) + length >= PATH_MAX) return;

   newpath[0] = 0;
   strncat(newpath, ere->dst, f - ere->dst);
   strcat(newpath, f);
   if (e_filereg_file_protected(newpath)) return;
   length = 0;
   args = e_util_string_append_quoted(args, &size, &length, file);
   if (!args) return;
   args = e_util_string_append_char(args, &size, &length, ' ');
   if (!args) return;
   args = e_util_string_append_quoted(args, &size, &length, newpath);
   if (!args) return;

   e_fm2_client_file_move(ere->e_fm, args);
   free(args);
   if (file != ere->src) free(file);

   _e_fm2_op_registry_aborted(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_ABORT, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_error_ignore_this_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_IGNORE_THIS, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_error_ignore_all_cb(void *data EINA_UNUSED, E_Dialog *dialog)
{
   int id = (int)(intptr_t)E_OBJECT(dialog)->data;
   _e_fm2_op_registry_go_on(id);
   _e_fm_client_send(E_FM_OP_ERROR_RESPONSE_IGNORE_ALL, id, NULL, 0);
   e_object_del(E_OBJECT(dialog));
}

static void
_e_fm_device_error_dialog(const char *title, const char *msg, const char *pstr)
{
   E_Dialog *dialog;
   char text[PATH_MAX];
   const char *u, *d, *n, *m;

   dialog = e_dialog_new(NULL, "E", "_fm_device_error_dialog");
   e_dialog_title_set(dialog, title);
   e_dialog_icon_set(dialog, "drive-harddisk", 64);
   e_dialog_button_add(dialog, _("OK"), NULL, NULL, NULL);

   u = pstr;
   pstr += strlen(pstr) + 1;
   d = pstr;
   pstr += strlen(pstr) + 1;
   n = pstr;
   pstr += strlen(pstr) + 1;
   m = pstr;
   snprintf(text, sizeof(text), "%s<ps/>%s<ps/>%s<ps/>%s<ps/>%s", msg, u, d, n, m);
   e_dialog_text_set(dialog, text);

   elm_win_center(dialog->win, 1, 1);
   e_dialog_button_focus_num(dialog, 0);
   e_dialog_show(dialog);
}

static void
_e_fm2_file_application_properties(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   Efreet_Desktop *desktop;
   E_Fm2_Icon *ic;
   char buf[PATH_MAX];

   ic = data;
   if (!_e_fm2_icon_realpath(ic, buf, sizeof(buf)))
     return;
   desktop = efreet_desktop_get(buf);

   e_desktop_edit(desktop);
}

static void
_e_fm2_file_properties(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Icon *ic;

   ic = data;
   if ((ic->entry_dialog) || (ic->entry_widget)) return;

   if (ic->prop_dialog) e_object_del(E_OBJECT(ic->prop_dialog));
   ic->prop_dialog = e_fm_prop_file(ic);
   E_OBJECT(ic->prop_dialog)->data = ic;
   e_object_del_attach_func_set(E_OBJECT(ic->prop_dialog), _e_fm2_file_properties_delete_cb);
}

static void
_e_fm2_file_properties_delete_cb(void *obj)
{
   E_Fm2_Icon *ic;

   ic = E_OBJECT(obj)->data;
   ic->prop_dialog = NULL;
}

static void
_e_fm2_file_delete_menu(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Fm2_Icon *ic = data;
   if ((!ic) || (!ic->sd)) return;
   _e_fm2_file_delete(ic->sd->obj);
}

static void
_e_fm2_file_delete(Evas_Object *obj)
{
   E_Dialog *dialog;
   E_Fm2_Icon *ic;
   char text[4096 + 256];
   Eina_List *sel;
   int n = 0, folder_count = 0;

   ic = _e_fm2_icon_first_selected_find(obj);
   if (!ic) return;
   if (ic->dialog) return;
   dialog = e_dialog_new(NULL, "E", "_fm_file_delete_dialog");
   ic->dialog = dialog;
   E_OBJECT(dialog)->data = ic;
   e_object_del_attach_func_set(E_OBJECT(dialog), _e_fm2_file_delete_delete_cb);
   e_dialog_button_add(dialog, _("Delete"), NULL, _e_fm2_file_delete_yes_cb, ic);
   e_dialog_button_add(dialog, _("No"), NULL, _e_fm2_file_delete_no_cb, ic);
   e_dialog_button_focus_num(dialog, 0);
   e_dialog_title_set(dialog, _("Confirm Delete"));
   e_dialog_icon_set(dialog, "dialog-warning", 64);
   sel = e_fm2_selected_list_get(obj);
   if (sel)
     {
        n = eina_list_count(sel);
        folder_count = eina_list_count(ic->sd->icons);
     }
   if ((!sel) || (n == 1))
     snprintf(text, sizeof(text),
              _("Are you sure you want to delete<ps/>"
                "<hilight>%s</hilight>?"),
              ic->info.file);
   else if (n == folder_count)
     snprintf(text, sizeof(text),
              _("Are you sure you want to delete<ps/>"
                "<hilight>all</hilight> the %d files in<ps/>"
                "<hilight>%s</hilight>?"),
              n, ic->sd->realpath);
   else
     {
        /* Here the singular version is not used in english, but plural support
         * is nonetheless needed for languages who have multiple plurals
         * depending on the number of files. */
        snprintf(text, sizeof(text),
                 P_("Are you sure you want to delete<ps/>"
                    "the %d selected file in<ps/>"
                    "<hilight>%s</hilight>?",
                    "Are you sure you want to delete<ps/>"
                    "the %d selected files in<ps/>"
                    "<hilight>%s</hilight>?", n),
                 n, ic->sd->realpath);
     }
   if (sel) eina_list_free(sel);
   e_dialog_text_set(dialog, text);
   elm_win_center(dialog->win, 1, 1);
   e_dialog_show(dialog);
}

static void
_e_fm2_file_delete_delete_cb(void *obj)
{
   E_Fm2_Icon *ic;

   ic = E_OBJECT(obj)->data;
   ic->dialog = NULL;
}

static void
_e_fm2_file_delete_yes_cb(void *data, E_Dialog *dialog)
{
   E_Fm2_Icon *ic, *ic_next;
   char buf[PATH_MAX];
   char *files = NULL;
   size_t size = 0;
   size_t len = 0;
   Eina_List *sel, *l;
   E_Fm2_Icon_Info *ici;
   Eina_Bool memerr = EINA_FALSE;

   ic = data;
   ic->dialog = NULL;

   e_object_del(E_OBJECT(dialog));
   ic_next = _e_fm2_icon_next_find(ic->sd->obj, 1, NULL, NULL);
   sel = e_fm2_selected_list_get(ic->sd->obj);
   if (sel && (eina_list_count(sel) != 1))
     {
        EINA_LIST_FOREACH(sel, l, ici)
          {
             if (ic_next && (&(ic_next->info) == ici))
               ic_next = NULL;

             snprintf(buf, sizeof(buf), "%s/%s", ic->sd->realpath, ici->file);
             if (e_filereg_file_protected(buf)) continue;

             if (!memerr)
               {
                  files = e_util_string_append_quoted(files, &size, &len, buf);
                  if (!files) memerr = EINA_TRUE;
                  else
                    {
                       if (eina_list_next(l))
                         {
                            files = e_util_string_append_char(files, &size, &len, ' ');
                            if (!files) memerr = EINA_TRUE;
                         }
                    }
               }
                evas_object_pass_events_set(ici->ic->obj, 1);
          }
        eina_list_free(sel);
     }
   else
     {
        if (sel) eina_list_free(sel);
        _e_fm2_icon_realpath(ic, buf, sizeof(buf));
        if (e_filereg_file_protected(buf)) return;
        evas_object_pass_events_set(ic->obj, 1);
        files = e_util_string_append_quoted(files, &size, &len, buf);
     }
   if (files)
     {
        _e_fm_client_file_del(files, e_config->filemanager_secure_rm | ic->sd->config->secure_rm, ic->sd->obj);
        free(files);
     }

   if (ic_next)
     {
        _e_fm2_icon_select(ic_next);
        evas_object_smart_callback_call(ic_next->sd->obj, "selection_change", NULL);
        _e_fm2_icon_make_visible(ic_next);
     }

   evas_object_smart_callback_call(ic->sd->obj, "files_deleted", NULL);
}

static void
_e_fm2_file_delete_no_cb(void *data, E_Dialog *dialog)
{
   E_Fm2_Icon *ic;

   ic = data;
   ic->dialog = NULL;
   e_object_del(E_OBJECT(dialog));
}

static Eina_Bool
_e_fm2_sys_suspend_hibernate(void *d EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED)
{
   Eina_List *l, *ll, *lll;
   E_Volume *v;
   E_Fm2_Mount *m;

   EINA_LIST_FOREACH(e_fm2_device_volume_list_get(), l, v)
     {
        EINA_LIST_FOREACH_SAFE(v->mounts, ll, lll, m)
          e_fm2_device_unmount(m);
     }
   return ECORE_CALLBACK_RENEW;
}

static void
_e_fm2_refresh_job_cb(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return;
   e_fm2_refresh(data);
   sd->refresh_job = NULL;
}

static void
_e_fm2_live_file_add(Evas_Object *obj, const char *file, const char *file_rel, int after, E_Fm2_Finfo *finf)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Action *a;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   a = E_NEW(E_Fm2_Action, 1);
   if (!a) return;
   sd->live.actions = eina_list_append(sd->live.actions, a);
   a->type = FILE_ADD;
   a->file = eina_stringshare_add(file);
   a->file2 = eina_stringshare_add(file_rel);
   a->flags = after;
   if (finf) memcpy(&(a->finf), finf, sizeof(E_Fm2_Finfo));
   a->finf.lnk = eina_stringshare_add(a->finf.lnk);
   a->finf.rlnk = eina_stringshare_add(a->finf.rlnk);
   _e_fm2_live_process_begin(obj);
}

static void
_e_fm2_live_file_del(Evas_Object *obj, const char *file)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Action *a;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   a = E_NEW(E_Fm2_Action, 1);
   if (!a) return;
   sd->live.actions = eina_list_append(sd->live.actions, a);
   a->type = FILE_DEL;
   a->file = eina_stringshare_add(file);
   _e_fm2_live_process_begin(obj);
}

static void
_e_fm2_live_file_changed(Evas_Object *obj, const char *file, E_Fm2_Finfo *finf)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Action *a;

   sd = evas_object_smart_data_get(obj);
   if (!sd) return;
   a = E_NEW(E_Fm2_Action, 1);
   if (!a) return;
   sd->live.actions = eina_list_append(sd->live.actions, a);
   a->type = FILE_CHANGE;
   a->file = eina_stringshare_add(file);
   if (finf) memcpy(&(a->finf), finf, sizeof(E_Fm2_Finfo));
   a->finf.lnk = eina_stringshare_add(a->finf.lnk);
   a->finf.rlnk = eina_stringshare_add(a->finf.rlnk);
   _e_fm2_live_process_begin(obj);
}

static void
_e_fm2_live_process_begin(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(obj);
   if (!sd->live.actions) return;
   if ((sd->live.idler) || (sd->live.timer) ||
       (sd->listing) || (sd->scan_timer)) return;
   sd->live.idler = ecore_idler_add(_e_fm2_cb_live_idler, obj);
   sd->live.timer = ecore_timer_loop_add(0.2, _e_fm2_cb_live_timer, obj);
   sd->tmp.last_insert = NULL;
}

static void
_e_fm2_live_process_end(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Action *a;

   sd = evas_object_smart_data_get(obj);
   EINA_LIST_FREE(sd->live.actions, a)
     {
        eina_stringshare_del(a->file);
        eina_stringshare_del(a->file2);
        eina_stringshare_del(a->finf.lnk);
        eina_stringshare_del(a->finf.rlnk);
        free(a);
     }
   if (sd->live.idler)
     {
        ecore_idler_del(sd->live.idler);
        sd->live.idler = NULL;
     }
   if (sd->live.timer)
     {
        ecore_timer_del(sd->live.timer);
        sd->live.timer = NULL;
     }
   sd->tmp.last_insert = NULL;
}

static void
_e_fm2_live_process(Evas_Object *obj)
{
   E_Fm2_Smart_Data *sd;
   E_Fm2_Action *a;
   Eina_List *l;
   E_Fm2_Icon *ic;

   sd = evas_object_smart_data_get(obj);
   if (!sd->live.actions) return;
   a = eina_list_data_get(sd->live.actions);
   sd->live.actions = eina_list_remove_list(sd->live.actions, sd->live.actions);
   switch (a->type)
     {
      case FILE_ADD:
        /* new file to sort in place */
        if (!strcmp(a->file, ".order"))
          {
             sd->order_file = EINA_TRUE;
             /* FIXME: reload fm view */
          }
        else
          {
             if (!((a->file[0] == '.') && (!sd->show_hidden_files)))
               _e_fm2_file_add(obj, a->file, 1, a->file2, a->flags, &(a->finf));
          }
        break;

      case FILE_DEL:
        if (!strcmp(a->file, ".order"))
          {
             sd->order_file = EINA_FALSE;
             /* FIXME: reload fm view */
          }
        else
          {
             if (!((a->file[0] == '.') && (!sd->show_hidden_files)))
               _e_fm2_file_del(obj, a->file);
             sd->live.deletions = EINA_TRUE;
          }
        break;

      case FILE_CHANGE:
        if (!strcmp(a->file, ".order"))
          {
             /* FIXME: reload fm view - ignore for now */
          }
        else
          {
             if (!((a->file[0] == '.') && (!sd->show_hidden_files)))
               {
                  EINA_LIST_FOREACH(sd->icons, l, ic)
                    {
                       if (!strcmp(ic->info.file, a->file))
                         {
                            if (ic->removable_state_change)
                              {
                                 _e_fm2_icon_unfill(ic);
                                 _e_fm2_icon_fill(ic, &(a->finf));
                                 ic->removable_state_change = EINA_FALSE;
                                 if ((ic->realized) && (ic->obj_icon))
                                   {
                                      _e_fm2_icon_removable_update(ic);
                                      _e_fm2_icon_label_set(ic, ic->obj);
                                   }
                              }
                            else if (!eina_str_has_extension(ic->info.file, ".part"))
                              {
                                 int realized;

                                 realized = ic->realized;
                                 if (realized) _e_fm2_icon_unrealize(ic);
                                 _e_fm2_icon_unfill(ic);
                                 _e_fm2_icon_fill(ic, &(a->finf));
                                 if (realized) _e_fm2_icon_realize(ic);
                              }
                            break;
                         }
                    }
               }
          }
        break;

      default:
        break;
     }
   eina_stringshare_del(a->file);
   eina_stringshare_del(a->file2);
   eina_stringshare_del(a->finf.lnk);
   eina_stringshare_del(a->finf.rlnk);
   free(a);
}

static Eina_Bool
_e_fm2_cb_live_idler(void *data)
{
   E_Fm2_Smart_Data *sd;
   double t;

   sd = evas_object_smart_data_get(data);
   if (!sd) return ECORE_CALLBACK_CANCEL;
   t = ecore_time_get();
   do
     {
        if (!sd->live.actions) break;
        _e_fm2_live_process(data);
     }
   while ((ecore_time_get() - t) > 0.02);
   if (sd->live.actions) return ECORE_CALLBACK_RENEW;
   _e_fm2_live_process_end(data);
   _e_fm2_cb_live_timer(data);
   if ((sd->order_file) || (sd->config->view.always_order))
     {
        e_fm2_refresh(data);
     }
   sd->live.idler = NULL;
   return ECORE_CALLBACK_CANCEL;
}

static Eina_Bool
_e_fm2_cb_live_timer(void *data)
{
   E_Fm2_Smart_Data *sd;

   sd = evas_object_smart_data_get(data);
   if (!sd) return ECORE_CALLBACK_CANCEL;
   if (sd->queue) _e_fm2_queue_process(data);
   else if (sd->iconlist_changed)
     {
        if (sd->resize_job) ecore_job_del(sd->resize_job);
        sd->resize_job = ecore_job_add(_e_fm2_cb_resize_job, sd->obj);
     }
   else
     {
        if (sd->live.deletions)
          {
             sd->iconlist_changed = EINA_TRUE;
             if (sd->resize_job) ecore_job_del(sd->resize_job);
             sd->resize_job = ecore_job_add(_e_fm2_cb_resize_job, sd->obj);
          }
     }
   sd->live.deletions = EINA_FALSE;
   sd->live.timer = NULL;
   if ((!sd->queue) && (!sd->live.idler)) return ECORE_CALLBACK_CANCEL;
   sd->live.timer = ecore_timer_loop_add(0.2, _e_fm2_cb_live_timer, data);
   return ECORE_CALLBACK_CANCEL;
}

static int
_e_fm2_theme_edje_object_set(E_Fm2_Smart_Data *sd, Evas_Object *o, const char *category, const char *group)
{
   char buf[1024];
   int ret;

   if (sd->custom_theme_content)
     snprintf(buf, sizeof(buf), "e/fileman/%s/%s", sd->custom_theme_content, group);
   else
     snprintf(buf, sizeof(buf), "e/fileman/default/%s", group);

   if (sd->custom_theme)
     {
        if (edje_object_file_set(o, sd->custom_theme, buf)) return 1;
     }
   if (sd->custom_theme)
     {
        if (!ecore_file_exists(sd->custom_theme))
          {
             eina_stringshare_del(sd->custom_theme);
             sd->custom_theme = NULL;
          }
     }
   ret = e_theme_edje_object_set(o, category, buf);
   return ret;
}

static int
_e_fm2_theme_edje_icon_object_set(E_Fm2_Smart_Data *sd, Evas_Object *o, const char *category, const char *group)
{
   char buf[1024];
   int ret;

//   if (sd->custom_theme_content)
//     snprintf(buf, sizeof(buf), "e/icons/fileman/%s/%s", sd->custom_theme_content, group);
//   else
   snprintf(buf, sizeof(buf), "e/icons/fileman/mime/%s", group);

   if (sd->custom_theme)
     {
        if (edje_object_file_set(o, sd->custom_theme, buf)) return 1;
     }
   if (sd->custom_theme)
     {
        if (!ecore_file_exists(sd->custom_theme))
          {
             eina_stringshare_del(sd->custom_theme);
             sd->custom_theme = NULL;
          }
     }
   ret = e_theme_edje_object_set(o, category, buf);
   return ret;
}

static void
_e_fm2_volume_mount(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Volume *v;
   const char *mp;

   v = data;
   if (!v) return;

   mp = e_fm2_device_volume_mountpoint_get(v);
   _e_fm2_client_mount(v->udi, mp);
   eina_stringshare_del(mp);
}

static void
_e_fm2_volume_unmount(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Volume *v;

   v = data;
   if (!v) return;

   v->auto_unmount = EINA_FALSE;
   _e_fm2_client_unmount(v->udi);
}

static void
_e_fm2_volume_eject(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   E_Volume *v;

   v = data;
   if (!v) return;

   v->auto_unmount = EINA_FALSE;
   _e_fm2_client_eject(v->udi);
}

static void
_update_volume_icon(E_Volume *v, E_Fm2_Icon *ic)
{
   Evas_Object *e = e_icon_edje_get(ic->obj_icon);
   if (e)
     {
        if (ic->info.removable_full)
          e_icon_edje_emit(ic->obj_icon, "e,state,removable,full", "e");
        else
          e_icon_edje_emit(ic->obj_icon, "e,state,removable,empty", "e");
     }

   if (v)
     {
        if (v->mounted)
          edje_object_signal_emit(ic->obj, "e,state,volume,mounted", "e");
        else
          edje_object_signal_emit(ic->obj, "e,state,volume,unmounted", "e");
     }
   else
     edje_object_signal_emit(ic->obj, "e,state,volume,off", "e");
}

static void
_e_fm2_volume_icon_update(E_Volume *v)
{
   Evas_Object *o;
   char file[PATH_MAX], fav[PATH_MAX];
   const char *desk;
   Eina_List *l;
   E_Fm2_Icon *ic;

   if (!v || !v->storage) return;

   e_user_dir_snprintf(fav, sizeof(fav), "fileman/favorites");
   desk = efreet_desktop_dir_get();
   snprintf(file, sizeof(file), "|%s_%d.desktop",
            ecore_file_file_get(v->storage->udi), v->partition_number);

   EINA_LIST_FOREACH(_e_fm2_list, l, o)
     {
        const char *rp;

        if ((_e_fm2_list_walking > 0) &&
            (eina_list_data_find(_e_fm2_list_remove, o))) continue;

        rp = e_fm2_real_path_get(o);
        if ((rp) && (strcmp(rp, fav)) && (strcmp(rp, desk))) continue;

        ic = _e_fm2_icon_find(o, file);
        if (ic)
          _update_volume_icon(v, ic);
     }
}

static void
_e_fm2_icon_removable_update(E_Fm2_Icon *ic)
{
   E_Volume *v;

   if (!ic) return;
   v = e_fm2_device_volume_find(ic->info.link);
   _update_volume_icon(v, ic);
}

static void
_e_fm2_operation_abort_internal(E_Fm2_Op_Registry_Entry *ere)
{
   ere->status = E_FM2_OP_STATUS_ABORTED;
   ere->finished = 1;
   ere->needs_attention = 0;
   ere->dialog = NULL;
   e_fm2_op_registry_entry_changed(ere);
   _e_fm_client_send(E_FM_OP_ABORT, ere->id, NULL, 0);
}

E_API void
e_fm2_operation_abort(int id)
{
   E_Fm2_Op_Registry_Entry *ere;

   ere = e_fm2_op_registry_entry_get(id);
   if (!ere) return;

   e_fm2_op_registry_entry_ref(ere);
   e_fm2_op_registry_entry_abort(ere);
   e_fm2_op_registry_entry_unref(ere);
}

E_API Eina_Bool
e_fm2_optimal_size_calc(Evas_Object *obj, int minw, int maxw, int maxh, int *w, int *h)
{
   int x, y, step_w, step_h;
   EFM_SMART_CHECK(EINA_FALSE);
   if ((!w) || (!h)) return EINA_FALSE;
   if (!sd->icons) return EINA_FALSE;
   if (maxw < 0) maxw = 0;
   if (maxh < 0) maxh = 0;
   step_w = sd->min.w + 5, step_h = sd->min.h + 5;
   switch (_e_fm2_view_mode_get(sd))
     {
      case E_FM2_VIEW_MODE_LIST:
        *w = MIN(step_w, maxw);
        *h = MIN(step_h * eina_list_count(sd->icons), (unsigned int)maxh);
        return EINA_TRUE;

      default:
        break;
     }
   y = x = (int)sqrt(eina_list_count(sd->icons));
   if (maxw && (x * step_w > maxw))
     {
        x = maxw / step_w;
        y = (eina_list_count(sd->icons) / x) + ((maxw % step_w) ? 1 : 0);
     }
   if (minw && (x * step_w < minw))
     {
        x = minw / step_w;
        y = (eina_list_count(sd->icons) / x) + ((minw % step_w) ? 1 : 0);
     }
   *w = step_w * x;
   *w = MIN(*w, maxw);
   *h = step_h * y;
   *h = MIN(*h, maxh);
   return EINA_TRUE;
}

E_API E_Fm2_View_Mode
e_fm2_view_mode_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(0);
   return _e_fm2_view_mode_get(sd);
}

E_API Eina_Bool
e_fm2_typebuf_visible_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(EINA_FALSE);
   return sd->typebuf_visible;
}

E_API void
e_fm2_typebuf_clear(Evas_Object *obj)
{
   EFM_SMART_CHECK();
   _e_fm2_typebuf_hide(obj);
}

E_API void
e_fm2_overlay_clip_to(Evas_Object *obj, Evas_Object *clip)
{
   int x, y, w, h;
   EFM_SMART_CHECK();
   if (sd->overlay_clip)
     {
        evas_object_event_callback_del_full(sd->overlay_clip, EVAS_CALLBACK_MOVE, _e_fm2_overlay_clip_move, sd);
        evas_object_event_callback_del_full(sd->overlay_clip, EVAS_CALLBACK_RESIZE, _e_fm2_overlay_clip_resize, sd);
     }
   sd->overlay_clip = clip;
   if (clip)
     {
        evas_object_clip_set(sd->overlay, clip);
        evas_object_geometry_get(clip, &x, &y, &w, &h);
        evas_object_move(sd->overlay, x, y);
        evas_object_resize(sd->overlay, w, h);
        evas_object_event_callback_add(clip, EVAS_CALLBACK_MOVE, _e_fm2_overlay_clip_move, sd);
        evas_object_event_callback_add(clip, EVAS_CALLBACK_RESIZE, _e_fm2_overlay_clip_resize, sd);
     }
   else
     {
        evas_object_clip_set(sd->overlay, sd->clip);
        evas_object_move(sd->overlay, sd->x, sd->y);
        evas_object_resize(sd->overlay, sd->w, sd->h);
     }
}

E_API const char *
e_fm2_real_path_map(const char *dev, const char *path)
{
   return _e_fm2_dev_path_map(NULL, dev, path);
}

E_API void
e_fm2_favorites_init(void)
{
   Eina_List *files;
   char buf[PATH_MAX], buf2[PATH_MAX], *file;

   // make dir for favorites and install ones shipped
   e_user_dir_concat_static(buf, "fileman/favorites");
   ecore_file_mkpath(buf);
   if (!ecore_file_dir_is_empty(buf)) return;
   e_prefix_data_concat(buf, sizeof(buf), "data/favorites");
   files = ecore_file_ls(buf);
   if (!files) return;
   EINA_LIST_FREE(files, file)
     {
        e_prefix_data_snprintf(buf, sizeof(buf), "data/favorites/%s", file);
        snprintf(buf2, sizeof(buf2), "%s/fileman/favorites/%s",
                 e_user_dir_get(), file);
        ecore_file_cp(buf, buf2);
        free(file);
     }
}

E_API unsigned int
e_fm2_selected_count(Evas_Object *obj)
{
   EFM_SMART_CHECK(0);
   return eina_list_count(sd->selected_icons);
}

E_API E_Fm2_Icon_Info *
e_fm2_drop_icon_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(NULL);
   return sd->drop_icon ? &sd->drop_icon->info : NULL;
}

E_API Eina_List *
e_fm2_uri_path_list_get(const Eina_List *uri_list)
{
   E_Fm2_Uri *uri;
   const Eina_List *l;
   Eina_List *path_list = NULL;
   char current_hostname[_POSIX_HOST_NAME_MAX];
   const char *uri_str;

   if (gethostname(current_hostname, _POSIX_HOST_NAME_MAX) == -1)
     current_hostname[0] = '\0';

   EINA_LIST_FOREACH(uri_list, l, uri_str)
     {
        if (!(uri = _e_fm2_uri_parse(uri_str)))
          continue;

        if (!uri->hostname || !strcmp(uri->hostname, "localhost")
            || !strcmp(uri->hostname, current_hostname))
          {
             path_list = eina_list_append(path_list, uri->path);
          }
        else
          eina_stringshare_del(uri->path);

        eina_stringshare_del(uri->hostname);
        E_FREE(uri);
     }

   return path_list;
}

E_API Efreet_Desktop *
e_fm2_desktop_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(NULL);
   if (_e_fm2_desktop_open(sd) != 1) return NULL;
   return sd->desktop;
}

E_API void
e_fm2_drop_menu(Evas_Object *obj, char *args)
{
   E_Menu *menu;
   E_Menu_Item *item;
   E_Zone *zone;
   int x, y;

   EFM_SMART_CHECK();

   EINA_SAFETY_ON_TRUE_RETURN(!!sd->menu);
   sd->menu = menu = e_menu_new();
   if (!menu) return;

   e_menu_post_deactivate_callback_set(menu, _e_fm2_menu_post_cb, sd);

   evas_object_data_set(obj, "drop_menu_data", args);
   e_object_data_set(E_OBJECT(menu), obj);
   e_object_free_attach_func_set(E_OBJECT(menu), _e_fm_drop_menu_free);

   item = e_menu_item_new(menu);
   e_menu_item_label_set(item, _("Copy"));
   e_menu_item_callback_set(item, _e_fm_drop_menu_copy_cb, obj);
   e_util_menu_item_theme_icon_set(item, "edit-copy");

   item = e_menu_item_new(menu);
   e_menu_item_label_set(item, _("Move"));
   e_menu_item_callback_set(item, _e_fm_drop_menu_move_cb, obj);
   e_menu_item_icon_edje_set(item,
                             e_theme_edje_file_get("base/theme/fileman",
                                                   "e/fileman/default/button/move"),
                             "e/fileman/default/button/move");

   item = e_menu_item_new(menu);
   e_menu_item_label_set(item, _("Link"));
   e_menu_item_callback_set(item, _e_fm_drop_menu_symlink_cb, obj);
   e_util_menu_item_theme_icon_set(item, "emblem-symbolic-link");

   item = e_menu_item_new(menu);
   e_menu_item_separator_set(item, 1);

   item = e_menu_item_new(menu);
   e_menu_item_label_set(item, _("Abort"));
   e_menu_item_callback_set(item, _e_fm_drop_menu_abort_cb, obj);
   e_menu_item_icon_edje_set(item,
                             e_theme_edje_file_get("base/theme/fileman",
                                                   "e/fileman/default/button/abort"),
                             "e/fileman/default/button/abort");

   ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   zone = e_zone_current_get();
   if (!zone) goto error;
   e_menu_activate_mouse(menu, zone, x, y, 1, 1, E_MENU_POP_DIRECTION_DOWN, 0);
   return;
error:
   e_object_del(E_OBJECT(menu));
   sd->menu = NULL;
}

E_API E_Fm2_Icon *
e_fm2_icon_editing_get(Evas_Object *obj)
{
   EFM_SMART_CHECK(NULL);
   return sd->iop_icon;
}
