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.

2874 lines
84 KiB

#include "ephoto.h"
#define ZOOM_MAX 512
#define ZOOM_MIN 128
#define ZOOM_STEP 32
#define TODO_ITEM_MIN_BATCH 5
#define FILESEP "file://"
#define FILESEP_LEN sizeof(FILESEP) - 1
#define DRAG_TIMEOUT 0.3
#define ANIM_TIME 0.2
static Eina_Bool _5s_cancel = EINA_FALSE;
static Ecore_Timer *_5s_timeout = NULL;
typedef struct _Ephoto_Thumb_Browser Ephoto_Thumb_Browser;
struct _Ephoto_Thumb_Browser
{
Ephoto *ephoto;
Evas_Object *main;
Evas_Object *table;
Evas_Object *gridbox;
Evas_Object *grid;
Evas_Object *original_grid;
Evas_Object *nolabel;
Evas_Object *infolabel;
Evas_Object *fsel;
Evas_Object *leftbox;
Evas_Object *direntry;
Evas_Object *search;
Evas_Object *hover;
Elm_Object_Item *dir_current;
Elm_Object_Item *last_sel;
Ephoto_Sort sort;
Eio_File *ls;
Ecore_File_Monitor *monitor;
Eina_List *cut_items;
Eina_List *copy_items;
Eina_List *handlers;
Eina_List *todo_items;
Eina_List *entries;
Eina_List *searchentries;
Ecore_Job *change_dir_job;
Ecore_Timer *click_timer;
Eina_Bool thumbs_only;
Eina_Bool dirs_only;
double totsize;
double totsize_old;
int totimages;
int totimages_old;
Eina_Bool dragging;
Eina_Bool searching;
Eina_Bool processing;
struct
{
Ecore_Animator *todo_items;
int count;
int processed;
} animator;
Eina_Bool main_deleted:1;
};
static Elm_Gengrid_Item_Class _ephoto_thumb_file_class;
static Elm_Genlist_Item_Class _ephoto_dir_class;
static void _ephoto_dir_hide_folders(void *data, Evas_Object *obj,
void *event_info);
static void _ephoto_dir_show_folders(void *data, Evas_Object *obj,
void *event_info);
static Elm_Object_Item *_dnd_item_get(Evas_Object *obj, Evas_Coord x,
Evas_Coord y, int *xposret, int *yposret);
static Eina_Bool _dnd_item_data_get(Evas_Object *obj, Elm_Object_Item *it,
Elm_Drag_User_Info *info);
static void _grid_mouse_up_cb(void *data, Evas *e EINA_UNUSED,
Evas_Object *obj EINA_UNUSED, void *event_info);
static void _ephoto_thumb_activated(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info);
static void _zoom_set(Ephoto_Thumb_Browser *tb, int zoom);
static void _monitor_cb(void *data, Ecore_File_Monitor *em EINA_UNUSED,
Ecore_File_Event event, const char *path);
static void
_update_info_label(Ephoto_Thumb_Browser *tb)
{
char buf[PATH_MAX];
char isize[PATH_MAX];
char image_info[PATH_MAX];
double totsize;
if (!tb->totimages)
{
elm_object_text_set(tb->nolabel,
_("No images matched your search"));
snprintf(buf, PATH_MAX, "<b>%s:</b> 0 %s <b>%s:</b> 0%s",
_("Total"), ngettext("image", "images", 0), _("Size"),
ngettext("B", "B", 0));
elm_object_text_set(tb->infolabel, buf);
}
else
{
elm_object_text_set(tb->nolabel, " ");
totsize = tb->totsize;
if (totsize < 1024.0)
snprintf(isize, sizeof(isize), "%'.0f%s", totsize, ngettext("B",
"B", totsize));
else
{
totsize /= 1024.0;
if (totsize < 1024)
snprintf(isize, sizeof(isize), "%'.0f%s", totsize,
ngettext("KB", "KB", totsize));
else
{
totsize /= 1024.0;
if (totsize < 1024)
snprintf(isize, sizeof(isize), "%'.1f%s", totsize,
ngettext("MB", "MB", totsize));
else
{
totsize /= 1024.0;
if (totsize < 1024)
snprintf(isize, sizeof(isize), "%'.1f%s", totsize,
ngettext("GB", "GB", totsize));
else
{
totsize /= 1024.0;
snprintf(isize, sizeof(isize), "%'.1f%s",
totsize, ngettext("TB", "TB", totsize));
}
}
}
}
snprintf(image_info, PATH_MAX, "<b>%s:</b> %d %s <b>%s:</b> %s",
_("Total"), tb->totimages, ngettext("image", "images",
tb->totimages), _("Size"), isize);
elm_object_text_set(tb->infolabel, image_info);
}
}
static void
_todo_items_free(Ephoto_Thumb_Browser *tb)
{
eina_list_free(tb->todo_items);
tb->todo_items = NULL;
}
static void
_on_list_expand_req(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Ephoto_Thumb_Browser *tb = data;
Elm_Object_Item *it = event_info;
ecore_job_del(tb->change_dir_job);
tb->change_dir_job = NULL;
ecore_timer_del(tb->click_timer);
tb->click_timer = NULL;
elm_genlist_item_expanded_set(it, EINA_TRUE);
}
static void
_on_list_contract_req(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Ephoto_Thumb_Browser *tb = data;
Elm_Object_Item *it = event_info;
ecore_job_del(tb->change_dir_job);
tb->change_dir_job = NULL;
ecore_timer_del(tb->click_timer);
tb->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_Thumb_Browser *tb = data;
Elm_Object_Item *it = event_info;
Ephoto_Entry *entry;
const char *path;
entry = elm_object_item_data_get(it);
path = entry->path;
tb->dirs_only = 0;
if (!strcmp(path, tb->ephoto->config->directory))
tb->dirs_only = 1;
else
tb->dirs_only = 0;
tb->thumbs_only = 0;
ephoto_directory_set(tb->ephoto, path, it, tb->dirs_only, tb->thumbs_only);
ephoto_title_set(tb->ephoto, tb->ephoto->config->directory);
}
static void
_on_list_contracted(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
Ephoto_Thumb_Browser *tb = data;
Elm_Object_Item *it = event_info;
Ephoto_Entry *entry;
const char *path;
entry = elm_object_item_data_get(it);
path = entry->path;
elm_genlist_item_subitems_clear(it);
if (!strcmp(path, tb->ephoto->config->directory))
return;
tb->thumbs_only = 1;
tb->dirs_only = 0;
ephoto_directory_set(tb->ephoto, path, NULL,
tb->dirs_only, tb->thumbs_only);
ephoto_title_set(tb->ephoto,
tb->ephoto->config->directory);
}
static void
_dir_job(void *data)
{
Elm_Object_Item *it = data;
Ephoto_Thumb_Browser *tb = evas_object_data_get(it, "thumb_browser");
Ephoto_Entry *entry;
const char *path;
entry = elm_object_item_data_get(it);
path = entry->path;
tb->change_dir_job = NULL;
tb->thumbs_only = 1;
tb->dirs_only = 0;
ephoto_directory_set(tb->ephoto, path, NULL,
tb->dirs_only, tb->thumbs_only);
ephoto_title_set(tb->ephoto, tb->ephoto->config->directory);
}
static void
_wait_job(void *data)
{
Elm_Object_Item *it = data;
Ephoto_Thumb_Browser *tb = evas_object_data_get(it, "thumb_browser");
if (tb->change_dir_job)
ecore_job_del(tb->change_dir_job);
tb->change_dir_job = ecore_job_add(_dir_job, it);
}
static void
_on_list_selected(void *data, Evas_Object *obj EINA_UNUSED,
void *event_info)
{
Ephoto_Thumb_Browser *tb = data;
Elm_Object_Item *it = event_info;
evas_object_data_set(it, "thumb_browser", tb);
if (!tb->dragging)
{
tb->dir_current = it;
ecore_job_add(_wait_job, it);
}
}
static char *
_ephoto_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 char *
_ephoto_thumb_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 *
_ephoto_dir_item_icon_get(void *data EINA_UNUSED, Evas_Object *obj,
const char *part)
{
if (!strcmp(part, "elm.swallow.end"))
return NULL;
Evas_Object *ic = elm_icon_add(obj);
elm_icon_order_lookup_set(ic, ELM_ICON_LOOKUP_FDO_THEME);
elm_icon_standard_set(ic, "folder");
return ic;
}
static Evas_Object *
_ephoto_thumb_file_icon_get(void *data, Evas_Object *obj,
const char *part)
{
Ephoto_Entry *e = data;
Evas_Object *thumb = NULL;
if (strcmp(part, "elm.swallow.icon"))
return NULL;
if (e)
{
thumb = ephoto_thumb_add(e->ephoto, obj, e->path);
evas_object_show(thumb);
}
return thumb;
}
static void
_ephoto_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 void
_ephoto_thumb_item_del(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED)
{
/* The entry is already freed when changing directories. */
}
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 int
_entry_cmp_grid_alpha_asc(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 int
_entry_cmp_grid_alpha_desc(const void *pa, const void *pb)
{
const Ephoto_Entry *a, *b;
int i;
a = elm_object_item_data_get(pa);
b = elm_object_item_data_get(pb);
i = strcasecmp(a->basename, b->basename);
if (i < 0)
i = 1;
else if (i > 0)
i = -1;
return i;
}
static int
_entry_cmp_grid_mod_asc(const void *pa, const void *pb)
{
const Ephoto_Entry *a, *b;
long long moda, modb;
a = elm_object_item_data_get(pa);
b = elm_object_item_data_get(pb);
moda = ecore_file_mod_time(a->path);
modb = ecore_file_mod_time(b->path);
if (moda < modb)
return -1;
else if (moda > modb)
return 1;
else
return strcasecmp(a->basename, b->basename);
}
static int
_entry_cmp_grid_mod_desc(const void *pa, const void *pb)
{
const Ephoto_Entry *a, *b;
long long moda, modb;
a = elm_object_item_data_get(pa);
b = elm_object_item_data_get(pb);
moda = ecore_file_mod_time(a->path);
modb = ecore_file_mod_time(b->path);
if (moda < modb)
return 1;
else if (moda > modb)
return -1;
else
{
int i;
i = strcasecmp(a->basename, b->basename);
if (i < 0)
i = 1;
else if (i > 0)
i = -1;
return i;
}
}
static void
_sort_alpha_asc(void *data, Evas_Object *obj EINA_UNUSED,
void *event_data EINA_UNUSED)
{
Ephoto_Thumb_Browser *tb = data;
Evas_Object *ic;
tb->sort = EPHOTO_SORT_ALPHABETICAL_ASCENDING;
tb->thumbs_only = 1;
tb->dirs_only = 0;
ic = elm_icon_add(tb->hover);
elm_icon_standard_set(ic, "view-sort-ascending");
elm_object_part_content_set(tb->hover, "icon", ic);
evas_object_show(ic);
ephoto_directory_set(tb->ephoto, tb->ephoto->config->directory,
NULL, tb->dirs_only, tb->thumbs_only);
}
static void
_sort_alpha_desc(void *data, Evas_Object *obj EINA_UNUSED,
void *event_data EINA_UNUSED)
{
Ephoto_Thumb_Browser *tb = data;
Evas_Object *ic;
tb->sort = EPHOTO_SORT_ALPHABETICAL_DESCENDING;
tb->thumbs_only = 1;
tb->dirs_only = 0;
ic = elm_icon_add(tb->hover);
elm_icon_standard_set(ic, "view-sort-descending");
elm_object_part_content_set(tb->hover, "icon", ic);
evas_object_show(ic);
ephoto_directory_set(tb->ephoto, tb->ephoto->config->directory,
NULL, tb->dirs_only, tb->thumbs_only);
}
static void
_sort_mod_asc(void *data, Evas_Object *obj EINA_UNUSED,
void *event_data EINA_UNUSED)
{
Ephoto_Thumb_Browser *tb = data;
Evas_Object *ic;
tb->sort = EPHOTO_SORT_MODTIME_ASCENDING;
tb->thumbs_only = 1;
tb->dirs_only = 0;
ic = elm_icon_add(tb->hover);
elm_icon_standard_set(ic, "view-sort-ascending");
elm_object_part_content_set(tb->hover, "icon", ic);
evas_object_show(ic);
ephoto_directory_set(tb->ephoto, tb->ephoto->config->directory,
NULL, tb->dirs_only, tb->thumbs_only);
}
static void
_sort_mod_desc(void *data, Evas_Object *obj EINA_UNUSED,
void *event_data EINA_UNUSED)
{
Ephoto_Thumb_Browser *tb = data;
Evas_Object *ic;
tb->sort = EPHOTO_SORT_MODTIME_DESCENDING;
tb->thumbs_only = 1;
tb->dirs_only = 0;
ic = elm_icon_add(tb->hover);
elm_icon_standard_set(ic, "view-sort-descending");
elm_object_part_content_set(tb->hover, "icon", ic);
evas_object_show(ic);
ephoto_directory_set(tb->ephoto, tb->ephoto->config->directory,
NULL, tb->dirs_only, tb->thumbs_only);
}
static Eina_Bool
_check_for_subdirs(Ephoto_Entry *entry)
{
Eina_Iterator *ls = eina_file_direct_ls(entry->path);
Eina_File_Direct_Info *info;
if (!ls)
return EINA_FALSE;
EINA_ITERATOR_FOREACH(ls, info)
{
if (info->type == EINA_FILE_DIR)
{
eina_iterator_free(ls);
return EINA_TRUE;
}
}
eina_iterator_free(ls);
return EINA_FALSE;
}
static void
_monitor_cb(void *data, Ecore_File_Monitor *em EINA_UNUSED,
Ecore_File_Event event, const char *path)
{
Elm_Object_Item *item;
Ephoto_Entry *entry = data;
Ephoto_Entry *e;
char file[PATH_MAX], dir[PATH_MAX];
const Elm_Genlist_Item_Class *ic;
char buf[PATH_MAX];
if (!entry)
return;
snprintf(file, PATH_MAX, "%s", path);
snprintf(dir, PATH_MAX, "%s", ecore_file_dir_get(file));
if (strcmp(entry->path, dir))
return;
if (event == ECORE_FILE_EVENT_CREATED_DIRECTORY)
{
if (!ecore_file_is_dir(path))
return;
if (ephoto_entry_exists(entry->ephoto, path))
return;
if (elm_genlist_item_type_get(entry->item) == ELM_GENLIST_ITEM_TREE &&
elm_genlist_item_expanded_get(entry->item) == EINA_TRUE)
{
ic = &_ephoto_dir_class;
snprintf(buf, PATH_MAX, "%s", path);
e = ephoto_entry_new(entry->ephoto, path, basename(buf),
EINA_FILE_DIR);
e->genlist = entry->genlist;
e->parent = entry->item;
e->item =
elm_genlist_item_sorted_insert(entry->genlist, ic, e,
e->parent, ELM_GENLIST_ITEM_NONE, _entry_cmp, NULL, NULL);
if (e->item)
e->monitor = ecore_file_monitor_add(e->path, _monitor_cb, e);
}
if (elm_genlist_item_type_get(entry->item) == ELM_GENLIST_ITEM_NONE)
{
Elm_Object_Item *parent;
ic = &_ephoto_dir_class;
parent =
elm_genlist_item_insert_before(entry->genlist, ic, entry,
entry->parent, entry->item, ELM_GENLIST_ITEM_TREE, NULL, NULL);
entry->no_delete = EINA_TRUE;
if (entry->monitor)
ecore_file_monitor_del(entry->monitor);
elm_object_item_del(entry->item);
entry->item = parent;
entry->no_delete = EINA_FALSE;
entry->monitor = ecore_file_monitor_add(entry->path, _monitor_cb, entry);
}
return;
}
else if (event == ECORE_FILE_EVENT_DELETED_DIRECTORY)
{
item = elm_genlist_first_item_get(entry->genlist);
while (item)
{
e = elm_object_item_data_get(item);
if (!strcmp(e->path, path))
{
elm_object_item_del(e->item);
//if (!strcmp(e->path, e->ephoto->config->directory))
break;
}
item = elm_genlist_item_next_get(item);
}
if (elm_genlist_item_type_get(entry->item) == ELM_GENLIST_ITEM_TREE &&
_check_for_subdirs(entry) == EINA_FALSE)
{
Elm_Object_Item *parent;
ic = &_ephoto_dir_class;
parent =
elm_genlist_item_insert_before(entry->genlist, ic, entry,
entry->parent, entry->item, ELM_GENLIST_ITEM_NONE, NULL, NULL);
entry->no_delete = EINA_TRUE;
elm_object_item_del(entry->item);
entry->item = parent;
entry->no_delete = EINA_FALSE;
}
if (!ecore_file_exists(entry->ephoto->config->directory))
{
ephoto_directory_set(entry->ephoto, entry->path, entry->parent, 0, 1);
ephoto_title_set(entry->ephoto, entry->path);
}
return;
}
else if (event == ECORE_FILE_EVENT_MODIFIED)
{
if (!ecore_file_is_dir(path))
return;
if ((elm_genlist_item_expanded_get(entry->item) == EINA_TRUE))
{
item = elm_genlist_first_item_get(entry->genlist);
while (item)
{
e = elm_object_item_data_get(item);
if (!strcmp(e->path, path))
{
elm_genlist_item_update(e->item);
break;
}
item = elm_genlist_item_next_get(item);
}
}
return;
}
}
static Eina_Bool
_todo_items_process(void *data)
{
Ephoto_Thumb_Browser *tb = data;
Ephoto_Entry *entry;
int i = 0;
if ((!tb->ls) && (tb->animator.processed == tb->animator.count))
{
if (tb->animator.count == 0)
return EINA_TRUE;
tb->animator.todo_items = NULL;
tb->processing = 0;
return EINA_FALSE;
}
if ((tb->ls) && (eina_list_count(tb->todo_items) < TODO_ITEM_MIN_BATCH))
return EINA_TRUE;
tb->animator.todo_items = NULL;
tb->processing = 1;
EINA_LIST_FREE(tb->todo_items, entry)
{
i++;
if (i > TODO_ITEM_MIN_BATCH)
return EINA_TRUE;
if (entry->is_dir && !entry->item)
{
const Elm_Genlist_Item_Class *ic;
ic = &_ephoto_dir_class;
if (_check_for_subdirs(entry))
entry->item =
elm_genlist_item_sorted_insert(tb->fsel, ic, entry,
entry->parent, ELM_GENLIST_ITEM_TREE, _entry_cmp, NULL, NULL);
else
entry->item =
elm_genlist_item_sorted_insert(tb->fsel, ic, entry,
entry->parent, ELM_GENLIST_ITEM_NONE, _entry_cmp, NULL, NULL);
if (!entry->item)