Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_file/filelist.c')
-rw-r--r--source/blender/editors/space_file/filelist.c2438
1 files changed, 1854 insertions, 584 deletions
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 3358812796e..56c48b7f650 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -35,6 +35,8 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
#ifndef WIN32
# include <unistd.h>
@@ -45,9 +47,16 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
#include "BLI_fnmatch.h"
+#include "BLI_ghash.h"
+#include "BLI_hash_md5.h"
#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_stack.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
#ifdef WIN32
@@ -60,13 +69,13 @@
#include "BKE_icons.h"
#include "BKE_idcode.h"
#include "BKE_main.h"
-#include "BKE_report.h"
#include "BLO_readfile.h"
#include "DNA_space_types.h"
#include "ED_datafiles.h"
#include "ED_fileselect.h"
+#include "ED_screen.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -78,6 +87,9 @@
#include "WM_types.h"
#include "UI_resources.h"
+#include "UI_interface_icons.h"
+
+#include "atomic_ops.h"
#include "filelist.h"
@@ -194,49 +206,132 @@ ListBase *folderlist_duplicate(ListBase *folderlist)
/* ------------------FILELIST------------------------ */
-struct FileList;
+typedef struct FileListInternEntry {
+ struct FileListInternEntry *next, *prev;
+
+ char uuid[16]; /* ASSET_UUID_LENGTH */
+
+ int typeflag; /* eFileSel_File_Types */
+ int blentype; /* ID type, in case typeflag has FILE_TYPE_BLENDERLIB set. */
+
+ char *relpath;
+ char *name; /* not striclty needed, but used during sorting, avoids to have to recompute it there... */
+
+ BLI_stat_t st;
+} FileListInternEntry;
+
+typedef struct FileListIntern {
+ ListBase entries; /* FileListInternEntry items. */
+ FileListInternEntry **filtered;
+
+ char curr_uuid[16]; /* Used to generate uuid during internal listing. */
+} FileListIntern;
+
+#define FILELIST_ENTRYCACHESIZE_DEFAULT 1024 /* Keep it a power of two! */
+typedef struct FileListEntryCache {
+ size_t size; /* The size of the cache... */
+
+ int flags;
+
+ /* This one gathers all entries from both block and misc caches. Used for easy bulk-freing. */
+ ListBase cached_entries;
+
+ /* Block cache: all entries between start and end index. used for part of the list on diplay. */
+ FileDirEntry **block_entries;
+ int block_start_index, block_end_index, block_center_index, block_cursor;
+
+ /* Misc cache: random indices, FIFO behavior.
+ * Note: Not 100% sure we actually need that, time will say. */
+ int misc_cursor;
+ int *misc_entries_indices;
+ GHash *misc_entries;
-typedef struct FileImage {
- struct FileImage *next, *prev;
+ /* Allows to quickly get a cached entry from its UUID. */
+ GHash *uuids;
+
+ /* Previews handling. */
+ TaskScheduler *previews_scheduler;
+ TaskPool *previews_pool;
+ ThreadQueue *previews_todo;
+ ThreadQueue *previews_done;
+ double previews_timestamp;
+ int previews_pending;
+} FileListEntryCache;
+
+/* FileListCache.flags */
+enum {
+ FLC_IS_INIT = 1 << 0,
+ FLC_PREVIEWS_ACTIVE = 1 << 1,
+};
+
+typedef struct FileListEntryPreview {
char path[FILE_MAX];
unsigned int flags;
int index;
- short done;
ImBuf *img;
-} FileImage;
+} FileListEntryPreview;
typedef struct FileListFilter {
- bool hide_dot;
- bool hide_parent;
unsigned int filter;
+ unsigned int filter_id;
char filter_glob[64];
char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
+ short flags;
} FileListFilter;
+/* FileListFilter.flags */
+enum {
+ FLF_HIDE_DOT = 1 << 0,
+ FLF_HIDE_PARENT = 1 << 1,
+ FLF_HIDE_LIB_DIR = 1 << 2,
+};
+
typedef struct FileList {
- struct direntry *filelist;
- int numfiles;
- char dir[FILE_MAX];
+ FileDirEntryArr filelist;
+
short prv_w;
short prv_h;
- bool changed;
+ short flags;
short sort;
- bool need_sorting;
FileListFilter filter_data;
- int *fidx; /* Also used to detect when we need to filter! */
- int numfiltered;
- bool need_thumbnails;
+ struct FileListIntern filelist_intern;
+
+ struct FileListEntryCache filelist_cache;
+
+ /* We need to keep those info outside of actual filelist items, because those are no more persistent
+ * (only generated on demand, and freed as soon as possible).
+ * Persistent part (mere list of paths + stat info) is kept as small as possible, and filebrowser-agnostic.
+ */
+ GHash *selection_state;
+
+ short max_recursion;
+ short recursion_level;
struct BlendHandle *libfiledata;
- void (*readf)(struct FileList *);
- bool (*filterf)(struct direntry *, const char *, FileListFilter *);
+ /* Set given path as root directory, may change given string in place to a valid value. */
+ void (*checkdirf)(struct FileList *, char *);
+
+ /* Fill filelist (to be called by read job). */
+ void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+
+ /* Filter an entry of current filelist. */
+ bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *);
} FileList;
+/* FileList.flags */
+enum {
+ FL_FORCE_RESET = 1 << 0,
+ FL_IS_READY = 1 << 1,
+ FL_IS_PENDING = 1 << 2,
+ FL_NEED_SORTING = 1 << 3,
+ FL_NEED_FILTERING = 1 << 4,
+};
+
#define SPECIAL_IMG_SIZE 48
#define SPECIAL_IMG_ROWS 4
#define SPECIAL_IMG_COLS 4
@@ -260,153 +355,188 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_from_main(struct FileList *filelist);
-static void filelist_from_library(struct FileList *filelist);
+static void filelist_readjob_main(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+static void filelist_readjob_lib(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+static void filelist_readjob_dir(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
-static void filelist_read_main(struct FileList *filelist);
-static void filelist_read_library(struct FileList *filelist);
-static void filelist_read_dir(struct FileList *filelist);
+/* helper, could probably go in BKE actually? */
+static int groupname_to_code(const char *group);
+static unsigned int groupname_to_filter_id(const char *group);
static void filelist_filter_clear(FileList *filelist);
+static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size);
/* ********** Sort helpers ********** */
-static bool compare_is_directory(const struct direntry *entry)
-{
- /* for library browse .blend files may be treated as directories, but
- * for sorting purposes they should be considered regular files */
- if (S_ISDIR(entry->type))
- return !(entry->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP));
-
- return false;
-}
-
-static int compare_direntry_generic(const struct direntry *entry1, const struct direntry *entry2)
+static int compare_direntry_generic(const FileListInternEntry *entry1, const FileListInternEntry *entry2)
{
/* type is equal to stat.st_mode */
- if (compare_is_directory(entry1)) {
- if (compare_is_directory(entry2) == 0) {
- return -1;
+ if (entry1->typeflag & FILE_TYPE_DIR) {
+ if (entry2->typeflag & FILE_TYPE_DIR) {
+ /* If both entries are tagged as dirs, we make a 'sub filter' that shows first the real dirs,
+ * then libs (.blend files), then categories in libs. */
+ if (entry1->typeflag & FILE_TYPE_BLENDERLIB) {
+ if (!(entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
+ return 1;
+ }
+ }
+ else if (entry2->typeflag & FILE_TYPE_BLENDERLIB) {
+ return -1;
+ }
+ else if (entry1->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ if (!(entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
+ return 1;
+ }
+ }
+ else if (entry2->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ return -1;
+ }
}
- }
- else if (compare_is_directory(entry2)) {
- return 1;
- }
-
- if (S_ISREG(entry1->type)) {
- if (!S_ISREG(entry2->type)) {
+ else {
return -1;
}
}
- else if (S_ISREG(entry2->type)) {
- return 1;
+ else if (entry2->typeflag & FILE_TYPE_DIR) {
+ return 1;
}
- if ((entry1->type & S_IFMT) < (entry2->type & S_IFMT)) return -1;
- if ((entry1->type & S_IFMT) > (entry2->type & S_IFMT)) return 1;
-
+
/* make sure "." and ".." are always first */
- if (FILENAME_IS_CURRENT(entry1->relname)) return -1;
- if (FILENAME_IS_CURRENT(entry2->relname)) return 1;
- if (FILENAME_IS_PARENT(entry1->relname)) return -1;
- if (FILENAME_IS_PARENT(entry2->relname)) return 1;
+ if (FILENAME_IS_CURRENT(entry1->relpath)) return -1;
+ if (FILENAME_IS_CURRENT(entry2->relpath)) return 1;
+ if (FILENAME_IS_PARENT(entry1->relpath)) return -1;
+ if (FILENAME_IS_PARENT(entry2->relpath)) return 1;
return 0;
}
-static int compare_name(const void *a1, const void *a2)
+static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2)
{
- const struct direntry *entry1 = a1, *entry2 = a2;
+ const FileListInternEntry *entry1 = a1;
+ const FileListInternEntry *entry2 = a2;
+ char *name1, *name2;
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
- return (BLI_natstrcmp(entry1->relname, entry2->relname));
+ name1 = entry1->name;
+ name2 = entry2->name;
+
+ return BLI_natstrcmp(name1, name2);
}
-static int compare_date(const void *a1, const void *a2)
+static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2)
{
- const struct direntry *entry1 = a1, *entry2 = a2;
+ const FileListInternEntry *entry1 = a1;
+ const FileListInternEntry *entry2 = a2;
+ char *name1, *name2;
+ int64_t time1, time2;
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
- if (entry1->s.st_mtime < entry2->s.st_mtime) return 1;
- if (entry1->s.st_mtime > entry2->s.st_mtime) return -1;
+ time1 = (int64_t)entry1->st.st_mtime;
+ time2 = (int64_t)entry2->st.st_mtime;
+ if (time1 < time2) return 1;
+ if (time1 > time2) return -1;
- return BLI_natstrcmp(entry1->relname, entry2->relname);
+ name1 = entry1->name;
+ name2 = entry2->name;
+
+ return BLI_natstrcmp(name1, name2);
}
-static int compare_size(const void *a1, const void *a2)
+static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2)
{
- const struct direntry *entry1 = a1, *entry2 = a2;
+ const FileListInternEntry *entry1 = a1;
+ const FileListInternEntry *entry2 = a2;
+ char *name1, *name2;
+ uint64_t size1, size2;
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
- if (entry1->s.st_size < entry2->s.st_size) return 1;
- if (entry1->s.st_size > entry2->s.st_size) return -1;
+ size1 = entry1->st.st_size;
+ size2 = entry2->st.st_size;
+ if (size1 < size2) return 1;
+ if (size1 > size2) return -1;
+
+ name1 = entry1->name;
+ name2 = entry2->name;
- return BLI_natstrcmp(entry1->relname, entry2->relname);
+ return BLI_natstrcmp(name1, name2);
}
-static int compare_extension(const void *a1, const void *a2)
+static int compare_extension(void *UNUSED(user_data), const void *a1, const void *a2)
{
- const struct direntry *entry1 = a1, *entry2 = a2;
- const char *sufix1, *sufix2;
- const char *nil = "";
+ const FileListInternEntry *entry1 = a1;
+ const FileListInternEntry *entry2 = a2;
+ char *name1, *name2;
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
return ret;
}
- if (!(sufix1 = strstr(entry1->relname, ".blend.gz")))
- sufix1 = strrchr(entry1->relname, '.');
- if (!(sufix2 = strstr(entry2->relname, ".blend.gz")))
- sufix2 = strrchr(entry2->relname, '.');
- if (!sufix1) sufix1 = nil;
- if (!sufix2) sufix2 = nil;
+ if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && !(entry2->typeflag & FILE_TYPE_BLENDERLIB)) return -1;
+ if (!(entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) return 1;
+ if ((entry1->typeflag & FILE_TYPE_BLENDERLIB) && (entry2->typeflag & FILE_TYPE_BLENDERLIB)) {
+ if ((entry1->typeflag & FILE_TYPE_DIR) && !(entry2->typeflag & FILE_TYPE_DIR)) return 1;
+ if (!(entry1->typeflag & FILE_TYPE_DIR) && (entry2->typeflag & FILE_TYPE_DIR)) return -1;
+ if (entry1->blentype < entry2->blentype) return -1;
+ if (entry1->blentype > entry2->blentype) return 1;
+ }
+ else {
+ const char *sufix1, *sufix2;
+
+ if (!(sufix1 = strstr(entry1->relpath, ".blend.gz")))
+ sufix1 = strrchr(entry1->relpath, '.');
+ if (!(sufix2 = strstr(entry2->relpath, ".blend.gz")))
+ sufix2 = strrchr(entry2->relpath, '.');
+ if (!sufix1) sufix1 = "";
+ if (!sufix2) sufix2 = "";
- return BLI_strcasecmp(sufix1, sufix2);
-}
+ if ((ret = BLI_strcasecmp(sufix1, sufix2))) {
+ return ret;
+ }
+ }
-bool filelist_need_sorting(struct FileList *filelist)
-{
- return filelist->need_sorting && (filelist->sort != FILE_SORT_NONE);
+ name1 = entry1->name;
+ name2 = entry2->name;
+
+ return BLI_natstrcmp(name1, name2);
}
void filelist_sort(struct FileList *filelist)
{
- if (filelist_need_sorting(filelist)) {
- filelist->need_sorting = false;
-
+ if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) {
switch (filelist->sort) {
case FILE_SORT_ALPHA:
- qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_name);
+ BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_name, NULL);
break;
case FILE_SORT_TIME:
- qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_date);
+ BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_date, NULL);
break;
case FILE_SORT_SIZE:
- qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_size);
+ BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_size, NULL);
break;
case FILE_SORT_EXTENSION:
- qsort(filelist->filelist, filelist->numfiles, sizeof(struct direntry), compare_extension);
+ BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_extension, NULL);
break;
case FILE_SORT_NONE: /* Should never reach this point! */
default:
BLI_assert(0);
- return;
+ break;
}
filelist_filter_clear(filelist);
+ filelist->flags &= ~FL_NEED_SORTING;
}
}
@@ -414,7 +544,7 @@ void filelist_setsorting(struct FileList *filelist, const short sort)
{
if (filelist->sort != sort) {
filelist->sort = sort;
- filelist->need_sorting = true;
+ filelist->flags |= FL_NEED_SORTING;
}
}
@@ -422,9 +552,10 @@ void filelist_setsorting(struct FileList *filelist, const short sort)
static bool is_hidden_file(const char *filename, FileListFilter *filter)
{
+ char *sep = (char *)BLI_last_slash(filename);
bool is_hidden = false;
- if (filter->hide_dot) {
+ if (filter->flags & FLF_HIDE_DOT) {
if (filename[0] == '.' && filename[1] != '.' && filename[1] != '\0') {
is_hidden = true; /* ignore .file */
}
@@ -435,7 +566,7 @@ static bool is_hidden_file(const char *filename, FileListFilter *filter)
}
}
}
- if (!is_hidden && filter->hide_parent) {
+ if (!is_hidden && (filter->flags & FLF_HIDE_PARENT)) {
if (filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') {
is_hidden = true; /* ignore .. */
}
@@ -443,22 +574,49 @@ static bool is_hidden_file(const char *filename, FileListFilter *filter)
if (!is_hidden && ((filename[0] == '.') && (filename[1] == '\0'))) {
is_hidden = true; /* ignore . */
}
+ /* filename might actually be a piece of path, in which case we have to check all its parts. */
+ if (!is_hidden && sep) {
+ char tmp_filename[FILE_MAX_LIBEXTRA];
+
+ BLI_strncpy(tmp_filename, filename, sizeof(tmp_filename));
+ sep = tmp_filename + (sep - filename);
+ while (sep) {
+ BLI_assert(sep[1] != '\0');
+ if (is_hidden_file(sep + 1, filter)) {
+ is_hidden = true;
+ break;
+ }
+ *sep = '\0';
+ sep = (char *)BLI_last_slash(tmp_filename);
+ }
+ }
return is_hidden;
}
-static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), FileListFilter *filter)
+static bool is_filtered_file(FileListInternEntry *file, const char *UNUSED(root), FileListFilter *filter)
{
- bool is_filtered = !is_hidden_file(file->relname, filter);
+ bool is_filtered = !is_hidden_file(file->relpath, filter);
- if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) {
- if ((file->type & S_IFDIR) && !(filter->filter & FILE_TYPE_FOLDER)) {
- is_filtered = false;
+ if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relpath)) {
+ if (file->typeflag & FILE_TYPE_DIR) {
+ if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
+ is_filtered = false;
+ }
+ }
+ else {
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
+ is_filtered = false;
+ }
+ }
}
- if (!(file->type & S_IFDIR) && !(file->flags & filter->filter)) {
- is_filtered = false;
+ else {
+ if (!(file->typeflag & filter->filter)) {
+ is_filtered = false;
+ }
}
if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relname, FNM_CASEFOLD) != 0) {
+ if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
}
}
@@ -467,16 +625,41 @@ static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), Fi
return is_filtered;
}
-static bool is_filtered_lib(struct direntry *file, const char *root, FileListFilter *filter)
+static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
{
- bool is_filtered = !is_hidden_file(file->relname, filter);
- char dir[FILE_MAXDIR], group[BLO_GROUP_MAX];
+ bool is_filtered;
+ char path[FILE_MAX_LIBEXTRA], dir[FILE_MAXDIR], *group, *name;
- if (BLO_is_a_library(root, dir, group)) {
- is_filtered = !is_hidden_file(file->relname, filter);
- if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relname)) {
+ BLI_join_dirfile(path, sizeof(path), root, file->relpath);
+
+ if (BLO_library_path_explode(path, dir, &group, &name)) {
+ is_filtered = !is_hidden_file(file->relpath, filter);
+ if (is_filtered && filter->filter && !FILENAME_IS_CURRPAR(file->relpath)) {
+ if (file->typeflag & FILE_TYPE_DIR) {
+ if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
+ is_filtered = false;
+ }
+ }
+ else {
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
+ is_filtered = false;
+ }
+ }
+ }
+ if (is_filtered && group) {
+ if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
+ is_filtered = false;
+ }
+ else {
+ unsigned int filter_id = groupname_to_filter_id(group);
+ if (!(filter_id & filter->filter_id)) {
+ is_filtered = false;
+ }
+ }
+ }
if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relname, FNM_CASEFOLD) != 0) {
+ if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
}
}
@@ -489,70 +672,97 @@ static bool is_filtered_lib(struct direntry *file, const char *root, FileListFil
return is_filtered;
}
-static bool is_filtered_main(struct direntry *file, const char *UNUSED(dir), FileListFilter *filter)
+static bool is_filtered_main(FileListInternEntry *file, const char *UNUSED(dir), FileListFilter *filter)
{
- return !is_hidden_file(file->relname, filter);
+ return !is_hidden_file(file->relpath, filter);
}
static void filelist_filter_clear(FileList *filelist)
{
- MEM_SAFE_FREE(filelist->fidx);
- filelist->numfiltered = 0;
+ filelist->flags |= FL_NEED_FILTERING;
}
void filelist_filter(FileList *filelist)
{
int num_filtered = 0;
- int *fidx_tmp;
- int i;
+ const int num_files = filelist->filelist.nbr_entries;
+ FileListInternEntry **filtered_tmp, *file;
- if (!filelist->filelist) {
+ if (filelist->filelist.nbr_entries == 0) {
return;
}
- if (filelist->fidx) {
+ if (!(filelist->flags & FL_NEED_FILTERING)) {
/* Assume it has already been filtered, nothing else to do! */
return;
}
- fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__);
+ filelist->filter_data.flags &= ~FLF_HIDE_LIB_DIR;
+ if (filelist->max_recursion) {
+ /* Never show lib ID 'categories' directories when we are in 'flat' mode, unless
+ * root path is a blend file. */
+ char dir[FILE_MAXDIR];
+ if (!filelist_islibrary(filelist, dir, NULL)) {
+ filelist->filter_data.flags |= FLF_HIDE_LIB_DIR;
+ }
+ }
- /* Filter remap & count how many files are left after filter in a single loop. */
- for (i = 0; i < filelist->numfiles; ++i) {
- struct direntry *file = &filelist->filelist[i];
+ filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__);
- if (filelist->filterf(file, filelist->dir, &filelist->filter_data)) {
- fidx_tmp[num_filtered++] = i;
+ /* Filter remap & count how many files are left after filter in a single loop. */
+ for (file = filelist->filelist_intern.entries.first; file; file = file->next) {
+ if (filelist->filterf(file, filelist->filelist.root, &filelist->filter_data)) {
+ filtered_tmp[num_filtered++] = file;
}
}
- /* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */
- filelist->fidx = MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__);
- memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered);
- filelist->numfiltered = num_filtered;
+ if (filelist->filelist_intern.filtered) {
+ MEM_freeN(filelist->filelist_intern.filtered);
+ }
+ filelist->filelist_intern.filtered = MEM_mallocN(sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered,
+ __func__);
+ memcpy(filelist->filelist_intern.filtered, filtered_tmp,
+ sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered);
+ filelist->filelist.nbr_entries_filtered = num_filtered;
+// printf("Filetered: %d over %d entries\n", num_filtered, filelist->filelist.nbr_entries);
+
+ filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
+ filelist->flags &= ~FL_NEED_FILTERING;
- MEM_freeN(fidx_tmp);
+ MEM_freeN(filtered_tmp);
}
void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent,
- const unsigned int filter,
+ const unsigned int filter, const unsigned int filter_id,
const char *filter_glob, const char *filter_search)
{
- if ((filelist->filter_data.hide_dot != hide_dot) ||
- (filelist->filter_data.hide_parent != hide_parent) ||
- (filelist->filter_data.filter != filter) ||
- !STREQ(filelist->filter_data.filter_glob, filter_glob) ||
- (BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0))
- {
- filelist->filter_data.hide_dot = hide_dot;
- filelist->filter_data.hide_parent = hide_parent;
+ bool update = false;
+ if (((filelist->filter_data.flags & FLF_HIDE_DOT) != 0) != (hide_dot != 0)) {
+ filelist->filter_data.flags ^= FLF_HIDE_DOT;
+ update = true;
+ }
+ if (((filelist->filter_data.flags & FLF_HIDE_PARENT) != 0) != (hide_parent != 0)) {
+ filelist->filter_data.flags ^= FLF_HIDE_PARENT;
+ update = true;
+ }
+ if ((filelist->filter_data.filter != filter) || (filelist->filter_data.filter_id != filter_id)) {
filelist->filter_data.filter = filter;
+ filelist->filter_data.filter_id = filter_id;
+ update = true;
+ }
+ if (!STREQ(filelist->filter_data.filter_glob, filter_glob)) {
BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob));
+ update = true;
+ }
+ if ((BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0)) {
BLI_strncpy_ensure_pad(filelist->filter_data.filter_search, filter_search, '*',
sizeof(filelist->filter_data.filter_search));
+ update = true;
+ }
- /* And now, free filtered data so that we now we have to filter again. */
+ if (update) {
+ /* And now, free filtered data so that we know we have to filter again. */
filelist_filter_clear(filelist);
}
}
@@ -607,101 +817,512 @@ void filelist_imgsize(struct FileList *filelist, short w, short h)
filelist->prv_h = h;
}
-ImBuf *filelist_getimage(struct FileList *filelist, const int index)
+static FileDirEntry *filelist_geticon_get_file(struct FileList *filelist, const int index)
{
- ImBuf *ibuf = NULL;
- int fidx = 0;
-
BLI_assert(G.background == false);
- if ((index < 0) || (index >= filelist->numfiltered)) {
- return NULL;
- }
- fidx = filelist->fidx[index];
- ibuf = filelist->filelist[fidx].image;
+ return filelist_file(filelist, index);
+}
- return ibuf;
+ImBuf *filelist_getimage(struct FileList *filelist, const int index)
+{
+ FileDirEntry *file = filelist_geticon_get_file(filelist, index);
+
+ return file->image;
}
-ImBuf *filelist_geticon(struct FileList *filelist, const int index)
+static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char *relpath)
{
ImBuf *ibuf = NULL;
- struct direntry *file = NULL;
- int fidx = 0;
- BLI_assert(G.background == false);
-
- if ((index < 0) || (index >= filelist->numfiltered)) {
- return NULL;
- }
- fidx = filelist->fidx[index];
- file = &filelist->filelist[fidx];
- if (file->type & S_IFDIR) {
- if (FILENAME_IS_PARENT(filelist->filelist[fidx].relname)) {
+ if (typeflag & FILE_TYPE_DIR) {
+ if (FILENAME_IS_PARENT(relpath)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
}
- else if (FILENAME_IS_CURRENT(filelist->filelist[fidx].relname)) {
+ else if (FILENAME_IS_CURRENT(relpath)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH];
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
}
}
- else {
- ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
- }
-
- if (file->flags & FILE_TYPE_BLENDER) {
+ else if (typeflag & FILE_TYPE_BLENDER) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE];
}
- else if (file->flags & FILE_TYPE_MOVIE) {
+ else if (typeflag & FILE_TYPE_BLENDERLIB) {
+ ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
+ }
+ else if (typeflag & (FILE_TYPE_MOVIE)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
}
- else if (file->flags & FILE_TYPE_SOUND) {
+ else if (typeflag & FILE_TYPE_SOUND) {
ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
}
- else if (file->flags & FILE_TYPE_PYSCRIPT) {
+ else if (typeflag & FILE_TYPE_PYSCRIPT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
}
- else if (file->flags & FILE_TYPE_FTFONT) {
+ else if (typeflag & FILE_TYPE_FTFONT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
}
- else if (file->flags & FILE_TYPE_TEXT) {
+ else if (typeflag & FILE_TYPE_TEXT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
}
- else if (file->flags & FILE_TYPE_IMAGE) {
+ else if (typeflag & FILE_TYPE_IMAGE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
}
- else if (file->flags & FILE_TYPE_BLENDER_BACKUP) {
+ else if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
}
+ else {
+ ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
+ }
return ibuf;
}
+ImBuf *filelist_geticon_image(struct FileList *filelist, const int index)
+{
+ FileDirEntry *file = filelist_geticon_get_file(filelist, index);
+
+ return filelist_geticon_image_ex(file->typeflag, file->relpath);
+}
+
+static int filelist_geticon_ex(
+ const int typeflag, const int blentype, const char *relpath, const bool is_main, const bool ignore_libdir)
+{
+ if ((typeflag & FILE_TYPE_DIR) && !(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) {
+ if (FILENAME_IS_PARENT(relpath)) {
+ return is_main ? ICON_FILE_PARENT : ICON_NONE;
+ }
+ else if (typeflag & FILE_TYPE_APPLICATIONBUNDLE) {
+ return ICON_UGLYPACKAGE;
+ }
+ else if (typeflag & FILE_TYPE_BLENDER) {
+ return ICON_FILE_BLEND;
+ }
+ else if (is_main) {
+ /* Do not return icon for folders if icons are not 'main' draw type (e.g. when used over previews). */
+ return ICON_FILE_FOLDER;
+ }
+ }
+
+ if (typeflag & FILE_TYPE_BLENDER)
+ return ICON_FILE_BLEND;
+ else if (typeflag & FILE_TYPE_BLENDER_BACKUP)
+ return ICON_FILE_BACKUP;
+ else if (typeflag & FILE_TYPE_IMAGE)
+ return ICON_FILE_IMAGE;
+ else if (typeflag & FILE_TYPE_MOVIE)
+ return ICON_FILE_MOVIE;
+ else if (typeflag & FILE_TYPE_PYSCRIPT)
+ return ICON_FILE_SCRIPT;
+ else if (typeflag & FILE_TYPE_SOUND)
+ return ICON_FILE_SOUND;
+ else if (typeflag & FILE_TYPE_FTFONT)
+ return ICON_FILE_FONT;
+ else if (typeflag & FILE_TYPE_BTX)
+ return ICON_FILE_BLANK;
+ else if (typeflag & FILE_TYPE_COLLADA)
+ return ICON_FILE_BLANK;
+ else if (typeflag & FILE_TYPE_TEXT)
+ return ICON_FILE_TEXT;
+ else if (typeflag & FILE_TYPE_BLENDERLIB) {
+ const int ret = UI_idcode_icon_get(blentype);
+ if (ret != ICON_NONE) {
+ return ret;
+ }
+ }
+ return is_main ? ICON_FILE_BLANK : ICON_NONE;
+}
+
+int filelist_geticon(struct FileList *filelist, const int index, const bool is_main)
+{
+ FileDirEntry *file = filelist_geticon_get_file(filelist, index);
+
+ return filelist_geticon_ex(file->typeflag, file->blentype, file->relpath, is_main, false);
+}
+
/* ********** Main ********** */
+static void filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir)
+{
+ BLI_make_exist(r_dir);
+}
+
+static void filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir)
+{
+ char dir[FILE_MAXDIR];
+ if (!BLO_library_path_explode(r_dir, dir, NULL, NULL)) {
+ /* if not a valid library, we need it to be a valid directory! */
+ BLI_make_exist(r_dir);
+ }
+}
+
+static void filelist_checkdir_main(struct FileList *filelist, char *r_dir)
+{
+ /* TODO */
+ filelist_checkdir_lib(filelist, r_dir);
+}
+
+static void filelist_entry_clear(FileDirEntry *entry)
+{
+ if (entry->name) {
+ MEM_freeN(entry->name);
+ }
+ if (entry->description) {
+ MEM_freeN(entry->description);
+ }
+ if (entry->relpath) {
+ MEM_freeN(entry->relpath);
+ }
+ if (entry->image) {
+ IMB_freeImBuf(entry->image);
+ }
+ /* For now, consider FileDirEntryRevision::poin as not owned here, so no need to do anything about it */
+
+ if (!BLI_listbase_is_empty(&entry->variants)) {
+ FileDirEntryVariant *var;
+
+ for (var = entry->variants.first; var; var = var->next) {
+ if (var->name) {
+ MEM_freeN(var->name);
+ }
+ if (var->description) {
+ MEM_freeN(var->description);
+ }
+
+ if (!BLI_listbase_is_empty(&var->revisions)) {
+ FileDirEntryRevision *rev;
+
+ for (rev = var->revisions.first; rev; rev = rev->next) {
+ if (rev->comment) {
+ MEM_freeN(rev->comment);
+ }
+ }
+
+ BLI_freelistN(&var->revisions);
+ }
+ }
+
+ /* TODO: tags! */
+
+ BLI_freelistN(&entry->variants);
+ }
+ else if (entry->entry) {
+ MEM_freeN(entry->entry);
+ }
+}
+
+static void filelist_entry_free(FileDirEntry *entry)
+{
+ filelist_entry_clear(entry);
+ MEM_freeN(entry);
+}
+
+static void filelist_direntryarr_free(FileDirEntryArr *array)
+{
+#if 0
+ FileDirEntry *entry, *entry_next;
+
+ for (entry = array->entries.first; entry; entry = entry_next) {
+ entry_next = entry->next;
+ filelist_entry_free(entry);
+ }
+ BLI_listbase_clear(&array->entries);
+#else
+ BLI_assert(BLI_listbase_is_empty(&array->entries));
+#endif
+ array->nbr_entries = 0;
+ array->nbr_entries_filtered = -1;
+ array->entry_idx_start = -1;
+ array->entry_idx_end = -1;
+}
+
+static void filelist_intern_entry_free(FileListInternEntry *entry)
+{
+ if (entry->relpath) {
+ MEM_freeN(entry->relpath);
+ }
+ if (entry->name) {
+ MEM_freeN(entry->name);
+ }
+ MEM_freeN(entry);
+}
+
+static void filelist_intern_free(FileListIntern *filelist_intern)
+{
+ FileListInternEntry *entry, *entry_next;
+
+ for (entry = filelist_intern->entries.first; entry; entry = entry_next) {
+ entry_next = entry->next;
+ filelist_intern_entry_free(entry);
+ }
+ BLI_listbase_clear(&filelist_intern->entries);
+
+ MEM_SAFE_FREE(filelist_intern->filtered);
+}
+
+static void filelist_cache_previewf(TaskPool *pool, void *taskdata, int UNUSED(threadid))
+{
+ FileListEntryCache *cache = taskdata;
+ FileListEntryPreview *preview;
+
+// printf("%s: Start (%d)...\n", __func__, threadid);
+
+ /* Note we wait on queue here. */
+ while (!BLI_task_pool_canceled(pool) && (preview = BLI_thread_queue_pop(cache->previews_todo))) {
+ ThumbSource source = 0;
+
+// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ BLI_assert(preview->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
+ FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
+ if (preview->flags & FILE_TYPE_IMAGE) {
+ source = THB_SOURCE_IMAGE;
+ }
+ else if (preview->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
+ source = THB_SOURCE_BLEND;
+ }
+ else if (preview->flags & FILE_TYPE_MOVIE) {
+ source = THB_SOURCE_MOVIE;
+ }
+ else if (preview->flags & FILE_TYPE_FTFONT) {
+ source = THB_SOURCE_FONT;
+ }
+
+ IMB_thumb_path_lock(preview->path);
+ preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source);
+ IMB_thumb_path_unlock(preview->path);
+
+ BLI_thread_queue_push(cache->previews_done, preview);
+ }
+
+// printf("%s: End (%d)...\n", __func__, threadid);
+}
+
+static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
+{
+ if (!cache->previews_pool) {
+ TaskScheduler *scheduler;
+ TaskPool *pool;
+ int num_tasks = max_ii(1, (BLI_system_thread_count() / 2) + 1);
+
+ scheduler = cache->previews_scheduler = BLI_task_scheduler_create(num_tasks + 1);
+ pool = cache->previews_pool = BLI_task_pool_create(scheduler, NULL);
+ cache->previews_todo = BLI_thread_queue_init();
+ cache->previews_done = BLI_thread_queue_init();
+
+ while (num_tasks--) {
+ BLI_task_pool_push(pool, filelist_cache_previewf, cache, false, TASK_PRIORITY_HIGH);
+ }
+ IMB_thumb_locks_acquire();
+ }
+ cache->previews_timestamp = 0.0;
+}
+
+static void filelist_cache_previews_clear(FileListEntryCache *cache)
+{
+ FileListEntryPreview *preview;
+
+ if (cache->previews_pool) {
+ while ((preview = BLI_thread_queue_pop_timeout(cache->previews_todo, 0))) {
+// printf("%s: TODO %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ MEM_freeN(preview);
+ cache->previews_pending--;
+ }
+ while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
+// printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ if (preview->img) {
+ IMB_freeImBuf(preview->img);
+ }
+ MEM_freeN(preview);
+ cache->previews_pending--;
+ }
+ }
+// printf("%s: remaining pending previews: %d\n", __func__, cache->previews_pending);
+}
+
+static void filelist_cache_previews_free(FileListEntryCache *cache, const bool set_inactive)
+{
+ if (cache->previews_pool) {
+ BLI_thread_queue_nowait(cache->previews_todo);
+ BLI_thread_queue_nowait(cache->previews_done);
+ BLI_task_pool_cancel(cache->previews_pool);
+
+ filelist_cache_previews_clear(cache);
+
+ BLI_thread_queue_free(cache->previews_done);
+ BLI_thread_queue_free(cache->previews_todo);
+ BLI_task_pool_free(cache->previews_pool);
+ BLI_task_scheduler_free(cache->previews_scheduler);
+ cache->previews_scheduler = NULL;
+ cache->previews_pool = NULL;
+ cache->previews_todo = NULL;
+ cache->previews_done = NULL;
+
+ IMB_thumb_locks_release();
+ }
+ if (set_inactive) {
+ cache->flags &= ~FLC_PREVIEWS_ACTIVE;
+ }
+ BLI_assert(cache->previews_pending == 0);
+ cache->previews_timestamp = 0.0;
+}
+
+static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry, const int index)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+
+ BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
+
+ if (!entry->image &&
+ !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
+ (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
+ FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)))
+ {
+ FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ BLI_join_dirfile(preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
+ preview->index = index;
+ preview->flags = entry->typeflag;
+ preview->img = NULL;
+// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+
+ filelist_cache_preview_ensure_running(cache);
+ BLI_thread_queue_push(cache->previews_todo, preview);
+ cache->previews_pending++;
+ }
+}
+
+static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
+{
+ BLI_listbase_clear(&cache->cached_entries);
+
+ cache->block_cursor = cache->block_start_index = cache->block_center_index = cache->block_end_index = 0;
+ cache->block_entries = MEM_mallocN(sizeof(*cache->block_entries) * cache_size, __func__);
+
+ cache->misc_entries = BLI_ghash_ptr_new_ex(__func__, cache_size);
+ cache->misc_entries_indices = MEM_mallocN(sizeof(*cache->misc_entries_indices) * cache_size, __func__);
+ copy_vn_i(cache->misc_entries_indices, cache_size, -1);
+ cache->misc_cursor = 0;
+
+ /* XXX This assumes uint is 32 bits and uuid is 128 bits (char[16]), be careful! */
+ cache->uuids = BLI_ghash_new_ex(
+ BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__, cache_size * 2);
+
+ cache->size = cache_size;
+ cache->flags = FLC_IS_INIT;
+}
+
+static void filelist_cache_free(FileListEntryCache *cache)
+{
+ FileDirEntry *entry, *entry_next;
+
+ if (!(cache->flags & FLC_IS_INIT)) {
+ return;
+ }
+
+ filelist_cache_previews_free(cache, true);
+
+ MEM_freeN(cache->block_entries);
+
+ BLI_ghash_free(cache->misc_entries, NULL, NULL);
+ MEM_freeN(cache->misc_entries_indices);
+
+ BLI_ghash_free(cache->uuids, NULL, NULL);
+
+ for (entry = cache->cached_entries.first; entry; entry = entry_next) {
+ entry_next = entry->next;
+ filelist_entry_free(entry);
+ }
+ BLI_listbase_clear(&cache->cached_entries);
+}
+
+static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
+{
+ FileDirEntry *entry, *entry_next;
+
+ if (!(cache->flags & FLC_IS_INIT)) {
+ return;
+ }
+
+ filelist_cache_previews_clear(cache);
+
+ cache->block_cursor = cache->block_start_index = cache->block_center_index = cache->block_end_index = 0;
+ if (new_size != cache->size) {
+ cache->block_entries = MEM_reallocN(cache->block_entries, sizeof(*cache->block_entries) * new_size);
+ }
+
+ BLI_ghash_clear_ex(cache->misc_entries, NULL, NULL, new_size);
+ if (new_size != cache->size) {
+ cache->misc_entries_indices = MEM_reallocN(cache->misc_entries_indices,
+ sizeof(*cache->misc_entries_indices) * new_size);
+ }
+ copy_vn_i(cache->misc_entries_indices, new_size, -1);
+
+ BLI_ghash_clear_ex(cache->uuids, NULL, NULL, new_size * 2);
+
+ cache->size = new_size;
+
+ for (entry = cache->cached_entries.first; entry; entry = entry_next) {
+ entry_next = entry->next;
+ filelist_entry_free(entry);
+ }
+ BLI_listbase_clear(&cache->cached_entries);
+}
+
FileList *filelist_new(short type)
{
FileList *p = MEM_callocN(sizeof(*p), __func__);
+ filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT);
+
+ p->selection_state = BLI_ghash_new(BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__);
+
switch (type) {
case FILE_MAIN:
- p->readf = filelist_read_main;
+ p->checkdirf = filelist_checkdir_main;
+ p->read_jobf = filelist_readjob_main;
p->filterf = is_filtered_main;
break;
case FILE_LOADLIB:
- p->readf = filelist_read_library;
+ p->checkdirf = filelist_checkdir_lib;
+ p->read_jobf = filelist_readjob_lib;
p->filterf = is_filtered_lib;
break;
default:
- p->readf = filelist_read_dir;
+ p->checkdirf = filelist_checkdir_dir;
+ p->read_jobf = filelist_readjob_dir;
p->filterf = is_filtered_file;
break;
}
return p;
}
+void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
+{
+ if (!filelist) {
+ return;
+ }
+
+ filelist_filter_clear(filelist);
+
+ if (do_cache) {
+ filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
+ }
+
+ filelist_intern_free(&filelist->filelist_intern);
+
+ filelist_direntryarr_free(&filelist->filelist);
+
+ if (do_selection && filelist->selection_state) {
+ BLI_ghash_clear(filelist->selection_state, MEM_freeN, NULL);
+ }
+}
+
+void filelist_clear(struct FileList *filelist)
+{
+ filelist_clear_ex(filelist, true, true);
+}
+
void filelist_free(struct FileList *filelist)
{
if (!filelist) {
@@ -709,16 +1330,18 @@ void filelist_free(struct FileList *filelist)
return;
}
- MEM_SAFE_FREE(filelist->fidx);
- filelist->numfiltered = 0;
+ filelist_clear_ex(filelist, false, false); /* No need to clear cache & selection_state, we free them anyway. */
+ filelist_cache_free(&filelist->filelist_cache);
+
+ if (filelist->selection_state) {
+ BLI_ghash_free(filelist->selection_state, MEM_freeN, NULL);
+ filelist->selection_state = NULL;
+ }
+
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
- filelist->need_sorting = false;
+ filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
filelist->sort = FILE_SORT_NONE;
-
- BLI_filelist_free(filelist->filelist, filelist->numfiles, NULL);
- filelist->numfiles = 0;
- filelist->filelist = NULL;
}
void filelist_freelib(struct FileList *filelist)
@@ -733,49 +1356,191 @@ BlendHandle *filelist_lib(struct FileList *filelist)
return filelist->libfiledata;
}
-int filelist_numfiles(struct FileList *filelist)
+static const char *fileentry_uiname(const char *root, const char *relpath, const int typeflag, char *buff)
{
- return filelist->numfiltered;
+ char *name = NULL;
+
+ if (typeflag & FILE_TYPE_BLENDERLIB) {
+ char abspath[FILE_MAX_LIBEXTRA];
+ char *group;
+
+ BLI_join_dirfile(abspath, sizeof(abspath), root, relpath);
+ BLO_library_path_explode(abspath, buff, &group, &name);
+ if (!name) {
+ name = group;
+ }
+ }
+ /* Depending on platforms, 'my_file.blend/..' might be viewed as dir or not... */
+ if (!name) {
+ if (typeflag & FILE_TYPE_DIR) {
+ name = (char *)relpath;
+ }
+ else {
+ name = (char *)BLI_path_basename(relpath);
+ }
+ }
+ BLI_assert(name);
+
+ return name;
}
const char *filelist_dir(struct FileList *filelist)
{
- return filelist->dir;
+ return filelist->filelist.root;
}
-void filelist_setdir(struct FileList *filelist, const char *dir)
+/**
+ * May modify in place given r_dir, which is expected to be FILE_MAX_LIBEXTRA length.
+ */
+void filelist_setdir(struct FileList *filelist, char *r_dir)
{
- BLI_strncpy(filelist->dir, dir, sizeof(filelist->dir));
+ BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
+
+ BLI_cleanup_dir(G.main->name, r_dir);
+ filelist->checkdirf(filelist, r_dir);
+
+ if (!STREQ(filelist->filelist.root, r_dir)) {
+ BLI_strncpy(filelist->filelist.root, r_dir, sizeof(filelist->filelist.root));
+ filelist->flags |= FL_FORCE_RESET;
+ }
}
-short filelist_changed(struct FileList *filelist)
+void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
{
- return filelist->changed;
+ if (filelist->max_recursion != recursion_level) {
+ filelist->max_recursion = recursion_level;
+ filelist->flags |= FL_FORCE_RESET;
+ }
}
-struct direntry *filelist_file(struct FileList *filelist, int index)
+bool filelist_force_reset(struct FileList *filelist)
{
- int fidx = 0;
-
- if ((index < 0) || (index >= filelist->numfiltered)) {
+ return (filelist->flags & FL_FORCE_RESET) != 0;
+}
+
+bool filelist_is_ready(struct FileList *filelist)
+{
+ return (filelist->flags & FL_IS_READY) != 0;
+}
+
+bool filelist_pending(struct FileList *filelist)
+{
+ return (filelist->flags & FL_IS_PENDING) != 0;
+}
+
+/**
+ * Limited version of full update done by space_file's file_refresh(), to be used by operators and such.
+ * Ensures given filelist is ready to be used (i.e. it is filtered and sorted), unless it is tagged for a full refresh.
+ */
+int filelist_files_ensure(FileList *filelist)
+{
+ if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) {
+ filelist_sort(filelist);
+ filelist_filter(filelist);
+ }
+
+ return filelist->filelist.nbr_entries_filtered;;
+}
+
+static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int index)
+{
+ FileListInternEntry *entry = filelist->filelist_intern.filtered[index];
+ FileListEntryCache *cache = &filelist->filelist_cache;
+ FileDirEntry *ret;
+ FileDirEntryRevision *rev;
+
+ ret = MEM_callocN(sizeof(*ret), __func__);
+ rev = MEM_callocN(sizeof(*rev), __func__);
+
+ rev->size = (uint64_t)entry->st.st_size;
+
+ rev->time = (int64_t)entry->st.st_mtime;
+
+ ret->entry = rev;
+ ret->relpath = BLI_strdup(entry->relpath);
+ ret->name = BLI_strdup(entry->name);
+ ret->description = BLI_strdupcat(filelist->filelist.root, entry->relpath);
+ memcpy(ret->uuid, entry->uuid, sizeof(ret->uuid));
+ ret->blentype = entry->blentype;
+ ret->typeflag = entry->typeflag;
+
+ BLI_addtail(&cache->cached_entries, ret);
+ return ret;
+}
+
+static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry)
+{
+ BLI_remlink(&filelist->filelist_cache.cached_entries, entry);
+ filelist_entry_free(entry);
+}
+
+static FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const bool use_request)
+{
+ FileDirEntry *ret = NULL, *old;
+ FileListEntryCache *cache = &filelist->filelist_cache;
+ const size_t cache_size = cache->size;
+ int old_index;
+
+ if ((index < 0) || (index >= filelist->filelist.nbr_entries_filtered)) {
+ return ret;
+ }
+
+ if (index >= cache->block_start_index && index < cache->block_end_index) {
+ const int idx = (index - cache->block_start_index + cache->block_cursor) % cache_size;
+ return cache->block_entries[idx];
+ }
+
+ if ((ret = BLI_ghash_lookup(cache->misc_entries, SET_INT_IN_POINTER(index)))) {
+ return ret;
+ }
+
+ if (!use_request) {
return NULL;
}
- fidx = filelist->fidx[index];
- return &filelist->filelist[fidx];
+// printf("requesting file %d (not yet cached)\n", index);
+
+ /* Else, we have to add new entry to 'misc' cache - and possibly make room for it first! */
+ ret = filelist_file_create_entry(filelist, index);
+ old_index = cache->misc_entries_indices[cache->misc_cursor];
+ if ((old = BLI_ghash_popkey(cache->misc_entries, SET_INT_IN_POINTER(old_index), NULL))) {
+ BLI_ghash_remove(cache->uuids, old->uuid, NULL, NULL);
+ filelist_file_release_entry(filelist, old);
+ }
+ BLI_ghash_insert(cache->misc_entries, SET_INT_IN_POINTER(index), ret);
+ BLI_ghash_insert(cache->uuids, ret->uuid, ret);
+
+ cache->misc_entries_indices[cache->misc_cursor] = index;
+ cache->misc_cursor = (cache->misc_cursor + 1) % cache_size;
+
+#if 0 /* Actually no, only block cached entries should have preview imho. */
+ if (cache->previews_pool) {
+ filelist_cache_previews_push(filelist, ret, index);
+ }
+#endif
+
+ return ret;
+}
+
+FileDirEntry *filelist_file(struct FileList *filelist, int index)
+{
+ return filelist_file_ex(filelist, index, true);
}
-int filelist_find(struct FileList *filelist, const char *filename)
+int filelist_file_findpath(struct FileList *filelist, const char *filename)
{
int fidx = -1;
- if (!filelist->fidx)
+ if (filelist->filelist.nbr_entries_filtered < 0) {
return fidx;
+ }
- for (fidx = 0; fidx < filelist->numfiltered; fidx++) {
- int index = filelist->fidx[fidx];
+ /* XXX TODO Cache could probably use a ghash on paths too? Not really urgent though.
+ * This is only used to find again renamed entry, annoying but looks hairy to get rid of it currently. */
- if (STREQ(filelist->filelist[index].relname, filename)) {
+ for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+ FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
+ if (STREQ(entry->relpath, filename)) {
return fidx;
}
}
@@ -783,6 +1548,385 @@ int filelist_find(struct FileList *filelist, const char *filename)
return -1;
}
+FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4])
+{
+ if (filelist->filelist.nbr_entries_filtered < 0) {
+ return NULL;
+ }
+
+ if (filelist->filelist_cache.uuids) {
+ FileDirEntry *entry = BLI_ghash_lookup(filelist->filelist_cache.uuids, uuid);
+ if (entry) {
+ return entry;
+ }
+ }
+
+ {
+ int fidx;
+
+ for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+ FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
+ if (memcmp(entry->uuid, uuid, sizeof(entry->uuid)) == 0) {
+ return filelist_file(filelist, fidx);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void filelist_file_cache_slidingwindow_set(FileList *filelist, size_t window_size)
+{
+ /* Always keep it power of 2, in [256, 8192] range for now, cache being app. twice bigger than requested window. */
+ size_t size = 256;
+ window_size *= 2;
+
+ while (size < window_size && size < 8192) {
+ size *= 2;
+ }
+
+ if (size != filelist->filelist_cache.size) {
+ filelist_cache_clear(&filelist->filelist_cache, size);
+ }
+}
+
+/* Helpers, low-level, they assume cursor + size <= cache_size */
+static bool filelist_file_cache_block_create(FileList *filelist, const int start_index, const int size, int cursor)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+
+ {
+ int i, idx;
+
+ for (i = 0, idx = start_index; i < size; i++, idx++, cursor++) {
+ FileDirEntry *entry;
+
+ /* That entry might have already been requested and stored in misc cache... */
+ if ((entry = BLI_ghash_popkey(cache->misc_entries, SET_INT_IN_POINTER(idx), NULL)) == NULL) {
+ entry = filelist_file_create_entry(filelist, idx);
+ BLI_ghash_insert(cache->uuids, entry->uuid, entry);
+ }
+ cache->block_entries[cursor] = entry;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static void filelist_file_cache_block_release(struct FileList *filelist, const int size, int cursor)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+
+ {
+ int i;
+
+ for (i = 0; i < size; i++, cursor++) {
+ FileDirEntry *entry = cache->block_entries[cursor];
+// printf("%s: release cacheidx %d (%%p %%s)\n", __func__, cursor/*, cache->block_entries[cursor], cache->block_entries[cursor]->relpath*/);
+ BLI_ghash_remove(cache->uuids, entry->uuid, NULL, NULL);
+ filelist_file_release_entry(filelist, entry);
+#ifndef NDEBUG
+ cache->block_entries[cursor] = NULL;
+#endif
+ }
+ }
+}
+
+/* Load in cache all entries "around" given index (as much as block cache may hold). */
+bool filelist_file_cache_block(struct FileList *filelist, const int index)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+ const size_t cache_size = cache->size;
+
+ const int nbr_entries = filelist->filelist.nbr_entries_filtered;
+ int start_index = max_ii(0, index - (cache_size / 2));
+ int end_index = min_ii(nbr_entries, index + (cache_size / 2));
+ int i;
+
+ if ((index < 0) || (index >= nbr_entries)) {
+// printf("Wrong index %d ([%d:%d])", index, 0, nbr_entries);
+ return false;
+ }
+
+ /* Maximize cached range! */
+ if ((end_index - start_index) < cache_size) {
+ if (start_index == 0) {
+ end_index = min_ii(nbr_entries, start_index + cache_size);
+ }
+ else if (end_index == nbr_entries) {
+ start_index = max_ii(0, end_index - cache_size);
+ }
+ }
+
+ BLI_assert((end_index - start_index) <= cache_size) ;
+
+// printf("%s: [%d:%d] around index %d (current cache: [%d:%d])\n", __func__,
+// start_index, end_index, index, cache->block_start_index, cache->block_end_index);
+
+ /* If we have something to (re)cache... */
+ if ((start_index != cache->block_start_index) || (end_index != cache->block_end_index)) {
+ if ((start_index >= cache->block_end_index) || (end_index <= cache->block_start_index)) {
+ int size1 = cache->block_end_index - cache->block_start_index;
+ int size2 = 0;
+ int idx1 = cache->block_cursor, idx2 = 0;
+
+// printf("Full Recaching!\n");
+
+ if (cache->flags & FLC_PREVIEWS_ACTIVE) {
+ filelist_cache_previews_clear(cache);
+ }
+
+ if (idx1 + size1 > cache_size) {
+ size2 = idx1 + size1 - cache_size;
+ size1 -= size2;
+ filelist_file_cache_block_release(filelist, size2, idx2);
+ }
+ filelist_file_cache_block_release(filelist, size1, idx1);
+
+ cache->block_start_index = cache->block_end_index = cache->block_cursor = 0;
+
+ /* New cached block does not overlap existing one, simple. */
+ if (!filelist_file_cache_block_create(filelist, start_index, end_index - start_index, 0)) {
+ return false;
+ }
+
+ cache->block_start_index = start_index;
+ cache->block_end_index = end_index;
+ }
+ else {
+// printf("Partial Recaching!\n");
+
+ /* At this point, we know we keep part of currently cached entries, so update previews if needed,
+ * and remove everything from working queue - we'll add all newly needed entries at the end. */
+ if (cache->flags & FLC_PREVIEWS_ACTIVE) {
+ filelist_cache_previews_update(filelist);
+ filelist_cache_previews_clear(cache);
+ }
+
+// printf("\tpreview cleaned up...\n");
+
+ if (start_index > cache->block_start_index) {
+ int size1 = start_index - cache->block_start_index;
+ int size2 = 0;
+ int idx1 = cache->block_cursor, idx2 = 0;
+
+// printf("\tcache releasing: [%d:%d] (%d, %d)\n", cache->block_start_index, cache->block_start_index + size1, cache->block_cursor, size1);
+
+ if (idx1 + size1 > cache_size) {
+ size2 = idx1 + size1 - cache_size;
+ size1 -= size2;
+ filelist_file_cache_block_release(filelist, size2, idx2);
+ }
+ filelist_file_cache_block_release(filelist, size1, idx1);
+
+ cache->block_cursor = (idx1 + size1 + size2) % cache_size;
+ cache->block_start_index = start_index;
+ }
+ if (end_index < cache->block_end_index) {
+ int size1 = cache->block_end_index - end_index;
+ int size2 = 0;
+ int idx1, idx2 = 0;
+
+// printf("\tcache releasing: [%d:%d] (%d)\n", cache->block_end_index - size1, cache->block_end_index, cache->block_cursor);
+
+ idx1 = (cache->block_cursor + end_index - cache->block_start_index) % cache_size;
+ if (idx1 + size1 > cache_size) {
+ size2 = idx1 + size1 - cache_size;
+ size1 -= size2;
+ filelist_file_cache_block_release(filelist, size2, idx2);
+ }
+ filelist_file_cache_block_release(filelist, size1, idx1);
+
+ cache->block_end_index = end_index;
+ }
+
+// printf("\tcache cleaned up...\n");
+
+ if (start_index < cache->block_start_index) {
+ /* Add (request) needed entries before already cached ones. */
+ /* Note: We need some index black magic to wrap around (cycle) inside our cache_size array... */
+ int size1 = cache->block_start_index - start_index;
+ int size2 = 0;
+ int idx1, idx2;
+
+ if (size1 > cache->block_cursor) {
+ size2 = size1;
+ size1 -= cache->block_cursor;
+ size2 -= size1;
+ idx2 = 0;
+ idx1 = cache_size - size1;
+ }
+ else {
+ idx1 = cache->block_cursor - size1;
+ }
+
+ if (size2) {
+ if (!filelist_file_cache_block_create(filelist, start_index + size1, size2, idx2)) {
+ return false;
+ }
+ }
+ if (!filelist_file_cache_block_create(filelist, start_index, size1, idx1)) {
+ return false;
+ }
+
+ cache->block_cursor = idx1;
+ cache->block_start_index = start_index;
+ }
+// printf("\tstart-extended...\n");
+ if (end_index > cache->block_end_index) {
+ /* Add (request) needed entries after already cached ones. */
+ /* Note: We need some index black magic to wrap around (cycle) inside our cache_size array... */
+ int size1 = end_index - cache->block_end_index;
+ int size2 = 0;
+ int idx1, idx2;
+
+ idx1 = (cache->block_cursor + end_index - cache->block_start_index - size1) % cache_size;
+ if ((idx1 + size1) > cache_size) {
+ size2 = size1;
+ size1 = cache_size - idx1;
+ size2 -= size1;
+ idx2 = 0;
+ }
+
+ if (size2) {
+ if (!filelist_file_cache_block_create(filelist, end_index - size2, size2, idx2)) {
+ return false;
+ }
+ }
+ if (!filelist_file_cache_block_create(filelist, end_index - size1 - size2, size1, idx1)) {
+ return false;
+ }
+
+ cache->block_end_index = end_index;
+ }
+
+// printf("\tend-extended...\n");
+ }
+ }
+ else if ((cache->block_center_index != index) && (cache->flags & FLC_PREVIEWS_ACTIVE)) {
+ /* We try to always preview visible entries first, so 'restart' preview background task. */
+ filelist_cache_previews_update(filelist);
+ filelist_cache_previews_clear(cache);
+ }
+
+// printf("Re-queueing previews...\n");
+
+ /* Note we try to preview first images around given index - i.e. assumed visible ones. */
+ if (cache->flags & FLC_PREVIEWS_ACTIVE) {
+ for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) {
+ if ((index - i) >= start_index) {
+ const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size;
+ filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i);
+ }
+ if ((index + i) < end_index) {
+ const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size;
+ filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i);
+ }
+ }
+ }
+
+ cache->block_center_index = index;
+
+// printf("%s Finished!\n", __func__);
+
+ return true;
+}
+
+void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+
+ if (use_previews == ((cache->flags & FLC_PREVIEWS_ACTIVE) != 0)) {
+ return;
+ }
+ /* Do not start preview work while listing, gives nasty flickering! */
+ else if (use_previews && (filelist->flags & FL_IS_READY)) {
+ cache->flags |= FLC_PREVIEWS_ACTIVE;
+
+ BLI_assert((cache->previews_pool == NULL) && (cache->previews_todo == NULL) && (cache->previews_done == NULL));
+
+// printf("%s: Init Previews...\n", __func__);
+
+ /* No need to populate preview queue here, filelist_file_cache_block() handles this. */
+ }
+ else {
+// printf("%s: Clear Previews...\n", __func__);
+
+ filelist_cache_previews_free(cache, true);
+ }
+}
+
+bool filelist_cache_previews_update(FileList *filelist)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+ TaskPool *pool = cache->previews_pool;
+ bool changed = false;
+
+ if (!pool) {
+ return changed;
+ }
+
+// printf("%s: Update Previews...\n", __func__);
+
+ while (!BLI_thread_queue_is_empty(cache->previews_done)) {
+ FileListEntryPreview *preview = BLI_thread_queue_pop(cache->previews_done);
+ FileDirEntry *entry;
+
+ /* Paranoid (should never happen currently since we consume this queue from a single thread), but... */
+ if (!preview) {
+ continue;
+ }
+ /* entry might have been removed from cache in the mean while, we do not want to cache it again here. */
+ entry = filelist_file_ex(filelist, preview->index, false);
+
+// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+
+ if (preview->img) {
+ /* Due to asynchronous process, a preview for a given image may be generated several times, i.e.
+ * entry->image may already be set at this point. */
+ if (entry && !entry->image) {
+ entry->image = preview->img;
+ changed = true;
+ }
+ else {
+ IMB_freeImBuf(preview->img);
+ }
+ }
+ else if (entry) {
+ /* We want to avoid re-processing this entry continuously!
+ * Note that, since entries only live in cache, preview will be retried quite often anyway. */
+ entry->flags |= FILE_ENTRY_INVALID_PREVIEW;
+ }
+
+ MEM_freeN(preview);
+ cache->previews_pending--;
+ BLI_assert(cache->previews_pending >= 0);
+ }
+
+// printf("%s: Previews pending: %d\n", __func__, cache->previews_pending);
+ if (cache->previews_pending == 0) {
+ if (cache->previews_timestamp == 0.0) {
+ cache->previews_timestamp = PIL_check_seconds_timer();
+ }
+ else if (PIL_check_seconds_timer() - cache->previews_timestamp > 1.0) {
+ /* Preview task is IDLE since more than (approximatively) 1000ms,
+ * kill workers & task for now - they will be automatically restarted when needed. */
+// printf("%s: Inactive preview task, sleeping (%f vs %f)!\n", __func__, PIL_check_seconds_timer(), cache->previews_timestamp);
+ filelist_cache_previews_free(cache, false);
+ }
+ }
+
+ return changed;
+}
+
+bool filelist_cache_previews_running(FileList *filelist)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+
+ return (cache->previews_pool != NULL);
+}
+
/* would recognize .blend as well */
static bool file_is_blend_backup(const char *str)
{
@@ -857,10 +2001,10 @@ static int path_extension_type(const char *path)
return 0;
}
-static int file_extension_type(const char *dir, const char *relname)
+static int file_extension_type(const char *dir, const char *relpath)
{
char path[FILE_MAX];
- BLI_join_dirfile(path, sizeof(path), dir, relname);
+ BLI_join_dirfile(path, sizeof(path), dir, relpath);
return path_extension_type(path);
}
@@ -868,185 +2012,137 @@ int ED_file_extension_icon(const char *path)
{
int type = path_extension_type(path);
- if (type == FILE_TYPE_BLENDER)
- return ICON_FILE_BLEND;
- else if (type == FILE_TYPE_BLENDER_BACKUP)
- return ICON_FILE_BACKUP;
- else if (type == FILE_TYPE_IMAGE)
- return ICON_FILE_IMAGE;
- else if (type == FILE_TYPE_MOVIE)
- return ICON_FILE_MOVIE;
- else if (type == FILE_TYPE_PYSCRIPT)
- return ICON_FILE_SCRIPT;
- else if (type == FILE_TYPE_SOUND)
- return ICON_FILE_SOUND;
- else if (type == FILE_TYPE_FTFONT)
- return ICON_FILE_FONT;
- else if (type == FILE_TYPE_BTX)
- return ICON_FILE_BLANK;
- else if (type == FILE_TYPE_COLLADA)
- return ICON_FILE_BLANK;
- else if (type == FILE_TYPE_TEXT)
- return ICON_FILE_TEXT;
-
- return ICON_FILE_BLANK;
-}
-
-static void filelist_setfiletypes(struct FileList *filelist)
-{
- struct direntry *file;
- int num;
-
- file = filelist->filelist;
-
- for (num = 0; num < filelist->numfiles; num++, file++) {
-#ifndef __APPLE__
- /* Don't check extensions for directories, allow in OSX cause bundles have extensions*/
- if (file->type & S_IFDIR) {
- continue;
- }
-#endif
- if (filelist->filter_data.filter_glob[0] &&
- BLI_testextensie_glob(file->relname, filelist->filter_data.filter_glob))
- {
- file->flags = FILE_TYPE_OPERATOR;
- }
- else {
- file->flags = file_extension_type(filelist->dir, file->relname);
- }
+ switch (type) {
+ case FILE_TYPE_BLENDER:
+ return ICON_FILE_BLEND;
+ case FILE_TYPE_BLENDER_BACKUP:
+ return ICON_FILE_BACKUP;
+ case FILE_TYPE_IMAGE:
+ return ICON_FILE_IMAGE;
+ case FILE_TYPE_MOVIE:
+ return ICON_FILE_MOVIE;
+ case FILE_TYPE_PYSCRIPT:
+ return ICON_FILE_SCRIPT;
+ case FILE_TYPE_SOUND:
+ return ICON_FILE_SOUND;
+ case FILE_TYPE_FTFONT:
+ return ICON_FILE_FONT;
+ case FILE_TYPE_BTX:
+ return ICON_FILE_BLANK;
+ case FILE_TYPE_COLLADA:
+ return ICON_FILE_BLANK;
+ case FILE_TYPE_TEXT:
+ return ICON_FILE_TEXT;
+ default:
+ return ICON_FILE_BLANK;
}
}
-static void filelist_read_dir(struct FileList *filelist)
-{
- if (!filelist) return;
-
- filelist->fidx = NULL;
- filelist->filelist = NULL;
-
- BLI_make_exist(filelist->dir);
- BLI_cleanup_dir(G.main->name, filelist->dir);
- filelist->numfiles = BLI_filelist_dir_contents(filelist->dir, &(filelist->filelist));
-
- /* We shall *never* get an empty list here, since we now the dir exists and is readable
- * (ensured by BLI_make_exist()). So we expect at the very least the parent '..' entry. */
- BLI_assert(filelist->numfiles != 0);
-
- filelist_setfiletypes(filelist);
-}
-
-static void filelist_read_main(struct FileList *filelist)
+int filelist_empty(struct FileList *filelist)
{
- if (!filelist) return;
- filelist_from_main(filelist);
+ return (filelist->filelist.nbr_entries == 0);
}
-static void filelist_read_library(struct FileList *filelist)
+unsigned int filelist_entry_select_set(
+ const FileList *filelist, const FileDirEntry *entry, FileSelType select, unsigned int flag, FileCheckType check)
{
- if (!filelist) return;
- BLI_cleanup_dir(G.main->name, filelist->dir);
- filelist_from_library(filelist);
- if (!filelist->libfiledata) {
- int num;
- struct direntry *file;
+ /* Default NULL pointer if not found is fine here! */
+ void **es_p = BLI_ghash_lookup_p(filelist->selection_state, entry->uuid);
+ unsigned int entry_flag = es_p ? GET_UINT_FROM_POINTER(*es_p) : 0;
+ const unsigned int org_entry_flag = entry_flag;
- filelist_read_dir(filelist);
- file = filelist->filelist;
- for (num = 0; num < filelist->numfiles; num++, file++) {
- if (BLO_has_bfile_extension(file->relname)) {
- char name[FILE_MAX];
+ BLI_assert(entry);
+ BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL));
- BLI_join_dirfile(name, sizeof(name), filelist->dir, file->relname);
+ if (((check == CHECK_ALL)) ||
+ ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
+ ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR)))
+ {
+ switch (select) {
+ case FILE_SEL_REMOVE:
+ entry_flag &= ~flag;
+ break;
+ case FILE_SEL_ADD:
+ entry_flag |= flag;
+ break;
+ case FILE_SEL_TOGGLE:
+ entry_flag ^= flag;
+ break;
+ }
+ }
- /* prevent current file being used as acceptable dir */
- if (BLI_path_cmp(G.main->name, name) != 0) {
- file->type &= ~S_IFMT;
- file->type |= S_IFDIR;
- }
+ if (entry_flag != org_entry_flag) {
+ if (es_p) {
+ if (entry_flag) {
+ *es_p = SET_UINT_IN_POINTER(entry_flag);
+ }
+ else {
+ BLI_ghash_remove(filelist->selection_state, entry->uuid, MEM_freeN, NULL);
}
}
+ else if (entry_flag) {
+ void *key = MEM_mallocN(sizeof(entry->uuid), __func__);
+ memcpy(key, entry->uuid, sizeof(entry->uuid));
+ BLI_ghash_insert(filelist->selection_state, key, SET_UINT_IN_POINTER(entry_flag));
+ }
}
-}
-
-void filelist_readdir(struct FileList *filelist)
-{
- filelist->readf(filelist);
- filelist->need_sorting = true;
- filelist->need_thumbnails = true;
- filelist_filter_clear(filelist);
+ return entry_flag;
}
-int filelist_empty(struct FileList *filelist)
+void filelist_entry_select_index_set(FileList *filelist, const int index, FileSelType select, unsigned int flag, FileCheckType check)
{
- return filelist->filelist == NULL;
-}
+ FileDirEntry *entry = filelist_file(filelist, index);
-void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check)
-{
- struct direntry *file = filelist_file(filelist, index);
- if (file != NULL) {
- int check_ok = 0;
- switch (check) {
- case CHECK_DIRS:
- check_ok = S_ISDIR(file->type);
- break;
- case CHECK_ALL:
- check_ok = 1;
- break;
- case CHECK_FILES:
- default:
- check_ok = !S_ISDIR(file->type);
- break;
- }
- if (check_ok) {
- switch (select) {
- case FILE_SEL_REMOVE:
- file->selflag &= ~flag;
- break;
- case FILE_SEL_ADD:
- file->selflag |= flag;
- break;
- case FILE_SEL_TOGGLE:
- file->selflag ^= flag;
- break;
- }
- }
+ if (entry) {
+ filelist_entry_select_set(filelist, entry, select, flag, check);
}
}
-void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check)
+void filelist_entries_select_index_range_set(
+ FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check)
{
/* select all valid files between first and last indicated */
- if ((sel->first >= 0) && (sel->first < filelist->numfiltered) && (sel->last >= 0) && (sel->last < filelist->numfiltered)) {
+ if ((sel->first >= 0) && (sel->first < filelist->filelist.nbr_entries_filtered) &&
+ (sel->last >= 0) && (sel->last < filelist->filelist.nbr_entries_filtered))
+ {
int current_file;
for (current_file = sel->first; current_file <= sel->last; current_file++) {
- filelist_select_file(filelist, current_file, select, flag, check);
+ filelist_entry_select_index_set(filelist, current_file, select, flag, check);
}
}
}
-bool filelist_is_selected(struct FileList *filelist, int index, FileCheckType check)
+unsigned int filelist_entry_select_get(FileList *filelist, FileDirEntry *entry, FileCheckType check)
{
- struct direntry *file = filelist_file(filelist, index);
- if (!file) {
- return 0;
- }
- switch (check) {
- case CHECK_DIRS:
- return S_ISDIR(file->type) && (file->selflag & FILE_SEL_SELECTED);
- case CHECK_FILES:
- return S_ISREG(file->type) && (file->selflag & FILE_SEL_SELECTED);
- case CHECK_ALL:
- default:
- return (file->selflag & FILE_SEL_SELECTED) != 0;
+ BLI_assert(entry);
+ BLI_assert(ELEM(check, CHECK_DIRS, CHECK_FILES, CHECK_ALL));
+
+ if (((check == CHECK_ALL)) ||
+ ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
+ ((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR)))
+ {
+ /* Default NULL pointer if not found is fine here! */
+ return GET_UINT_FROM_POINTER(BLI_ghash_lookup(filelist->selection_state, entry->uuid));
}
+
+ return 0;
}
+unsigned int filelist_entry_select_index_get(FileList *filelist, const int index, FileCheckType check)
+{
+ FileDirEntry *entry = filelist_file(filelist, index);
+
+ if (entry) {
+ return filelist_entry_select_get(filelist, entry, check);
+ }
+
+ return 0;
+}
-bool filelist_islibrary(struct FileList *filelist, char *dir, char *group)
+bool filelist_islibrary(struct FileList *filelist, char *dir, char **group)
{
- return BLO_is_a_library(filelist->dir, dir, group);
+ return BLO_library_path_explode(filelist->filelist.root, dir, group, NULL);
}
static int groupname_to_code(const char *group)
@@ -1054,6 +2150,8 @@ static int groupname_to_code(const char *group)
char buf[BLO_GROUP_MAX];
char *lslash;
+ BLI_assert(group);
+
BLI_strncpy(buf, group, sizeof(buf));
lslash = (char *)BLI_last_slash(buf);
if (lslash)
@@ -1062,182 +2160,228 @@ static int groupname_to_code(const char *group)
return buf[0] ? BKE_idcode_from_name(buf) : 0;
}
-static void filelist_from_library(struct FileList *filelist)
+static unsigned int groupname_to_filter_id(const char *group)
{
- LinkNode *l, *names, *previews;
- struct ImBuf *ima;
- int ok, i, nprevs, nnames, idcode;
- char filename[FILE_MAX];
- char dir[FILE_MAX], group[BLO_GROUP_MAX];
+ int id_code = groupname_to_code(group);
+
+ return BKE_idcode_to_idfilter(id_code);
+}
+
+/**
+ * From here, we are in 'Job Context', i.e. have to be careful about sharing stuff between background working thread
+ * and main one (used by UI among other things).
+ */
+typedef struct TodoDir {
+ int level;
+ char *dir;
+} TodoDir;
+
+static int filelist_readjob_list_dir(
+ const char *root, ListBase *entries, const char *filter_glob,
+ const bool do_lib, const char *main_name, const bool skip_currpar)
+{
+ struct direntry *files;
+ int nbr_files, nbr_entries = 0;
+
+ nbr_files = BLI_filelist_dir_contents(root, &files);
+ if (files) {
+ int i = nbr_files;
+ while (i--) {
+ FileListInternEntry *entry;
+
+ if (skip_currpar && FILENAME_IS_CURRPAR(files[i].relname)) {
+ continue;
+ }
+
+ entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = MEM_dupallocN(files[i].relname);
+ entry->st = files[i].s;
+
+ /* Set file type. */
+ if (S_ISDIR(files[i].s.st_mode)) {
+ entry->typeflag = FILE_TYPE_DIR;
+ }
+ else if (do_lib && BLO_has_bfile_extension(entry->relpath)) {
+ /* If we are considering .blend files as libs, promote them to directory status. */
+ char name[FILE_MAX];
+
+ entry->typeflag = FILE_TYPE_BLENDER;
+
+ BLI_join_dirfile(name, sizeof(name), root, entry->relpath);
+
+ /* prevent current file being used as acceptable dir */
+ if (BLI_path_cmp(main_name, name) != 0) {
+ entry->typeflag |= FILE_TYPE_DIR;
+ }
+ }
+ /* Otherwise, do not check extensions for directories! */
+ else if (!(entry->typeflag & FILE_TYPE_DIR)) {
+ entry->typeflag = file_extension_type(root, entry->relpath);
+ if (filter_glob[0] && BLI_testextensie_glob(entry->relpath, filter_glob)) {
+ entry->typeflag |= FILE_TYPE_OPERATOR;
+ }
+ }
+
+ BLI_addtail(entries, entry);
+ nbr_entries++;
+ }
+ BLI_filelist_free(files, nbr_files);
+ }
+ return nbr_entries;
+}
+
+static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
+{
+ FileListInternEntry *entry;
+ LinkNode *ln, *names;
+ int i, nnames, idcode = 0, nbr_entries = 0;
+ char dir[FILE_MAX], *group;
+ bool ok;
+
+ struct BlendHandle *libfiledata = NULL;
/* name test */
- ok = filelist_islibrary(filelist, dir, group);
+ ok = BLO_library_path_explode(root, dir, &group, NULL);
if (!ok) {
- /* free */
- if (filelist->libfiledata) BLO_blendhandle_close(filelist->libfiledata);
- filelist->libfiledata = NULL;
- return;
+ return nbr_entries;
}
-
- BLI_strncpy(filename, G.main->name, sizeof(filename));
/* there we go */
- /* for the time being only read filedata when libfiledata==0 */
- if (filelist->libfiledata == NULL) {
- filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
- if (filelist->libfiledata == NULL) return;
+ libfiledata = BLO_blendhandle_from_file(dir, NULL);
+ if (libfiledata == NULL) {
+ return nbr_entries;
}
-
- idcode = groupname_to_code(group);
-
- /* memory for strings is passed into filelist[i].relname
- * and freed in freefilelist */
- if (idcode) {
- previews = BLO_blendhandle_get_previews(filelist->libfiledata, idcode, &nprevs);
- names = BLO_blendhandle_get_datablock_names(filelist->libfiledata, idcode, &nnames);
- /* ugh, no rewind, need to reopen */
- BLO_blendhandle_close(filelist->libfiledata);
- filelist->libfiledata = BLO_blendhandle_from_file(dir, NULL);
-
+
+ /* memory for strings is passed into filelist[i].entry->relpath and freed in filelist_entry_free. */
+ if (group) {
+ idcode = groupname_to_code(group);
+ names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames);
}
else {
- previews = NULL;
- nprevs = 0;
- names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata);
+ names = BLO_blendhandle_get_linkable_groups(libfiledata);
nnames = BLI_linklist_count(names);
}
- filelist->numfiles = nnames + 1;
- filelist->filelist = MEM_mallocN(filelist->numfiles * sizeof(*filelist->filelist), __func__);
- memset(filelist->filelist, 0, filelist->numfiles * sizeof(*filelist->filelist));
+ BLO_blendhandle_close(libfiledata);
- filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
- filelist->filelist[0].type |= S_IFDIR;
-
- for (i = 0, l = names; i < nnames; i++, l = l->next) {
- const char *blockname = l->link;
-
- filelist->filelist[i + 1].relname = BLI_strdup(blockname);
- if (idcode) {
- filelist->filelist[i + 1].type |= S_IFREG;
- }
- else {
- filelist->filelist[i + 1].type |= S_IFDIR;
- }
+ if (!skip_currpar) {
+ entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(FILENAME_PARENT);
+ entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR);
+ BLI_addtail(entries, entry);
+ nbr_entries++;
}
- if (previews && (nnames != nprevs)) {
- printf("filelist_from_library: error, found %d items, %d previews\n", nnames, nprevs);
- }
- else if (previews) {
- for (i = 0, l = previews; i < nnames; i++, l = l->next) {
- PreviewImage *img = l->link;
-
- if (img) {
- unsigned int w = img->w[ICON_SIZE_PREVIEW];
- unsigned int h = img->h[ICON_SIZE_PREVIEW];
- unsigned int *rect = img->rect[ICON_SIZE_PREVIEW];
+ for (i = 0, ln = names; i < nnames; i++, ln = ln->next) {
+ const char *blockname = ln->link;
- /* first allocate imbuf for copying preview into it */
- if (w > 0 && h > 0 && rect) {
- ima = IMB_allocImBuf(w, h, 32, IB_rect);
- memcpy(ima->rect, rect, w * h * sizeof(unsigned int));
- filelist->filelist[i + 1].image = ima;
- filelist->filelist[i + 1].flags = FILE_TYPE_IMAGE;
- }
- }
+ entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(blockname);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB;
+ if (!(group && idcode)) {
+ entry->typeflag |= FILE_TYPE_DIR;
+ entry->blentype = groupname_to_code(blockname);
+ }
+ else {
+ entry->blentype = idcode;
}
+ BLI_addtail(entries, entry);
+ nbr_entries++;
}
BLI_linklist_free(names, free);
- if (previews) {
- BLI_linklist_free(previews, BKE_previewimg_freefunc);
- }
- BLI_strncpy(G.main->name, filename, sizeof(filename)); /* prevent G.main->name to change */
+ return nbr_entries;
}
-static void filelist_from_main(struct FileList *filelist)
+#if 0
+/* Kept for reference here, in case we want to add back that feature later. We do not need it currently. */
+/* Code ***NOT*** updated for job stuff! */
+static void filelist_readjob_main_rec(struct FileList *filelist)
{
ID *id;
- struct direntry *files, *firstlib = NULL;
+ FileDirEntry *files, *firstlib = NULL;
ListBase *lb;
int a, fake, idcode, ok, totlib, totbl;
+
+ // filelist->type = FILE_MAIN; // XXX TODO: add modes to filebrowser
- // filelist->type = FILE_MAIN; // XXXXX TODO: add modes to filebrowser
+ BLI_assert(filelist->filelist.entries == NULL);
- if (filelist->dir[0] == '/') filelist->dir[0] = 0;
+ if (filelist->filelist.root[0] == '/') filelist->filelist.root[0] = '\0';
- if (filelist->dir[0]) {
- idcode = groupname_to_code(filelist->dir);
- if (idcode == 0) filelist->dir[0] = 0;
+ if (filelist->filelist.root[0]) {
+ idcode = groupname_to_code(filelist->filelist.root);
+ if (idcode == 0) filelist->filelist.root[0] = '\0';
}
if (filelist->dir[0] == 0) {
/* make directories */
#ifdef WITH_FREESTYLE
- filelist->numfiles = 24;
+ filelist->filelist.nbr_entries = 24;
#else
- filelist->numfiles = 23;
+ filelist->filelist.nbr_entries = 23;
#endif
- filelist->filelist = MEM_mallocN(sizeof(*filelist->filelist) * filelist->numfiles, __func__);
-
- for (a = 0; a < filelist->numfiles; a++) {
- memset(&(filelist->filelist[a]), 0, sizeof(struct direntry));
- filelist->filelist[a].type |= S_IFDIR;
- }
-
- filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
- filelist->filelist[1].relname = BLI_strdup("Scene");
- filelist->filelist[2].relname = BLI_strdup("Object");
- filelist->filelist[3].relname = BLI_strdup("Mesh");
- filelist->filelist[4].relname = BLI_strdup("Curve");
- filelist->filelist[5].relname = BLI_strdup("Metaball");
- filelist->filelist[6].relname = BLI_strdup("Material");
- filelist->filelist[7].relname = BLI_strdup("Texture");
- filelist->filelist[8].relname = BLI_strdup("Image");
- filelist->filelist[9].relname = BLI_strdup("Ika");
- filelist->filelist[10].relname = BLI_strdup("Wave");
- filelist->filelist[11].relname = BLI_strdup("Lattice");
- filelist->filelist[12].relname = BLI_strdup("Lamp");
- filelist->filelist[13].relname = BLI_strdup("Camera");
- filelist->filelist[14].relname = BLI_strdup("Ipo");
- filelist->filelist[15].relname = BLI_strdup("World");
- filelist->filelist[16].relname = BLI_strdup("Screen");
- filelist->filelist[17].relname = BLI_strdup("VFont");
- filelist->filelist[18].relname = BLI_strdup("Text");
- filelist->filelist[19].relname = BLI_strdup("Armature");
- filelist->filelist[20].relname = BLI_strdup("Action");
- filelist->filelist[21].relname = BLI_strdup("NodeTree");
- filelist->filelist[22].relname = BLI_strdup("Speaker");
+ filelist_resize(filelist, filelist->filelist.nbr_entries);
+
+ for (a = 0; a < filelist->filelist.nbr_entries; a++) {
+ filelist->filelist.entries[a].typeflag |= FILE_TYPE_DIR;
+ }
+
+ filelist->filelist.entries[0].entry->relpath = BLI_strdup(FILENAME_PARENT);
+ filelist->filelist.entries[1].entry->relpath = BLI_strdup("Scene");
+ filelist->filelist.entries[2].entry->relpath = BLI_strdup("Object");
+ filelist->filelist.entries[3].entry->relpath = BLI_strdup("Mesh");
+ filelist->filelist.entries[4].entry->relpath = BLI_strdup("Curve");
+ filelist->filelist.entries[5].entry->relpath = BLI_strdup("Metaball");
+ filelist->filelist.entries[6].entry->relpath = BLI_strdup("Material");
+ filelist->filelist.entries[7].entry->relpath = BLI_strdup("Texture");
+ filelist->filelist.entries[8].entry->relpath = BLI_strdup("Image");
+ filelist->filelist.entries[9].entry->relpath = BLI_strdup("Ika");
+ filelist->filelist.entries[10].entry->relpath = BLI_strdup("Wave");
+ filelist->filelist.entries[11].entry->relpath = BLI_strdup("Lattice");
+ filelist->filelist.entries[12].entry->relpath = BLI_strdup("Lamp");
+ filelist->filelist.entries[13].entry->relpath = BLI_strdup("Camera");
+ filelist->filelist.entries[14].entry->relpath = BLI_strdup("Ipo");
+ filelist->filelist.entries[15].entry->relpath = BLI_strdup("World");
+ filelist->filelist.entries[16].entry->relpath = BLI_strdup("Screen");
+ filelist->filelist.entries[17].entry->relpath = BLI_strdup("VFont");
+ filelist->filelist.entries[18].entry->relpath = BLI_strdup("Text");
+ filelist->filelist.entries[19].entry->relpath = BLI_strdup("Armature");
+ filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
+ filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
+ filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
#ifdef WITH_FREESTYLE
- filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle");
+ filelist->filelist.entries[23].entry->relpath = BLI_strdup("FreestyleLineStyle");
#endif
}
else {
/* make files */
- idcode = groupname_to_code(filelist->dir);
+ idcode = groupname_to_code(filelist->filelist.root);
lb = which_libbase(G.main, idcode);
if (lb == NULL) return;
- filelist->numfiles = 0;
+ filelist->filelist.nbr_entries = 0;
for (id = lb->first; id; id = id->next) {
- if (!filelist->filter_data.hide_dot || id->name[2] != '.') {
- filelist->numfiles++;
+ if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
+ filelist->filelist.nbr_entries++;
}
}
- /* XXXXX TODO: if databrowse F4 or append/link filelist->hide_parent has to be set */
- if (!filelist->filter_data.hide_parent) filelist->numfiles += 1;
- filelist->filelist = filelist->numfiles > 0 ? MEM_mallocN(sizeof(*filelist->filelist) * filelist->numfiles, __func__) : NULL;
+ /* XXX TODO: if databrowse F4 or append/link filelist->flags & FLF_HIDE_PARENT has to be set */
+ if (!(filelist->filter_data.flags & FLF_HIDE_PARENT))
+ filelist->filelist.nbr_entries++;
- files = filelist->filelist;
+ if (filelist->filelist.nbr_entries > 0) {
+ filelist_resize(filelist, filelist->filelist.nbr_entries);
+ }
- if (files && !filelist->filter_data.hide_parent) {
- memset(&(filelist->filelist[0]), 0, sizeof(struct direntry));
- filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
- filelist->filelist[0].type |= S_IFDIR;
+ files = filelist->filelist.entries;
+
+ if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
+ files->entry->relpath = BLI_strdup(FILENAME_PARENT);
+ files->typeflag |= FILE_TYPE_DIR;
files++;
}
@@ -1246,37 +2390,36 @@ static void filelist_from_main(struct FileList *filelist)
for (id = lb->first; id; id = id->next) {
ok = 1;
if (ok) {
- if (files && (!filelist->filter_data.hide_dot || id->name[2] != '.')) {
- memset(files, 0, sizeof(struct direntry));
+ if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
if (id->lib == NULL) {
- files->relname = BLI_strdup(id->name + 2);
+ files->entry->relpath = BLI_strdup(id->name + 2);
}
else {
char relname[FILE_MAX + (MAX_ID_NAME - 2) + 3];
BLI_snprintf(relname, sizeof(relname), "%s | %s", id->lib->name, id->name + 2);
- files->relname = BLI_strdup(relname);
+ files->entry->relpath = BLI_strdup(relname);
}
- files->type |= S_IFREG;
-#if 0 /* XXXXX TODO show the selection status of the objects */
+// files->type |= S_IFREG;
+#if 0 /* XXX TODO show the selection status of the objects */
if (!filelist->has_func) { /* F4 DATA BROWSE */
if (idcode == ID_OB) {
- if ( ((Object *)id)->flag & SELECT) files->selflag |= FILE_SEL_SELECTED;
+ if ( ((Object *)id)->flag & SELECT) files->entry->selflag |= FILE_SEL_SELECTED;
}
else if (idcode == ID_SCE) {
- if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->selflag |= FILE_SEL_SELECTED;
+ if ( ((Scene *)id)->r.scemode & R_BG_RENDER) files->entry->selflag |= FILE_SEL_SELECTED;
}
}
#endif
- files->nr = totbl + 1;
- files->poin = id;
+// files->entry->nr = totbl + 1;
+ files->entry->poin = id;
fake = id->flag & LIB_FAKEUSER;
if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO || idcode == ID_IM) {
- files->flags |= FILE_TYPE_IMAGE;
+ files->typeflag |= FILE_TYPE_IMAGE;
}
- if (id->lib && fake) BLI_snprintf(files->extra, sizeof(files->extra), "LF %d", id->us);
- else if (id->lib) BLI_snprintf(files->extra, sizeof(files->extra), "L %d", id->us);
- else if (fake) BLI_snprintf(files->extra, sizeof(files->extra), "F %d", id->us);
- else BLI_snprintf(files->extra, sizeof(files->extra), " %d", id->us);
+// if (id->lib && fake) BLI_snprintf(files->extra, sizeof(files->entry->extra), "LF %d", id->us);
+// else if (id->lib) BLI_snprintf(files->extra, sizeof(files->entry->extra), "L %d", id->us);
+// else if (fake) BLI_snprintf(files->extra, sizeof(files->entry->extra), "F %d", id->us);
+// else BLI_snprintf(files->extra, sizeof(files->entry->extra), " %d", id->us);
if (id->lib) {
if (totlib == 0) firstlib = files;
@@ -1291,152 +2434,279 @@ static void filelist_from_main(struct FileList *filelist)
/* only qsort of library blocks */
if (totlib > 1) {
- qsort(firstlib, totlib, sizeof(struct direntry), compare_name);
+ qsort(firstlib, totlib, sizeof(*files), compare_name);
}
}
}
+#endif
-/* ********** Thumbnails job ********** */
+static void filelist_readjob_do(
+ const bool do_lib,
+ FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock)
+{
+ ListBase entries = {0};
+ BLI_Stack *todo_dirs;
+ TodoDir *td_dir;
+ char dir[FILE_MAX_LIBEXTRA];
+ char filter_glob[64]; /* TODO should be define! */
+ const char *root = filelist->filelist.root;
+ const int max_recursion = filelist->max_recursion;
+ int nbr_done_dirs = 0, nbr_todo_dirs = 1;
-typedef struct ThumbnailJob {
- ListBase loadimages;
- ImBuf *static_icons_buffers[BIFICONID_LAST];
- const short *stop;
- const short *do_update;
- struct FileList *filelist;
- ReportList reports;
-} ThumbnailJob;
+// BLI_assert(filelist->filtered == NULL);
+ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && (filelist->filelist.nbr_entries == 0));
-bool filelist_need_thumbnails(FileList *filelist)
-{
- return filelist->need_thumbnails;
-}
+ todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
+ td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir->level = 1;
-static void thumbnail_joblist_free(ThumbnailJob *tj)
-{
- FileImage *limg = tj->loadimages.first;
-
- /* free the images not yet copied to the filelist -> these will get freed with the filelist */
- for (; limg; limg = limg->next) {
- if ((limg->img) && (!limg->done)) {
- IMB_freeImBuf(limg->img);
- }
- }
- BLI_freelistN(&tj->loadimages);
-}
+ BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
+ BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
-static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float *UNUSED(progress))
-{
- ThumbnailJob *tj = tjv;
- FileImage *limg = tj->loadimages.first;
+ BLI_cleanup_dir(main_name, dir);
+ td_dir->dir = BLI_strdup(dir);
- tj->stop = stop;
- tj->do_update = do_update;
+ while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
+ FileListInternEntry *entry;
+ int nbr_entries = 0;
+ bool is_lib = do_lib;
- while ((*stop == 0) && (limg)) {
- ThumbSource source = 0;
+ char *subdir;
+ int recursion_level;
+ bool skip_currpar;
- BLI_assert(limg->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
- FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP));
- if (limg->flags & FILE_TYPE_IMAGE) {
- source = THB_SOURCE_IMAGE;
+ td_dir = BLI_stack_peek(todo_dirs);
+ subdir = td_dir->dir;
+ recursion_level = td_dir->level;
+ skip_currpar = (recursion_level > 1);
+
+ BLI_stack_discard(todo_dirs);
+
+ if (do_lib) {
+ nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar);
}
- else if (limg->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
- source = THB_SOURCE_BLEND;
+ if (!nbr_entries) {
+ is_lib = false;
+ nbr_entries = filelist_readjob_list_dir(subdir, &entries, filter_glob, do_lib, main_name, skip_currpar);
}
- else if (limg->flags & FILE_TYPE_MOVIE) {
- source = THB_SOURCE_MOVIE;
+
+ for (entry = entries.first; entry; entry = entry->next) {
+ BLI_join_dirfile(dir, sizeof(dir), subdir, entry->relpath);
+ BLI_cleanup_file(root, dir);
+
+ /* Generate our entry uuid. Abusing uuid as an uint32, shall be more than enough here,
+ * things would crash way before we overflow that counter!
+ * Using an atomic operation to avoid having to lock thread...
+ * Note that we do not really need this here currently, since there is a single listing thread, but better
+ * remain consistent about threading! */
+ *((uint32_t *)entry->uuid) = atomic_add_uint32((uint32_t *)filelist->filelist_intern.curr_uuid, 1);
+
+ BLI_path_rel(dir, root);
+ /* Only thing we change in direntry here, so we need to free it first. */
+ MEM_freeN(entry->relpath);
+ entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' added by BLI_path_rel */
+ entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir));
+
+ /* Here we decide whether current filedirentry is to be listed too, or not. */
+ if (max_recursion && (is_lib || (recursion_level <= max_recursion))) {
+ if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) {
+ /* Skip... */
+ }
+ else if (!is_lib && (recursion_level >= max_recursion) &&
+ ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0))
+ {
+ /* Do not recurse in real directories in this case, only in .blend libs. */
+ }
+ else {
+ /* We have a directory we want to list, add it to todo list! */
+ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
+ BLI_cleanup_dir(main_name, dir);
+ td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir->level = recursion_level + 1;
+ td_dir->dir = BLI_strdup(dir);
+ nbr_todo_dirs++;
+ }
+ }
}
- else if (limg->flags & FILE_TYPE_FTFONT) {
- source = THB_SOURCE_FONT;
+
+ if (nbr_entries) {
+ BLI_mutex_lock(lock);
+
+ *do_update = true;
+
+ BLI_movelisttolist(&filelist->filelist.entries, &entries);
+ filelist->filelist.nbr_entries += nbr_entries;
+
+ BLI_mutex_unlock(lock);
}
- limg->img = IMB_thumb_manage(limg->path, THB_LARGE, source);
- *do_update = true;
- PIL_sleep_ms(10);
- limg = limg->next;
+
+ nbr_done_dirs++;
+ *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs;
+ MEM_freeN(subdir);
+ }
+
+ /* If we were interrupted by stop, stack may not be empty and we need to free pending dir paths. */
+ while (!BLI_stack_is_empty(todo_dirs)) {
+ td_dir = BLI_stack_peek(todo_dirs);
+ MEM_freeN(td_dir->dir);
+ BLI_stack_discard(todo_dirs);
}
+ BLI_stack_free(todo_dirs);
}
-static void thumbnails_update(void *tjv)
+static void filelist_readjob_dir(
+ FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock)
{
- ThumbnailJob *tj = tjv;
+ filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
+}
- if (tj->filelist && tj->filelist->filelist) {
- FileImage *limg = tj->loadimages.first;
- while (limg) {
- if (!limg->done && limg->img) {
- tj->filelist->filelist[limg->index].image = IMB_dupImBuf(limg->img);
- limg->done = true;
- IMB_freeImBuf(limg->img);
- limg->img = NULL;
- }
- limg = limg->next;
- }
- }
+static void filelist_readjob_lib(
+ FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock)
+{
+ filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
+}
+
+static void filelist_readjob_main(
+ FileList *filelist, const char *main_name, short *stop, short *do_update, float *progress, ThreadMutex *lock)
+{
+ /* TODO! */
+ filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock);
+}
+
+
+typedef struct FileListReadJob {
+ ThreadMutex lock;
+ char main_name[FILE_MAX];
+ struct FileList *filelist;
+ struct FileList *tmp_filelist; /* XXX We may use a simpler struct here... just a linked list and root path? */
+} FileListReadJob;
+
+static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress)
+{
+ FileListReadJob *flrj = flrjv;
+
+// printf("START filelist reading (%d files, main thread: %d)\n",
+// flrj->filelist->filelist.nbr_entries, BLI_thread_is_main());
+
+ BLI_mutex_lock(&flrj->lock);
+
+ BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist);
+
+ flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
+
+ BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries);
+ flrj->tmp_filelist->filelist.nbr_entries = 0;
+
+ flrj->tmp_filelist->filelist_intern.filtered = NULL;
+ BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
+ memset(flrj->tmp_filelist->filelist_intern.curr_uuid, 0, sizeof(flrj->tmp_filelist->filelist_intern.curr_uuid));
+
+ flrj->tmp_filelist->libfiledata = NULL;
+ memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
+ flrj->tmp_filelist->selection_state = NULL;
+
+ BLI_mutex_unlock(&flrj->lock);
+
+ flrj->tmp_filelist->read_jobf(flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock);
}
-static void thumbnails_endjob(void *tjv)
+static void filelist_readjob_update(void *flrjv)
{
- ThumbnailJob *tj = tjv;
+ FileListReadJob *flrj = flrjv;
+ FileListIntern *fl_intern = &flrj->filelist->filelist_intern;
+ ListBase new_entries = {NULL};
+ int nbr_entries, new_nbr_entries = 0;
+
+ BLI_movelisttolist(&new_entries, &fl_intern->entries);
+ nbr_entries = flrj->filelist->filelist.nbr_entries;
- if (!*tj->stop) {
- tj->filelist->need_thumbnails = false;
+ BLI_mutex_lock(&flrj->lock);
+
+ if (flrj->tmp_filelist->filelist.nbr_entries) {
+ /* We just move everything out of 'thread context' into final list. */
+ new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries;
+ BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries);
+ flrj->tmp_filelist->filelist.nbr_entries = 0;
+ }
+
+ BLI_mutex_unlock(&flrj->lock);
+
+ if (new_nbr_entries) {
+ /* Do not clear selection cache, we can assume already 'selected' uuids are still valid! */
+ filelist_clear_ex(flrj->filelist, true, false);
+
+ flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING);
}
+
+ /* if no new_nbr_entries, this is NOP */
+ BLI_movelisttolist(&fl_intern->entries, &new_entries);
+ flrj->filelist->filelist.nbr_entries = nbr_entries + new_nbr_entries;
}
-static void thumbnails_free(void *tjv)
+static void filelist_readjob_endjob(void *flrjv)
{
- ThumbnailJob *tj = tjv;
- thumbnail_joblist_free(tj);
- MEM_freeN(tj);
+ FileListReadJob *flrj = flrjv;
+
+ /* In case there would be some dangling update... */
+ filelist_readjob_update(flrjv);
+
+ flrj->filelist->flags &= ~FL_IS_PENDING;
+ flrj->filelist->flags |= FL_IS_READY;
}
+static void filelist_readjob_free(void *flrjv)
+{
+ FileListReadJob *flrj = flrjv;
+
+// printf("END filelist reading (%d files)\n", flrj->filelist->filelist.nbr_entries);
+
+ if (flrj->tmp_filelist) {
+ /* tmp_filelist shall never ever be filtered! */
+ BLI_assert(flrj->tmp_filelist->filelist.nbr_entries == 0);
+ BLI_assert(BLI_listbase_is_empty(&flrj->tmp_filelist->filelist.entries));
-void thumbnails_start(FileList *filelist, const bContext *C)
+ filelist_freelib(flrj->tmp_filelist);
+ filelist_free(flrj->tmp_filelist);
+ MEM_freeN(flrj->tmp_filelist);
+ }
+
+ BLI_mutex_end(&flrj->lock);
+
+ MEM_freeN(flrj);
+}
+
+void filelist_readjob_start(FileList *filelist, const bContext *C)
{
wmJob *wm_job;
- ThumbnailJob *tj;
- int idx;
+ FileListReadJob *flrj;
/* prepare job data */
- tj = MEM_callocN(sizeof(*tj), __func__);
- tj->filelist = filelist;
- for (idx = 0; idx < filelist->numfiles; idx++) {
- if (!filelist->filelist[idx].path) {
- continue;
- }
- if (!filelist->filelist[idx].image) {
- if (filelist->filelist[idx].flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
- FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))
- {
- FileImage *limg = MEM_callocN(sizeof(*limg), __func__);
- BLI_strncpy(limg->path, filelist->filelist[idx].path, sizeof(limg->path));
- limg->index = idx;
- limg->flags = filelist->filelist[idx].flags;
- BLI_addtail(&tj->loadimages, limg);
- }
- }
- }
+ flrj = MEM_callocN(sizeof(*flrj), __func__);
+ flrj->filelist = filelist;
+ BLI_strncpy(flrj->main_name, G.main->name, sizeof(flrj->main_name));
+
+ filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
+ filelist->flags |= FL_IS_PENDING;
- BKE_reports_init(&tj->reports, RPT_PRINT);
+ BLI_mutex_init(&flrj->lock);
/* setup job */
- wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), filelist, "Thumbnails",
- 0, WM_JOB_TYPE_FILESEL_THUMBNAIL);
- WM_jobs_customdata_set(wm_job, tj, thumbnails_free);
- WM_jobs_timer(wm_job, 0.5, NC_WINDOW, NC_WINDOW);
- WM_jobs_callbacks(wm_job, thumbnails_startjob, NULL, thumbnails_update, thumbnails_endjob);
+ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_area(C), "Listing Dirs...",
+ WM_JOB_PROGRESS, WM_JOB_TYPE_FILESEL_READDIR);
+ WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free);
+ WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST);
+ WM_jobs_callbacks(wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);
/* start the job */
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
-void thumbnails_stop(wmWindowManager *wm, FileList *filelist)
+void filelist_readjob_stop(wmWindowManager *wm, ScrArea *sa)
{
- WM_jobs_kill_type(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);
+ WM_jobs_kill_type(wm, sa, WM_JOB_TYPE_FILESEL_READDIR);
}
-int thumbnails_running(wmWindowManager *wm, FileList *filelist)
+int filelist_readjob_running(wmWindowManager *wm, ScrArea *sa)
{
- return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_THUMBNAIL);
+ return WM_jobs_test(wm, sa, WM_JOB_TYPE_FILESEL_READDIR);
}