#include #include "main.h" #include "win.h" #include "winvid.h" #include "browser.h" #include "videothumb.h" #include "key.h" #include "dnd.h" #include "util.h" typedef struct _Message Message; typedef struct _Entry Entry; typedef enum _Type { TYPE_NEW, TYPE_UPDATE, TYPE_FINISH } Type; struct _Message { Type type; Entry *entry; }; struct _Entry { Eina_Lock lock; Entry *parent; // what is above Eina_Stringshare *path; // full path Eina_List *dirs; // entries fo subdir entires Eina_List *files; // strings of just filenames in path dir Eina_List *sels; Evas_Object *base; Evas_Object *box; Evas_Object *table; Evas_Object *sizer; Evas_Coord iw, ih; Evas_Object **file_obj; int cols, rows; int sel_x, sel_y; Eina_Bool sel : 1; }; typedef struct { char *videos; Evas_Object *win; } Fill_Data; #define CREATED_MAX 32 #define DESTROYED_MAX 32 #define NORMAL_COLS 12 #define NORMAL_ROWS 4 #define FULLSCREEN_COLS 6 #define FULLSCREEN_ROWS 3 // these gets scaled by scaling factor anyway... #define ITEM_MIN_W 100 #define ITEM_MIN_H 100 #define ITEM_MAX_W 140 #define ITEM_MAX_H 140 #define ITEM_ASPECT_MIN 50 #define ITEM_ASPECT_MAX 150 static char *selfile = NULL; static Entry *selentry = NULL; static int seli = 0; static Evas_Object *bx = NULL; static Evas_Object *sc, *bt; static Ecore_Thread *fill_thread = NULL; static Entry *dir_entry = NULL; static Eina_List *entries = NULL; static Ecore_Timer *_browser_hide_focus_restore_timer = NULL; static Eina_Semaphore step_sema; static Ecore_Timer *initial_update_timer = NULL; static Ecore_Animator *pop_eval_redo = NULL; static void _pop_eval_delay(Evas_Object *win, int created, int destroyed); static void _entry_files_pop_eval(Evas_Object *win, Entry *entry); static void _sel_go(Evas_Object *win EINA_UNUSED, Entry *base_entry, int x, int y); static void _item_size_get(Evas_Object *win, Evas_Coord *w, Evas_Coord *h) { Evas_Coord sz = 0; Evas_Coord minw = ITEM_MIN_W, minh = ITEM_MIN_H; Evas_Coord maxw = ITEM_MAX_W, maxh = ITEM_MAX_H; minw = elm_config_scale_get() * (double)minw; minh = elm_config_scale_get() * (double)minh; maxw = elm_config_scale_get() * (double)maxw; maxh = elm_config_scale_get() * (double)maxh; elm_coords_finger_size_adjust(1, &sz, 1, &sz); evas_object_geometry_get(win, NULL, NULL, w, h); if (elm_win_fullscreen_get(win)) { *w = (double)(*w) / (double)FULLSCREEN_COLS; *h = (double)(*h) / (double)FULLSCREEN_ROWS; maxw = 0; maxh = 0; } else { *w = (double)(*w) / (double)NORMAL_COLS; *h = (double)(*h) / (double)NORMAL_ROWS; } if (*w < minw) *w = minw; if (*h < minh) *h = minh; if ((maxw > 0) && (*w > maxw)) *w = maxw; if ((maxh > 0) && (*h > maxh)) *h = maxh; if (((*w * 100) / *h) < ITEM_ASPECT_MIN) *w = (*h * ITEM_ASPECT_MIN) / 100; else if (((*w * 100) / *h) > ITEM_ASPECT_MAX) *w = (*h * ITEM_ASPECT_MAX) / 100; if (*w < minw) *w = minw; if (*h < minh) *h = minh; if (*w < sz) *w = sz; if (*h < sz) *h = sz; } static void _fill_message(Ecore_Thread *th, Type type, Entry *entry) { Message *message = calloc(1, sizeof(Message)); if (!message) return; message->type = type; message->entry = entry; ecore_thread_feedback(th, message); // take semaphore lock after sending message so we wait if the mainloop // had not processed the previous message eina_semaphore_lock(&step_sema); } static Entry * _fill_scan(Ecore_Thread *th, Entry *parent, const char *dir) { Eina_List *files; char *file; Entry *entry = NULL; entry = calloc(1, sizeof(Entry)); if (!entry) return NULL; files = ecore_file_ls(dir); if (!files) { if (entry == selentry) selentry = NULL; free(entry); return NULL; } eina_lock_new(&(entry->lock)); entry->parent = parent; entry->path = eina_stringshare_add(dir); if (parent) { eina_lock_take(&(parent->lock)); parent->dirs = eina_list_append(parent->dirs, entry); eina_lock_release(&(parent->lock)); } _fill_message(th, TYPE_NEW, entry); EINA_LIST_FREE(files, file) { if (!ecore_thread_check(th)) { if (file[0] != '.') { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/%s", dir, file); if (ecore_file_is_dir(buf)) { _fill_scan(th, entry, buf); _fill_message(th, TYPE_UPDATE, entry); } else { if (util_video_ok(file) || util_audio_ok(file)) { eina_lock_take(&(entry->lock)); entry->files = eina_list_append (entry->files, eina_stringshare_add(file)); eina_lock_release(&(entry->lock)); _fill_message(th, TYPE_UPDATE, entry); } } } } free(file); } _fill_message(th, TYPE_FINISH, entry); return entry; } static void _fill_thread(void *data, Ecore_Thread *th) { Fill_Data *fdat = data; _fill_scan(th, NULL, fdat->videos); } static void _cb_vidthumb_data(void *data EINA_UNUSED, Evas_Object *obj, void *event EINA_UNUSED) { int w, h; videothumb_size_get(obj, &w, &h); evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_NEITHER, w, h); } static void _activate(Evas_Object *win, Entry *entry, const char *file) { Eina_List *list = NULL; Winvid_Entry *vid; char buf[PATH_MAX]; vid = calloc(1, sizeof(Winvid_Entry)); if (vid) { snprintf(buf, sizeof(buf), "%s/%s", entry->path, file); vid->file = eina_stringshare_add(buf); list = eina_list_append(list, vid); } win_video_file_list_set(win, list); EINA_LIST_FREE(list, vid) { if (vid->file) eina_stringshare_del(vid->file); if (vid->sub) eina_stringshare_del(vid->sub); if (vid->uri) efreet_uri_free(vid->uri); free(vid); } browser_hide(win); } static void _cb_file_selected(void *data, Evas_Object *obj, const char *sig EINA_UNUSED, const char *src EINA_UNUSED) { Evas_Object *win = data; Entry *entry = evas_object_data_get(obj, "entry"); char buf[PATH_MAX]; const char *file = evas_object_data_get(obj, "file"); elm_layout_signal_emit(obj, "rage,state,selected", "rage"); evas_object_raise(obj); _activate(win, entry, file); snprintf(buf, sizeof(buf), "%s/%s", entry->path, file); if (selfile) free(selfile); selfile = strdup(buf); } static void _entry_files_pop_clear(Entry *entry) { Evas_Object *win = evas_object_data_get(entry->table, "win"); int i, k, destroyed = 0; // if we had any content at all before - nuke it all if ((entry->sels) && (entry->file_obj)) { k = entry->cols * entry->rows; for (i = 0; i < k; i++) { if (entry->file_obj[i]) { Evas_Object *o = entry->file_obj[i]; entry->file_obj[i] = NULL; entry->sels = eina_list_remove(entry->sels, o); destroyed++; evas_object_del(o); } } _pop_eval_delay(win, 0, destroyed); } } static Eina_Bool _cb_pop_eval_redo(void *data) { Evas_Object *win = data; Inf *inf = evas_object_data_get(win, "inf"); Eina_List *l; Entry *entry; pop_eval_redo = NULL; if (!inf) return EINA_FALSE; if (!bx) return EINA_FALSE; EINA_LIST_FOREACH(entries, l, entry) { if (pop_eval_redo) break; _entry_files_pop_eval(win, entry); } return EINA_FALSE; } static void _pop_eval_delay(Evas_Object *win, int created, int destroyed) { if ((created >= CREATED_MAX) || (destroyed >= DESTROYED_MAX)) { if (pop_eval_redo) { ecore_animator_del(pop_eval_redo); pop_eval_redo = NULL; } pop_eval_redo = ecore_animator_add(_cb_pop_eval_redo, win); } } static void _entry_files_pop_eval(Evas_Object *win, Entry *entry) { const char *file; Eina_List *l; Evas_Object **obj; Evas_Coord win_w, win_h, ent_x, ent_y, ent_w, ent_h; int i = 0, j = 0, created = 0, destroyed = 0; Eina_Rectangle win_rect, file_rect; if (pop_eval_redo) return; evas_object_geometry_get(entry->table, &ent_x, &ent_y, &ent_w, &ent_h); evas_object_geometry_get(win, NULL, NULL, &win_w, &win_h); win_rect.x = 0; win_rect.y = 0; win_rect.w = win_w; win_rect.h = win_h; // if we're not in the viewport at all empty all content file_rect.x = ent_x; file_rect.y = ent_y; file_rect.w = ent_w; file_rect.h = ent_h; file_rect.x -= 80; file_rect.y -= 80; file_rect.w += 160; file_rect.h += 160; if (!eina_rectangles_intersect(&win_rect, &file_rect)) { _entry_files_pop_clear(entry); return; } // walk files to find which intersect the window EINA_LIST_FOREACH(entry->files, l, file) { if ((created >= CREATED_MAX) && (destroyed >= DESTROYED_MAX)) break; file_rect.x = ent_x + ((i * ent_w) / entry->cols); file_rect.y = ent_y + ((j * ent_h) / entry->rows); file_rect.w = (ent_w / entry->cols); file_rect.h = (ent_h / entry->rows); file_rect.x -= 80; file_rect.y -= 80; file_rect.w += 160; file_rect.h += 160; obj = &(entry->file_obj[(j * entry->cols) + i]); if (eina_rectangles_intersect(&win_rect, &file_rect)) { if ((!(*obj)) && (created < CREATED_MAX)) { Evas_Object *o, *base; char buf[PATH_MAX], *p; base = o = elm_layout_add(win); *obj = o; entry->sels = eina_list_append(entry->sels, o); evas_object_data_set(o, "entry", entry); evas_object_data_set(o, "file", file); elm_object_focus_allow_set(o, EINA_FALSE); snprintf(buf, sizeof(buf), "%s/themes/default.edj", elm_app_data_dir_get()); elm_layout_file_set(o, buf, "rage/browser/item"); if (elm_win_fullscreen_get(win)) elm_layout_signal_emit(base, "state,fullscreen", "rage"); else elm_layout_signal_emit(base, "state,normal", "rage"); snprintf(buf, sizeof(buf), "%s", file); for (p = buf; *p; p++) { // nuke stupid characters from the label that may be // in filename if ((*p == '_') || (*p == '#') || (*p == '$') || (*p == '%') || (*p == '*') || (*p == '+') || (*p == '[') || (*p == ']') || (*p == ';') || (*p == '<') || (*p == '=') || (*p == '>') || (*p == '^') || (*p == '`') || (*p == '{') || (*p == '}') || (*p == '|') || (*p == '~') || (*p == 127) || (*p == '\'') || (*p == '\\')) { *p = ' '; } else if (*p == '.') { *p = 0; break; } } elm_object_part_text_set(o, "rage.title", buf); evas_object_size_hint_weight_set(o, 0.0, 0.0); evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_table_pack(entry->table, o, i, j, 1, 1); evas_object_show(o); elm_layout_signal_callback_add(o, "rage,selected", "rage", _cb_file_selected, win); o = videothumb_add(win); videothumb_poster_mode_set(o, EINA_TRUE); evas_object_smart_callback_add(o, "data", _cb_vidthumb_data, base); evas_object_data_set(o, "entry", entry); evas_object_data_set(o, "file", file); snprintf(buf, sizeof(buf), "%s/%s", entry->path, file); videothumb_file_set(o, buf, 0.0); videothumb_autocycle_set(o, EINA_TRUE); elm_object_part_content_set(base, "rage.content", o); evas_object_show(o); if ((entry->sel) && (entry->sel_x == i) && (entry->sel_y == j)) { elm_layout_signal_emit(base, "rage,state,selected", "rage"); evas_object_raise(base); } created++; } } else { if ((*obj) && (destroyed < DESTROYED_MAX)) { entry->sels = eina_list_remove(entry->sels, *obj); evas_object_del(*obj); *obj = NULL; destroyed++; } } i++; if (i == entry->cols) { i = 0; j++; } } _pop_eval_delay(win, created, destroyed); } static void _entry_update(Evas_Object *win, Entry *entry) { eina_lock_take(&(entry->lock)); _entry_files_pop_eval(win, entry); eina_lock_release(&(entry->lock)); } static void _cb_sel_job(void *data) { Evas_Object *win = data; Entry *entry = selentry; if ((!dir_entry) || (!entry)) return; eina_lock_take(&(entry->lock)); entry->sel = EINA_TRUE; if (entry->cols > 0) entry->sel_y = seli / entry->cols; entry->sel_x = seli - (entry->sel_y * entry->cols); eina_lock_release(&(entry->lock)); _sel_go(win, dir_entry, 0, 0); } static void _entry_files_redo(Evas_Object *win, Entry *entry) { Evas_Coord x, y,w, h, iw = 1, ih = 1, ww, wh, sw, sh; int num, cols, rows; eina_lock_take(&(entry->lock)); if (elm_win_fullscreen_get(win)) elm_layout_signal_emit(entry->base, "state,fullscreen", "rage"); else elm_layout_signal_emit(entry->base, "state,normal", "rage"); num = eina_list_count(entry->files); evas_object_geometry_get(win, NULL, NULL, &ww, &wh); evas_object_geometry_get(entry->table, &x, &y, &w, &h); elm_scroller_region_get(sc, NULL, NULL, &sw, &sh); if (sw < w) w = sw; _entry_files_pop_clear(entry); free(entry->file_obj); entry->file_obj = NULL; _item_size_get(win, &iw, &ih); cols = w / iw; if (cols < 1) cols = 1; rows = (num + (cols - 1)) / cols; entry->iw = iw - 1; entry->ih = ih - 1; entry->cols = cols; entry->rows = rows; entry->file_obj = calloc(entry->cols * entry->rows, sizeof(Evas_Object *)); if ((entry->cols > 0) && (entry->rows > 0)) elm_table_pack(entry->table, entry->sizer, 0, 0, entry->cols, entry->rows); else elm_table_pack(entry->table, entry->sizer, 0, 0, 1, 1); evas_object_size_hint_min_set(entry->sizer, entry->cols * entry->iw, entry->rows * entry->ih); _entry_files_pop_eval(win, entry); if (selfile) { Eina_List *l; const char *file; char buf[PATH_MAX]; int i; i = 0; EINA_LIST_FOREACH(entry->files, l, file) { snprintf(buf, sizeof(buf), "%s/%s", entry->path, file); if (!strcmp(buf, selfile)) { selentry = entry; seli = i; ecore_job_add(_cb_sel_job, win); break; } i++; } } eina_lock_release(&(entry->lock)); } static void _cb_entry_table_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *info EINA_UNUSED) { Entry *entry = data; Evas_Object *win = evas_object_data_get(obj, "win"); if (initial_update_timer) return; _entry_update(win, entry); } static void _cb_entry_table_resize(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED) { // Entry *entry = data; // Evas_Object *win = evas_object_data_get(obj, "win"); // if (initial_update_timer) return; // _entry_files_redo(win, entry); } static void _cb_scroller_resize(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED) { // Inf *inf = evas_object_data_get(data, "inf"); // Eina_List *l; // Entry *entry; // if ((!inf) || (!bx)) return; // if (initial_update_timer) return; // printf("_cb_scroller_resize .... \n"); // EINA_LIST_FOREACH(entries, l, entry) // { // _entry_files_redo(data, entry); // } } static Eina_Bool _cb_initial_update_timer(void *data) { Inf *inf = evas_object_data_get(data, "inf"); Eina_List *l; Entry *entry; initial_update_timer = NULL; if ((!inf) || (!bx)) return EINA_FALSE; EINA_LIST_FOREACH(entries, l, entry) { _entry_files_redo(data, entry); } return EINA_FALSE; } static void _fill_feedback(void *data, Ecore_Thread *th, void *msg) { Fill_Data *fdat = data; Evas_Object *win = fdat->win; Message *message = msg; Evas_Object *o; char buf[PATH_MAX]; if ((th == fill_thread) && (bx)) { Entry *entry = message->entry; if ((message->type == TYPE_NEW) && (!dir_entry)) dir_entry = entry; if (message->type == TYPE_NEW) { eina_lock_take(&(entry->lock)); // if ((entry->dirs) || (entry->files)) { if (!entry->base) { const char *file; entry->base = o = elm_layout_add(win); elm_object_focus_allow_set(o, EINA_FALSE); snprintf(buf, sizeof(buf), "%s/themes/default.edj", elm_app_data_dir_get()); elm_layout_file_set(o, buf, "rage/browser/entry"); if (elm_win_fullscreen_get(win)) elm_layout_signal_emit(entry->base, "state,fullscreen", "rage"); else elm_layout_signal_emit(entry->base, "state,normal", "rage"); evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5); if (entry->parent) { file = entry->path + strlen(dir_entry->path) + 1; elm_object_part_text_set(o, "rage.title", file); } entry->box = o = elm_box_add(win); evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_object_part_content_set(entry->base, "rage.content", o); evas_object_show(o); entry->table = o = elm_table_add(win); elm_table_homogeneous_set(o, EINA_TRUE); evas_object_data_set(o, "win", win); evas_object_event_callback_add(o, EVAS_CALLBACK_MOVE, _cb_entry_table_move, entry); evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE, _cb_entry_table_resize, entry); evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5); elm_box_pack_end(entry->box, o); evas_object_show(o); entry->sizer = o = evas_object_rectangle_add(evas_object_evas_get(win)); evas_object_color_set(o, 0, 0, 0, 0); evas_object_size_hint_min_set(o, 10, 10); elm_table_pack(entry->table, o, 0, 0, 1, 1); if ((!entry->parent) || ((entry->parent) && (!entry->parent->parent)) || (!entry->parent->box)) elm_box_pack_end(bx, entry->base); else elm_box_pack_end(entry->parent->box, entry->base); evas_object_show(entry->base); } } entries = eina_list_append(entries, entry); eina_lock_release(&(entry->lock)); } else if ((message->type == TYPE_FINISH) && (entry->parent)) { if (!initial_update_timer) _entry_files_redo(win, entry); else { int iw, ih, cols, rows, num; Evas_Coord w, h; eina_lock_take(&(entry->lock)); num = eina_list_count(entry->files); evas_object_geometry_get(entry->table, NULL, NULL, &w, &h); _item_size_get(win, &iw, &ih); cols = w / iw; if (cols < 1) cols = 1; rows = (num + (cols - 1)) / cols; entry->iw = iw - 1; entry->ih = ih - 1; entry->cols = cols; entry->rows = rows; evas_object_size_hint_min_set(entry->sizer, entry->cols * entry->iw, entry->rows * entry->ih); if ((entry->cols > 0) && (entry->rows > 0)) elm_table_pack(entry->table, entry->sizer, 0, 0, entry->cols, entry->rows); else elm_table_pack(entry->table, entry->sizer, 0, 0, 1, 1); eina_lock_release(&(entry->lock)); } } } // allow the freedback thread to step more eina_semaphore_release(&step_sema, 1); free(msg); } static void _fill_end(void *data, Ecore_Thread *th) { Fill_Data *fdat = data; if (th == fill_thread) fill_thread = NULL; eina_semaphore_free(&step_sema); free(fdat->videos); free(fdat); } static void _fill_cancel(void *data EINA_UNUSED, Ecore_Thread *th) { Fill_Data *fdat = data; if (th == fill_thread) fill_thread = NULL; free(fdat->videos); free(fdat); } static void _entry_free(Entry *entry) { Entry *subentry; Eina_Stringshare *str; if (!entry) return; eina_lock_take(&(entry->lock)); entry->sels = eina_list_free(entry->sels); free(entry->file_obj); EINA_LIST_FREE(entry->files, str) eina_stringshare_del(str); EINA_LIST_FREE(entry->dirs, subentry) _entry_free(subentry); if (entry->base) evas_object_del(entry->base); eina_stringshare_del(entry->path); if (entry == selentry) selentry = NULL; entries = eina_list_remove(entries, entry); eina_lock_release(&(entry->lock)); eina_lock_free(&(entry->lock)); free(entry); } static void _fill(Evas_Object *win) { Fill_Data *fdat; if (fill_thread) { ecore_thread_cancel(fill_thread); ecore_thread_wait(fill_thread, 10.0); } _entry_free(dir_entry); dir_entry = NULL; fdat = malloc(sizeof(Fill_Data)); if (!fdat) return; fdat->videos = util_videos_dir_get(); fdat->win = win; eina_semaphore_new(&step_sema, 0); fill_thread = ecore_thread_feedback_run(_fill_thread, _fill_feedback, _fill_end, _fill_cancel, fdat, EINA_TRUE); } static Entry * _sel_find(Entry *entry) { Eina_List *l; Entry *subentry, *tmpentry; eina_lock_take(&(entry->lock)); if (entry->sel) { eina_lock_release(&(entry->lock)); return entry; } EINA_LIST_FOREACH(entry->dirs, l, subentry) { tmpentry = _sel_find(subentry); if (tmpentry) { eina_lock_release(&(entry->lock)); return tmpentry; } } eina_lock_release(&(entry->lock)); return NULL; } static Evas_Object * _sel_object_find(Entry *entry) { int num = (entry->sel_y * entry->cols) + entry->sel_x; if (!entry->file_obj) return NULL; Evas_Object *o = entry->file_obj[num]; return o; } static const char * _sel_file_find(Entry *entry) { int num = (entry->sel_y * entry->cols) + entry->sel_x; const char *file = eina_list_nth(entry->files, num); return file; } static Eina_List * _entry_list_flatten(Eina_List *list, Entry *entry) { Eina_List *l; Entry *subentry; eina_lock_take(&(entry->lock)); if (entry->files) list = eina_list_append(list, entry); EINA_LIST_FOREACH(entry->dirs, l, subentry) { list = _entry_list_flatten(list, subentry); } eina_lock_release(&(entry->lock)); return list; } static void _sel_go(Evas_Object *win EINA_UNUSED, Entry *base_entry, int x, int y) { Evas_Object *o; Evas_Coord bxx, bxy, tbx, tby; const char *file; if (!base_entry) return; evas_object_geometry_get(bx, &bxx, &bxy, NULL, NULL); Entry *entry = _sel_find(base_entry); if (!entry) { Eina_List *flatlist = _entry_list_flatten(NULL, base_entry); if (flatlist) { entry = flatlist->data; eina_lock_take(&(entry->lock)); entry->sel = EINA_TRUE; entry->sel_x = 0; entry->sel_y = 0; o = _sel_object_find(entry); elm_layout_signal_emit(o, "rage,state,selected", "rage"); evas_object_raise(o); eina_lock_release(&(entry->lock)); eina_list_free(flatlist); } } else { int sel_x, sel_y, num; Entry *subentry; Eina_List *l; Eina_List *flatlist = _entry_list_flatten(NULL, base_entry); eina_lock_take(&(entry->lock)); sel_x = entry->sel_x + x; sel_y = entry->sel_y + y; if (sel_x >= entry->cols) { sel_y++; sel_x = 0; } else if (sel_x < 0) { sel_y--; sel_x = entry->cols - 1; } num = (sel_y * entry->cols) + sel_x; if (num < 0) { EINA_LIST_FOREACH(flatlist, l, subentry) { if (subentry == entry) { if (l->prev) { subentry = l->prev->data; entry->sel = EINA_FALSE; subentry->sel = EINA_TRUE; if (sel_x < subentry->cols) subentry->sel_x = sel_x; else subentry->sel_x = subentry->cols - 1; subentry->sel_y = subentry->rows - 1; num = eina_list_count(subentry->files) - 1 - ((subentry->sel_y * subentry->cols) + subentry->sel_x); if (num < 0) subentry->sel_x += num; evas_object_geometry_get(subentry->table, &tbx, &tby, NULL, NULL); o = _sel_object_find(entry); if (o) elm_layout_signal_emit(o, "rage,state,unselected", "rage"); o = _sel_object_find(subentry); if (o) { elm_layout_signal_emit(o, "rage,state,selected", "rage"); evas_object_raise(o); } elm_scroller_region_bring_in (sc, (tbx - bxx) + (subentry->sel_x * subentry->iw), (tby - bxy) + (subentry->sel_y * subentry->ih), entry->iw, entry->ih); } break; } } } else if (num >= (int)eina_list_count(entry->files)) { EINA_LIST_FOREACH(flatlist, l, subentry) { if (subentry == entry) { if (l->next) { subentry = l->next->data; entry->sel = EINA_FALSE; subentry->sel = EINA_TRUE; if (sel_x < subentry->cols) subentry->sel_x = sel_x; else subentry->sel_x = subentry->cols - 1; subentry->sel_y = 0; num = eina_list_count(subentry->files) - 1 - ((subentry->sel_y * subentry->cols) + subentry->sel_x); if (num < 0) subentry->sel_x += num; evas_object_geometry_get(subentry->table, &tbx, &tby, NULL, NULL); o = _sel_object_find(entry); if (o) elm_layout_signal_emit(o, "rage,state,unselected", "rage"); o = _sel_object_find(subentry); if (o) { elm_layout_signal_emit(o, "rage,state,selected", "rage"); evas_object_raise(o); } elm_scroller_region_bring_in (sc, (tbx - bxx) + (subentry->sel_x * subentry->iw), (tby - bxy) + (subentry->sel_y * subentry->ih), entry->iw, entry->ih); } break; } } } else { o = _sel_object_find(entry); if (o) elm_layout_signal_emit(o, "rage,state,unselected", "rage"); entry->sel_x = sel_x; entry->sel_y = sel_y; evas_object_geometry_get(entry->table, &tbx, &tby, NULL, NULL); o = _sel_object_find(entry); if (o) { elm_layout_signal_emit(o, "rage,state,selected", "rage"); evas_object_raise(o); } elm_scroller_region_bring_in (sc, (tbx - bxx) + (entry->sel_x * entry->iw), (tby - bxy) + (entry->sel_y * entry->ih), entry->iw, entry->ih); } eina_lock_release(&(entry->lock)); eina_list_free(flatlist); } entry = _sel_find(base_entry); if (entry) { file = _sel_file_find(entry); if (file) { char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "%s/%s", entry->path, file); if (selfile) free(selfile); selfile = strdup(buf); } } } static void _sel_do(Evas_Object *win, Entry *base_entry) { Entry *entry; if (!base_entry) return; entry = _sel_find(base_entry); if (entry) { eina_lock_take(&(entry->lock)); Evas_Object *o = _sel_object_find(entry); const char *file = _sel_file_find(entry); if (file) { char buf[PATH_MAX]; elm_layout_signal_emit(o, "rage,state,selected", "rage"); evas_object_raise(o); _activate(win, entry, file); snprintf(buf, sizeof(buf), "%s/%s", entry->path, file); if (selfile) free(selfile); selfile = strdup(buf); } eina_lock_release(&(entry->lock)); } } static void _cb_key_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Key_Down *ev = event_info; Evas_Object *win = data; if ((!strcmp(ev->key, "Left")) || (!strcmp(ev->key, "bracketleft"))) { _sel_go(win, dir_entry, -1, 0); } else if ((!strcmp(ev->key, "Right")) || (!strcmp(ev->key, "bracketright"))) { _sel_go(win, dir_entry, 1, 0); } else if ((!strcmp(ev->key, "Up")) || (!strcmp(ev->key, "XF86AudioPrev"))) { _sel_go(win, dir_entry, 0, -1); } else if ((!strcmp(ev->key, "Prior"))) { _sel_go(win, dir_entry, 0, -10); } else if ((!strcmp(ev->key, "Down")) || (!strcmp(ev->key, "XF86AudioNext"))) { _sel_go(win, dir_entry, 0, 1); } else if ((!strcmp(ev->key, "Next"))) { _sel_go(win, dir_entry, 0, 10); } else if ((!strcmp(ev->key, "space")) || (!strcmp(ev->key, "Pause")) || (!strcmp(ev->keyname, "p")) || (!strcmp(ev->key, "XF86AudioPlay")) || (!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter"))) { _sel_do(win, dir_entry); } else if ((!strcmp(ev->keyname, "q")) || (!strcmp(ev->key, "Escape")) || (!strcmp(ev->key, "e"))) { browser_hide(win); } else key_handle(win, ev); elm_object_focus_set(bt, EINA_TRUE); } static Ecore_Timer *focus_timer = NULL; static Eina_Bool _browser_focus_timer_cb(void *data) { focus_timer = NULL; elm_object_focus_set(data, EINA_TRUE); return EINA_FALSE; } Eina_Bool browser_visible(void) { if (bx) return EINA_TRUE; return EINA_FALSE; } void browser_show(Evas_Object *win) { Inf *inf = evas_object_data_get(win, "inf"); if (_browser_hide_focus_restore_timer) ecore_timer_del(_browser_hide_focus_restore_timer); _browser_hide_focus_restore_timer = NULL; if (!bx) { bx = elm_box_add(win); sc = elm_scroller_add(win); evas_object_event_callback_add(sc, EVAS_CALLBACK_RESIZE, _cb_scroller_resize, win); dnd_init(win, sc); elm_object_style_set(sc, "noclip"); elm_object_focus_allow_set(sc, EINA_FALSE); evas_object_size_hint_weight_set(sc, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(sc, EVAS_HINT_FILL, EVAS_HINT_FILL); // elm_scroller_content_min_limit(sc, EINA_TRUE, EINA_FALSE); bx = elm_box_add(win); elm_object_focus_allow_set(bx, EINA_FALSE); evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0); evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.5); elm_object_content_set(sc, bx); evas_object_show(bx); elm_object_part_content_set(inf->lay, "rage.browser", sc); _fill(win); // a dummy button to collect key events and have focus bt = elm_button_add(win); elm_object_focus_highlight_style_set(bt, "blank"); evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(win, bt); evas_object_lower(bt); evas_object_show(bt); evas_object_event_callback_add(bt, EVAS_CALLBACK_KEY_DOWN, _cb_key_down, win); if (focus_timer) ecore_timer_del(focus_timer); focus_timer = ecore_timer_add(0.7, _browser_focus_timer_cb, bt); } elm_layout_signal_emit(inf->lay, "browser,state,visible", "rage"); initial_update_timer = ecore_timer_add(0.2, _cb_initial_update_timer, win); } static void _cb_hidden(void *data, Evas_Object *obj, const char *sig EINA_UNUSED, const char *src EINA_UNUSED) { elm_layout_signal_callback_del(obj, "browser,state,hidden,finished", "rage", _cb_hidden); if (fill_thread) ecore_thread_cancel(fill_thread); if (dir_entry) _entry_free(dir_entry); dir_entry = NULL; evas_object_del(bx); bx = NULL; evas_object_del(bt); bt = NULL; evas_object_del(sc); sc = NULL; elm_object_focus_next(data, ELM_FOCUS_PREVIOUS); } static Eina_Bool _browser_hide_focus_restore_cb(void *data) { win_focus(data); _browser_hide_focus_restore_timer = NULL; return EINA_FALSE; } void browser_hide(Evas_Object *win) { Inf *inf = evas_object_data_get(win, "inf"); if (!bx) return; if (initial_update_timer) { ecore_timer_del(initial_update_timer); initial_update_timer = NULL; } if (focus_timer) ecore_timer_del(focus_timer); focus_timer = NULL; elm_layout_signal_callback_add(inf->lay, "browser,state,hidden,finished", "rage", _cb_hidden, win); elm_layout_signal_emit(inf->lay, "browser,state,hidden", "rage"); if (_browser_hide_focus_restore_timer) ecore_timer_del(_browser_hide_focus_restore_timer); _browser_hide_focus_restore_timer = ecore_timer_add(0.2, _browser_hide_focus_restore_cb, win); } void browser_toggle(Evas_Object *win) { if (bx) browser_hide(win); else browser_show(win); } void browser_size_update(Evas_Object *win) { Inf *inf = evas_object_data_get(win, "inf"); Eina_List *l; Entry *entry; if (!inf) return; if (!bx) return; if (initial_update_timer) return; EINA_LIST_FOREACH(entries, l, entry) { _entry_files_redo(win, entry); } } void browser_fullscreen(Evas_Object *win, EINA_UNUSED Eina_Bool enabled) { Inf *inf = evas_object_data_get(win, "inf"); Eina_List *l; Entry *entry; if (!inf) return; if (!bx) return; if (initial_update_timer) return; EINA_LIST_FOREACH(entries, l, entry) { _entry_files_redo(win, entry); } }