You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1407 lines
43 KiB

#include "ephoto.h"
#define FILESEP "file://"
#define FILESEP_LEN sizeof(FILESEP) - 1
#define TODO_ITEM_MIN_BATCH 5
#define ANIM_TIME 0.2
typedef struct _Ephoto_Directory_Browser Ephoto_Directory_Browser;
struct _Ephoto_Directory_Browser
{
Ephoto *ephoto;
Evas_Object *main;
Evas_Object *fsel;
Evas_Object *fsel_back;
Evas_Object *leftbox;
Elm_Object_Item *dir_current;
Elm_Object_Item *last_sel;
Eio_File *ls;
Eina_Bool dirs_only;
Eina_Bool thumbs_only;
Eio_Monitor *monitor;
Eina_List *monitor_handlers;
Eina_List *handlers;
Eina_List *todo_items;
Ecore_Job *change_dir_job;
Ecore_Timer *click_timer;
Eina_Bool processing;
Eina_Bool initializing;
struct
{
Ecore_Animator *todo_items;
int count;
int processed;
} animator;
Eina_Bool main_deleted : 1;
const char *back_directory;
};
static Elm_Genlist_Item_Class *_ephoto_dir_class;
static Elm_Genlist_Item_Class *_ephoto_dir_tree_class;
static char *_drag_data_extract(char **drag_data);
static Eina_Bool _monitor_cb(void *data, int type,
void *event);
static void _fsel_mouse_up_cb(void *data, Evas *e EINA_UNUSED,
Evas_Object *obj EINA_UNUSED, void *event_info);
/*File Pane Callbacks*/
static void
_menu_dismissed_cb(void *data, Evas_Object *obj,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
evas_object_del(obj);
elm_object_focus_set(db->main, EINA_TRUE);
}
static void
_menu_empty_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
Eina_List *paths = NULL;
Elm_Object_Item *item;
Ephoto_Entry *file;
item = elm_genlist_first_item_get(db->fsel);
while (item)
{
file = elm_object_item_data_get(item);
paths = eina_list_append(paths, strdup(file->path));
item = elm_genlist_item_next_get(item);
}
if (eina_list_count(paths) <= 0)
return;
ephoto_file_empty_trash(db->ephoto, paths);
}
static Eina_Bool
_drop_dropcb(void *data EINA_UNUSED, Evas_Object *obj, Elm_Object_Item *it,
Elm_Selection_Data *ev, int xposret EINA_UNUSED, int yposret EINA_UNUSED)
{
if (!it)
return EINA_FALSE;
Ephoto_Entry *entry = elm_object_item_data_get(it);
const char *path = entry->path;
Eina_List *files = NULL;
Ephoto_Directory_Browser *db = evas_object_data_get(obj, "directory_browser");
if (!ev->data)
return EINA_FALSE;
if (ev->len <= 0)
return EINA_FALSE;
if (!path)
return EINA_FALSE;
char *dd = strdup(ev->data);
if (!dd)
return EINA_FALSE;
char *s = _drag_data_extract(&dd);
while (s)
{
if (evas_object_image_extension_can_load_get(s))
files = eina_list_append(files, s);
files = eina_list_append(files, s);
s = _drag_data_extract(&dd);
}
free(dd);
if (eina_list_count(files) <= 0)
return EINA_TRUE;
if (db->ephoto->config->move_drop)
ephoto_file_move(db->ephoto, files, path);
else
ephoto_file_copy(db->ephoto, files, path);
if (db->dir_current)
elm_genlist_item_selected_set(db->dir_current, EINA_TRUE);
return EINA_TRUE;
}
static Elm_Object_Item *
_drop_item_getcb(Evas_Object *obj, Evas_Coord x, Evas_Coord y,
int *xposret EINA_UNUSED, int *yposret)
{
Elm_Object_Item *gli;
gli = elm_genlist_at_xy_item_get(obj, x, y, yposret);
return gli;
}
static void
_drop_enter(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED)
{
return;
}
static void
_drop_leave(void *data, Evas_Object *obj EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
if (db->dir_current)
elm_genlist_item_selected_set(db->dir_current, EINA_TRUE);
}
static void
_drop_pos(void *data EINA_UNUSED, Evas_Object *cont EINA_UNUSED,
Elm_Object_Item *it, Evas_Coord x EINA_UNUSED,
Evas_Coord y EINA_UNUSED, int xposret EINA_UNUSED,
int yposret EINA_UNUSED, Elm_Xdnd_Action action EINA_UNUSED)
{
elm_genlist_item_selected_set(it, EINA_TRUE);
}
static char *
_drag_data_extract(char **drag_data)
{
char *uri = NULL;
if (!drag_data)
return uri;
char *p = *drag_data;
if (!p)
return uri;
char *s = strstr(p, FILESEP);
if (s)
p += FILESEP_LEN;
s = strchr(p, '\n');
uri = p;
if (s)
{
if (s - p > 0)
{
char *s1 = s - 1;
if (s1[0] == '\r')
s1[0] = '\0';
else
{
char *s2 = s + 1;
if (s2[0] == '\r')
{
s[0] = '\0';
s++;
}
else
s[0] = '\0';
}
}
else
s[0] = '\0';
s++;
}
else
p = NULL;
*drag_data = s;
return uri;
}
static int
_entry_cmp(const void *pa, const void *pb)
{
const Ephoto_Entry *a, *b;
a = elm_object_item_data_get(pa);
b = elm_object_item_data_get(pb);
return strcasecmp(a->basename, b->basename);
}
static void
_on_list_expand_req(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *it = event_info;
if (db->initializing)
return;
ecore_job_del(db->change_dir_job);
db->change_dir_job = NULL;
ecore_timer_del(db->click_timer);
db->click_timer = NULL;
elm_genlist_item_expanded_set(it, EINA_TRUE);
}
static void
_on_list_contract_req(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *it = event_info;
if (db->initializing)
return;
ecore_job_del(db->change_dir_job);
db->change_dir_job = NULL;
ecore_timer_del(db->click_timer);
db->click_timer = NULL;
elm_genlist_item_expanded_set(it, EINA_FALSE);
}
static void
_on_list_expanded(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *it = event_info;
Evas_Object *icon;
Ephoto_Entry *entry;
const char *path;
icon = elm_object_item_part_content_get(it, "elm.swallow.icon");
elm_icon_standard_set(icon, "folder-open");
if (db->initializing)
return;
entry = elm_object_item_data_get(it);
path = entry->path;
db->dirs_only = 0;
if (!strcmp(path, db->ephoto->config->directory))
{
db->dirs_only = 1;
ephoto_thumb_browser_dirs_only_set(db->ephoto, EINA_TRUE);
}
db->thumbs_only = 0;
ephoto_directory_set(db->ephoto, path, it, db->dirs_only, db->thumbs_only);
ephoto_title_set(db->ephoto, db->ephoto->config->directory);
}
static void
_on_list_contracted(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *it = event_info;
Evas_Object *icon;
Ephoto_Entry *entry;
const char *path;
icon = elm_object_item_part_content_get(it, "elm.swallow.icon");
elm_icon_standard_set(icon, "folder");
if (db->initializing)
return;
entry = elm_object_item_data_get(it);
path = entry->path;
elm_genlist_item_subitems_clear(it);
if (!strcmp(path, db->ephoto->config->directory))
return;
db->thumbs_only = 1;
db->dirs_only = 0;
ephoto_directory_set(db->ephoto, path, NULL,
db->dirs_only, db->thumbs_only);
ephoto_title_set(db->ephoto,
db->ephoto->config->directory);
}
static void
_dir_job(void *data)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *it = evas_object_data_get(db->fsel, "current_item");
Ephoto_Entry *entry;
const char *path;
entry = elm_object_item_data_get(it);
path = entry->path;
db->change_dir_job = NULL;
db->thumbs_only = 1;
db->dirs_only = 0;
ephoto_directory_set(db->ephoto, path, NULL,
db->dirs_only, db->thumbs_only);
ephoto_title_set(db->ephoto, db->ephoto->config->directory);
}
static void
_wait_job(void *data)
{
Ephoto_Directory_Browser *db = data;
if (db->change_dir_job)
ecore_job_del(db->change_dir_job);
db->change_dir_job = ecore_job_add(_dir_job, db);
}
static void
_on_list_selected(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *it = event_info;
if (!it)
return;
evas_object_data_set(db->fsel, "current_item", it);
db->dir_current = it;
ecore_job_add(_wait_job, db);
}
static char *
_dir_item_text_get(void *data, Evas_Object *obj EINA_UNUSED,
const char *part EINA_UNUSED)
{
Ephoto_Entry *e = data;
return strdup(e->label);
}
static Evas_Object *
_dir_item_icon_get(void *data, Evas_Object *obj,
const char *part)
{
Ephoto_Entry *entry = data;
if (!strcmp(part, "elm.swallow.end"))
return NULL;
Evas_Object *ic = elm_icon_add(obj);
if (entry->item)
{
if (elm_genlist_item_expanded_get(entry->item))
elm_icon_standard_set(ic, "folder-open");
else
elm_icon_standard_set(ic, "folder");
}
else
{
elm_icon_standard_set(ic, "folder");
}
return ic;
}
static void
_dir_item_del(void *data, Evas_Object *obj EINA_UNUSED)
{
Ephoto_Entry *e = data;
if (!e->no_delete)
ephoto_entry_free(e->ephoto, e);
}
static Eina_Bool
_check_for_subdirs(Ephoto_Entry *entry)
{
Eina_Iterator *ls = eina_file_stat_ls(entry->path);
Eina_File_Direct_Info *info;
if (!ls)
return EINA_FALSE;
EINA_ITERATOR_FOREACH(ls, info)
{
char *rp = ecore_file_realpath(info->path);
if (info->type != EINA_FILE_DIR && info->type != EINA_FILE_LNK)
{
free(rp);
continue;
}
if (info->type == EINA_FILE_LNK && !ecore_file_is_dir((const char *)rp))
{
free(rp);
continue;
}
eina_iterator_free(ls);
free(rp);
return EINA_TRUE;
}
eina_iterator_free(ls);
return EINA_FALSE;
}
static void
_trash_back(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
elm_box_clear(db->leftbox);
db->fsel = db->fsel_back;
elm_box_pack_end(db->leftbox, db->fsel);
evas_object_show(db->fsel);
db->fsel_back = NULL;
db->thumbs_only = 1;
db->dirs_only = 0;
ephoto_directory_set(db->ephoto, db->back_directory, NULL,
db->dirs_only, db->thumbs_only);
ephoto_title_set(db->ephoto, db->back_directory);
}
static void
_dir_go_trash(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
Evas_Object *ic, *but;
db->fsel_back = db->fsel;
evas_object_hide(db->fsel_back);
elm_box_unpack(db->leftbox, db->fsel_back);
ic = elm_icon_add(db->leftbox);
elm_icon_standard_set(ic, "go-previous");
evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
but = elm_button_add(db->leftbox);
elm_object_text_set(but, _("Back"));
elm_object_part_content_set(but, "icon", ic);
EPHOTO_WEIGHT(but, EVAS_HINT_EXPAND, EVAS_HINT_FILL);
EPHOTO_FILL(but);
evas_object_smart_callback_add(but, "clicked", _trash_back, db);
elm_box_pack_end(db->leftbox, but);
evas_object_show(but);
db->fsel = elm_genlist_add(db->leftbox);
elm_genlist_select_mode_set(db->fsel, ELM_OBJECT_SELECT_MODE_ALWAYS);
elm_genlist_highlight_mode_set(db->fsel, EINA_TRUE);
EPHOTO_EXPAND(db->fsel);
EPHOTO_FILL(db->fsel);
evas_object_size_hint_min_set(db->fsel, 195 * elm_config_scale_get(), 0);
evas_object_smart_callback_add(db->fsel, "expand,request",
_on_list_expand_req, db);
evas_object_smart_callback_add(db->fsel, "contract,request",
_on_list_contract_req, db);
evas_object_smart_callback_add(db->fsel, "expanded", _on_list_expanded, db);
evas_object_smart_callback_add(db->fsel, "contracted", _on_list_contracted,
db);
evas_object_event_callback_add(db->fsel, EVAS_CALLBACK_MOUSE_UP,
_fsel_mouse_up_cb, db);
evas_object_data_set(db->fsel, "directory_browser", db);
elm_box_pack_end(db->leftbox, db->fsel);
evas_object_show(db->fsel);
eina_stringshare_replace(&db->back_directory,
db->ephoto->config->directory);
if (!ecore_file_exists(db->ephoto->trash_path))
ecore_file_mkpath(db->ephoto->trash_path);
db->thumbs_only = 0;
db->dirs_only = 0;
ephoto_directory_set(db->ephoto, db->ephoto->trash_path, NULL,
db->dirs_only, db->thumbs_only);
ephoto_title_set(db->ephoto, _("Trash"));
ephoto_directory_browser_top_dir_set(db->ephoto, db->ephoto->config->directory);
}
static Eina_Bool
_click_timer_cb(void *data)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *item = evas_object_data_get(db->fsel, "current_item");
_on_list_selected(db, NULL, item);
db->click_timer = NULL;
return ECORE_CALLBACK_CANCEL;
}
static void
_fsel_menu_go_root(void *data, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
Ephoto *ephoto = data;
const char *path = "/";
char *rp = ecore_file_realpath(path);
ephoto_directory_browser_clear(ephoto);
ephoto_thumb_browser_clear(ephoto);
eina_stringshare_replace(&ephoto->config->directory, rp);
ephoto_directory_browser_top_dir_set(ephoto, ephoto->config->directory);
ephoto_directory_browser_initialize_structure(ephoto);
free(rp);
}
static void
_fsel_menu_go_home(void *data, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
Ephoto *ephoto = data;
const char *path = eina_environment_home_get();
char *rp = ecore_file_realpath(path);
ephoto_directory_browser_clear(ephoto);
ephoto_thumb_browser_clear(ephoto);
eina_stringshare_replace(&ephoto->config->directory, rp);
ephoto_directory_browser_top_dir_set(ephoto, ephoto->config->directory);
ephoto_directory_browser_initialize_structure(ephoto);
free(rp);
}
static void
_fsel_menu_new_dir_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *item = elm_genlist_selected_item_get(db->fsel);
Ephoto_Entry *entry;
const char *path;
if (item)
{
entry = elm_object_item_data_get(item);
path = entry->path;
}
else
path = db->ephoto->config->directory;
if (!path)
return;
ephoto_file_new_dir(db->ephoto, path);
}
static void
_fsel_menu_paste_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *item = elm_genlist_selected_item_get(db->fsel);
ephoto_thumb_browser_paste(db->ephoto, item);
}
static void
_fsel_menu_rename_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *item = elm_genlist_selected_item_get(db->fsel);
Ephoto_Entry *entry;
const char *path;
if (!item)
return;
entry = elm_object_item_data_get(item);
path = entry->path;
if (!path)
return;
ephoto_file_rename(db->ephoto, path);
}
static void
_fsel_menu_delete_cb(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info EINA_UNUSED)
{
Ephoto_Directory_Browser *db = data;
Elm_Object_Item *item = elm_genlist_selected_item_get(db->fsel);
Ephoto_Entry *entry;
Eina_List *files = NULL;
const char *path;
if (!item)
return;
entry = elm_object_item_data_get(item);
path = entry->path;
if (!path)
return;
files = eina_list_append(files, path);
ephoto_file_delete(db->ephoto, files, EINA_FILE_DIR);
}
static void
_fsel_mouse_up_cb(void *data, Evas *e EINA_UNUSED,
Evas_Object *obj EINA_UNUSED, void *event_info)
{
Ephoto_Directory_Browser *db = data;
Evas_Object *menu;
Elm_Object_Item *item;
Evas_Event_Mouse_Up *info = event_info;
Evas_Coord x, y;
evas_pointer_canvas_xy_get(evas_object_evas_get(db->fsel), &x, &y);
item = elm_genlist_at_xy_item_get(db->fsel, x, y, 0);
if (info->button == 1 && item)
{
if (info->flags == EVAS_BUTTON_DOUBLE_CLICK)
{
if (elm_genlist_item_type_get(item) == ELM_GENLIST_ITEM_TREE)
{
if (db->click_timer)
{
ecore_timer_del(db->click_timer);
db->click_timer = NULL;
elm_genlist_item_expanded_set(item,
!elm_genlist_item_expanded_get(item));
}
}
}
else
{
evas_object_data_del(db->fsel, "current_item");
evas_object_data_set(db->fsel, "current_item", item);
if (elm_genlist_item_type_get(item) == ELM_GENLIST_ITEM_TREE)
db->click_timer = ecore_timer_loop_add(.3, _click_timer_cb, db);
else
_on_list_selected(db, NULL, item);
}
}
if (info->button != 3)
return;
if (item)
elm_genlist_item_selected_set(item, EINA_TRUE);
menu = elm_menu_add(db->ephoto->win);
elm_menu_move(menu, x, y);
elm_menu_item_add(menu, NULL, "computer", _("Root"),
_fsel_menu_go_root, db->ephoto);
elm_menu_item_add(menu, NULL, "user-home", _("Home"),
_fsel_menu_go_home, db->ephoto);
if (strcmp(db->ephoto->config->directory, db->ephoto->trash_path))
{
elm_menu_item_add(menu, NULL, "folder-new", _("New Folder"),
_fsel_menu_new_dir_cb, db);
}
if (item)
{
elm_menu_item_add(menu, NULL, "edit", _("Rename"),
_fsel_menu_rename_cb, db);
elm_menu_item_add(menu, NULL, "edit-paste", _("Paste"),
_fsel_menu_paste_cb, db);
}
else if (!strcmp(db->ephoto->config->directory, db->ephoto->trash_path) &&
elm_genlist_first_item_get(db->fsel))
{
elm_menu_item_add(menu, NULL, "edit-delete", _("Empty Trash"),
_menu_empty_cb, db);
}
if (strcmp(db->ephoto->config->directory, db->ephoto->trash_path) && item)
{
elm_menu_item_add(menu, NULL, "edit-delete", _("Delete"),
_fsel_menu_delete_cb, db);
elm_menu_item_add(menu, NULL, "user-trash", _("Trash"),
_dir_go_trash, db);
}
evas_object_smart_callback_add(menu, "dismissed", _menu_dismissed_cb,
db);
evas_object_show(menu);
}
static void
_ephoto_directory_view_add(Ephoto_Directory_Browser *db)
{
db->leftbox = elm_box_add(db->main);
elm_box_horizontal_set(db->leftbox, EINA_FALSE);
elm_box_homogeneous_set(db->leftbox, EINA_FALSE);
EPHOTO_EXPAND(db->leftbox);
EPHOTO_FILL(db->leftbox);
elm_box_pack_end(db->main, db->leftbox);
evas_object_show(db->leftbox);
db->fsel = elm_genlist_add(db->leftbox);
elm_genlist_select_mode_set(db->fsel, ELM_OBJECT_SELECT_MODE_ALWAYS);
elm_genlist_highlight_mode_set(db->fsel, EINA_TRUE);
EPHOTO_EXPAND(db->fsel);
EPHOTO_FILL(db->fsel);
evas_object_size_hint_min_set(db->fsel, 195 * elm_config_scale_get(), 0);
evas_object_smart_callback_add(db->fsel, "expand,request",
_on_list_expand_req, db);
evas_object_smart_callback_add(db->fsel, "contract,request",
_on_list_contract_req, db);
evas_object_smart_callback_add(db->fsel, "expanded", _on_list_expanded, db);
evas_object_smart_callback_add(db->fsel, "contracted", _on_list_contracted,
db);
evas_object_event_callback_add(db->fsel, EVAS_CALLBACK_MOUSE_UP,
_fsel_mouse_up_cb, db);
evas_object_data_set(db->fsel, "directory_browser", db);
elm_box_pack_end(db->leftbox, db->fsel);
evas_object_show(db->fsel);
elm_drop_item_container_add(db->fsel, ELM_SEL_FORMAT_TARGETS,
_drop_item_getcb, _drop_enter, db, _drop_leave, db, _drop_pos, db,
_drop_dropcb, NULL);
}
/*Ephoto Populating Functions*/
static void
_todo_items_free(Ephoto_Directory_Browser *db)
{
if (eina_list_count(db->todo_items))
eina_list_free(db->todo_items);
db->todo_items = NULL;
}
static void
_monitor_add(Ephoto_Entry *e)
{
char *rp = ecore_file_realpath(e->path);
e->monitor = eio_monitor_add(rp);
e->monitor_handlers =
eina_list_append(e->monitor_handlers,
ecore_event_handler_add(EIO_MONITOR_FILE_CREATED,
_monitor_cb, e));
e->monitor_handlers =
eina_list_append(e->monitor_handlers,
ecore_event_handler_add(EIO_MONITOR_FILE_MODIFIED,
_monitor_cb, e));
e->monitor_handlers =
eina_list_append(e->monitor_handlers,
ecore_event_handler_add(EIO_MONITOR_FILE_DELETED,
_monitor_cb, e));
e->monitor_handlers =
eina_list_append(e->monitor_handlers,
ecore_event_handler_add(EIO_MONITOR_DIRECTORY_CREATED,
_monitor_cb, e));
e->monitor_handlers =
eina_list_append(e->monitor_handlers,
ecore_event_handler_add(EIO_MONITOR_DIRECTORY_MODIFIED,
_monitor_cb, e));
e->monitor_handlers =
eina_list_append(e->monitor_handlers,
ecore_event_handler_add(EIO_MONITOR_DIRECTORY_DELETED,
_monitor_cb, e));
free(rp);
}
static Eina_Bool
_monitor_cb(void *data, int type,
void *event)
{
Elm_Object_Item *item;
Ephoto_Entry *entry = data;
Ephoto_Entry *e;
Ecore_Event_Handler *handler;
Eio_Monitor_Event *ev = event;
char file[PATH_MAX], dir[PATH_MAX];
const Elm_Genlist_Item_Class *ic;
char buf[PATH_MAX];
char *freedir;
if (!entry)
return ECORE_CALLBACK_PASS_ON;