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:
authorBastien Montagne <montagne29@wanadoo.fr>2015-08-19 23:41:39 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2015-08-19 23:41:39 +0300
commitf69e9681fa32f6f9baafd2aa4a70427864ce6bb5 (patch)
tree7f2712d73f7e45ae2df1b8d361ce7fb6ae80fbb9
parent5c659574e643ff825ec02bafad7d7909fb287b4a (diff)
Final 'FileBrowser First Stage' merge.
It basically rewrites most of filelist.c, with some more limited changes in other areas of filebrowser. From user perspective, it: * Removes some info in 'long' drawing mode (owner, permissions) - OS-specific data that do not really matter in Blender! * Makes short/long display 'fixed' size (among four choices, like thumbnails mode). * Allows to list several layers of dirtree at once, in a flat way (inside .blend files and/or real directories). * Consequently, adds datablocks types filtering. * Uses way less RAM when listing big directories, especially in thumbnail mode (we are talking of several hundred of MiB spared). * Generates thumbnails way faster. From code perspective, it: * Is ready for asset engine needs (on data structure level in filebrowser's listing). * Simplifies and makes 'generic' file listing much lighter. * Separates file listing in three different aspects: ** 'generic' filelisting (in BLI), which becomes a shallow wrapper around stat struct. ** 'filebrowser drawing' filelisting, which only contains current visible subset of the whole list (sliding window), with extra drawing data (strings for size, date/time, preview, etc.). ** 'asset-ready' filelisting, which is used for operations common to 'basic' filehandling and future asset-related one. * Uses uuid's to handle file selection/state in the browser, instead of using flags in filelisting items. * Uses much lighter BLI_task handling for previews, instead of heavy 'job' system (using the new 'notifier' timer to handle UI refresh, in similar way to jobs). * Moves .blend datablocks preview handling to IMB_thumbnail (necessary to avoid storing all datablock previews at once, and gives better consistency and performances too). Revision: https://developer.blender.org/D1316 Thanks to Campbell & Sergey for the reviews. :)
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py42
-rw-r--r--source/blender/blenlib/BLI_fileops.h16
-rw-r--r--source/blender/blenlib/BLI_fileops_types.h19
-rw-r--r--source/blender/blenlib/intern/BLI_filelist.c264
-rw-r--r--source/blender/editors/include/ED_fileselect.h14
-rw-r--r--source/blender/editors/interface/interface_icons.c2
-rw-r--r--source/blender/editors/interface/interface_templates.c12
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_file/SConscript1
-rw-r--r--source/blender/editors/space_file/file_draw.c205
-rw-r--r--source/blender/editors/space_file/file_intern.h2
-rw-r--r--source/blender/editors/space_file/file_ops.c177
-rw-r--r--source/blender/editors/space_file/filelist.c2404
-rw-r--r--source/blender/editors/space_file/filelist.h48
-rw-r--r--source/blender/editors/space_file/filesel.c88
-rw-r--r--source/blender/editors/space_file/space_file.c69
-rw-r--r--source/blender/makesdna/DNA_space_types.h125
-rw-r--r--source/blender/makesrna/intern/rna_space.c126
-rw-r--r--source/blender/windowmanager/WM_api.h2
-rw-r--r--source/blender/windowmanager/WM_types.h1
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c192
21 files changed, 2743 insertions, 1067 deletions
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 0d900a41f25..0b9c8474dd9 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -40,22 +40,23 @@ class FILEBROWSER_HT_header(Header):
row.operator("file.parent", text="", icon='FILE_PARENT')
row.operator("file.refresh", text="", icon='FILE_REFRESH')
- row = layout.row()
- row.separator()
-
- row = layout.row(align=True)
+ layout.separator()
layout.operator_context = 'EXEC_DEFAULT'
- row.operator("file.directory_new", icon='NEWFOLDER')
+ layout.operator("file.directory_new", icon='NEWFOLDER', text="")
+ layout.separator()
layout.operator_context = 'INVOKE_DEFAULT'
params = st.params
# can be None when save/reload with a file selector open
if params:
+ is_lib_browser = params.use_library_browsing
+
+ layout.prop(params, "recursion_level", text="")
+
layout.prop(params, "display_type", expand=True, text="")
- if params.display_type == 'FILE_IMGDISPLAY':
- layout.prop(params, "thumbnail_size", text="")
+ layout.prop(params, "thumbnail_size", text="")
layout.prop(params, "sort_method", expand=True, text="")
@@ -81,9 +82,17 @@ class FILEBROWSER_HT_header(Header):
row.prop(params, "use_filter_sound", text="")
row.prop(params, "use_filter_text", text="")
+ if is_lib_browser:
+ row.prop(params, "use_filter_blendid", text="")
+ if params.use_filter_blendid:
+ row.separator()
+ row.prop(params, "filter_id_category", text="")
+
row.separator()
row.prop(params, "filter_search", text="", icon='VIEWZOOM')
+ layout.template_running_jobs()
+
class FILEBROWSER_UL_dir(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
@@ -214,5 +223,24 @@ class FILEBROWSER_PT_recent_folders(Panel):
col.operator("file.reset_recent", icon='X', text="")
+class FILEBROWSER_PT_advanced_filter(Panel):
+ bl_space_type = 'FILE_BROWSER'
+ bl_region_type = 'TOOLS'
+ bl_category = "Filter"
+ bl_label = "Advanced Filter"
+
+ def draw(self, context):
+ layout = self.layout
+ space = context.space_data
+ params = space.params
+
+ if params and params.use_library_browsing:
+ layout.prop(params, "use_filter_blendid")
+ if params.use_filter_blendid:
+ layout.separator()
+ col = layout.column()
+ col.prop(params, "filter_id")
+
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 93842731ab7..ba59c412c3a 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -91,11 +91,19 @@ char *BLI_current_working_dir(char *dir, const size_t maxlen) ATTR_NONNULL();
/* Filelist */
-unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **filelist);
+unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **r_filelist);
+void BLI_filelist_entry_duplicate(struct direntry *dst, const struct direntry *src);
void BLI_filelist_duplicate(
- struct direntry **dest_filelist, struct direntry *src_filelist, unsigned int nrentries,
- void *(*dup_poin)(void *));
-void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (*free_poin)(void *));
+ struct direntry **dest_filelist, struct direntry * const src_filelist, const unsigned int nrentries);
+void BLI_filelist_entry_free(struct direntry *entry);
+void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries);
+
+void BLI_filelist_entry_size_to_string(const struct stat *st, const uint64_t sz, const bool compact, char r_size[]);
+void BLI_filelist_entry_mode_to_string(
+ const struct stat *st, const bool compact, char r_mode1[], char r_mode2[], char r_mode3[]);
+void BLI_filelist_entry_owner_to_string(const struct stat *st, const bool compact, char r_owner[]);
+void BLI_filelist_entry_datetime_to_string(
+ const struct stat *st, const int64_t ts, const bool compact, char r_time[], char r_date[]);
/* Files */
diff --git a/source/blender/blenlib/BLI_fileops_types.h b/source/blender/blenlib/BLI_fileops_types.h
index 5a1126b05d2..0cf8c8ddb4a 100644
--- a/source/blender/blenlib/BLI_fileops_types.h
+++ b/source/blender/blenlib/BLI_fileops_types.h
@@ -39,7 +39,11 @@
typedef unsigned int mode_t;
#endif
-struct ImBuf;
+#define FILELIST_DIRENTRY_SIZE_LEN 16
+#define FILELIST_DIRENTRY_MODE_LEN 4
+#define FILELIST_DIRENTRY_OWNER_LEN 16
+#define FILELIST_DIRENTRY_TIME_LEN 8
+#define FILELIST_DIRENTRY_DATE_LEN 16
struct direntry {
mode_t type;
@@ -56,19 +60,6 @@ struct direntry {
#else
struct stat s;
#endif
- unsigned int flags;
- char size[16];
- char mode1[4];
- char mode2[4];
- char mode3[4];
- char owner[16];
- char time[8];
- char date[16];
- char extra[16];
- void *poin;
- int nr;
- struct ImBuf *image;
- unsigned int selflag; /* selection flag */
};
struct dirlink {
diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c
index 38a6781c159..62690ff4f3c 100644
--- a/source/blender/blenlib/intern/BLI_filelist.c
+++ b/source/blender/blenlib/intern/BLI_filelist.c
@@ -182,7 +182,6 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname)
/* Hack around for UNC paths on windows - does not support stat on '\\SERVER\foo\..', sigh... */
file->type |= S_IFDIR;
}
- file->flags = 0;
dir_ctx->nrfiles++;
file++;
dlink = dlink->next;
@@ -210,130 +209,160 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname)
}
/**
- * Fills in the "mode[123]", "size" and "string" fields in the elements of the files
- * array with descriptive details about each item. "string" will have a format similar to "ls -l".
+ * Scans the contents of the directory named *dirname, and allocates and fills in an
+ * array of entries describing them in *filelist.
+ *
+ * \return The length of filelist array.
*/
-static void bli_adddirstrings(struct BuildDirCtx *dir_ctx)
+unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
{
- const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
- /* symbolic display, indexed by mode field value */
- int num;
- double size;
- struct direntry *file;
- struct tm *tm;
- time_t zero = 0;
-
-#ifndef WIN32
- int mode;
-#endif
-
- for (num = 0, file = dir_ctx->files; num < dir_ctx->nrfiles; num++, file++) {
+ struct BuildDirCtx dir_ctx;
- /* Mode */
-#ifdef WIN32
- BLI_strncpy(file->mode1, types[0], sizeof(file->mode1));
- BLI_strncpy(file->mode2, types[0], sizeof(file->mode2));
- BLI_strncpy(file->mode3, types[0], sizeof(file->mode3));
-#else
- mode = file->s.st_mode;
+ dir_ctx.nrfiles = 0;
+ dir_ctx.files = NULL;
- BLI_strncpy(file->mode1, types[(mode & 0700) >> 6], sizeof(file->mode1));
- BLI_strncpy(file->mode2, types[(mode & 0070) >> 3], sizeof(file->mode2));
- BLI_strncpy(file->mode3, types[(mode & 0007)], sizeof(file->mode3));
+ bli_builddir(&dir_ctx, dirname);
- if (((mode & S_ISGID) == S_ISGID) && (file->mode2[2] == '-')) file->mode2[2] = 'l';
+ if (dir_ctx.files) {
+ *r_filelist = dir_ctx.files;
+ }
+ else {
+ // keep blender happy. Blender stores this in a variable
+ // where 0 has special meaning.....
+ *r_filelist = MEM_mallocN(sizeof(**r_filelist), __func__);
+ }
- if (mode & (S_ISUID | S_ISGID)) {
- if (file->mode1[2] == 'x') file->mode1[2] = 's';
- else file->mode1[2] = 'S';
+ return dir_ctx.nrfiles;
+}
- if (file->mode2[2] == 'x') file->mode2[2] = 's';
- }
+/**
+ * Convert given entry's size into human-readable strings.
+ *
+ */
+void BLI_filelist_entry_size_to_string(
+ const struct stat *st, const uint64_t sz, const bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN])
+{
+ double size;
+ const char *fmt;
+ const char *units[] = {"KiB", "MiB", "GiB", "TiB", NULL};
+ const char *units_compact[] = {"K", "M", "G", "T", NULL};
+ const char *unit = "B";
+
+ /*
+ * Seems st_size is signed 32-bit value in *nix and Windows. This
+ * will buy us some time until files get bigger than 4GB or until
+ * everyone starts using __USE_FILE_OFFSET64 or equivalent.
+ */
+ size = (double)(st ? st->st_size : sz);
+
+ if (size > 1024.0) {
+ const char **u;
+ for (u = compact ? units_compact : units, size /= 1024.0; size > 1024.0 && *(u + 1); u++, size /= 1024.0);
+ fmt = size > 100.0 ? "%.0f %s" : (size > 10.0 ? "%.1f %s" : "%.2f %s");
+ unit = *u;
+ }
+ else {
+ fmt = "%.0f %s";
+ }
- if (mode & S_ISVTX) {
- if (file->mode3[2] == 'x') file->mode3[2] = 't';
- else file->mode3[2] = 'T';
- }
-#endif
+ BLI_snprintf(r_size, sizeof(*r_size) * FILELIST_DIRENTRY_SIZE_LEN, fmt, size, unit);
+}
+/**
+ * Convert given entry's modes into human-readable strings.
+ *
+ */
+void BLI_filelist_entry_mode_to_string(
+ const struct stat *st, const bool UNUSED(compact), char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
+ char r_mode2[FILELIST_DIRENTRY_MODE_LEN], char r_mode3[FILELIST_DIRENTRY_MODE_LEN])
+{
+ const char *types[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
- /* User */
#ifdef WIN32
- strcpy(file->owner, "user");
+ BLI_strncpy(r_mode1, types[0], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
+ BLI_strncpy(r_mode2, types[0], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
+ BLI_strncpy(r_mode3, types[0], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
#else
- {
- struct passwd *pwuser;
- pwuser = getpwuid(file->s.st_uid);
- if (pwuser) {
- BLI_strncpy(file->owner, pwuser->pw_name, sizeof(file->owner));
- }
- else {
- BLI_snprintf(file->owner, sizeof(file->owner), "%u", file->s.st_uid);
- }
- }
-#endif
+ const int mode = st->st_mode;
+ BLI_strncpy(r_mode1, types[(mode & 0700) >> 6], sizeof(*r_mode1) * FILELIST_DIRENTRY_MODE_LEN);
+ BLI_strncpy(r_mode2, types[(mode & 0070) >> 3], sizeof(*r_mode2) * FILELIST_DIRENTRY_MODE_LEN);
+ BLI_strncpy(r_mode3, types[(mode & 0007)], sizeof(*r_mode3) * FILELIST_DIRENTRY_MODE_LEN);
- /* Time */
- tm = localtime(&file->s.st_mtime);
- // prevent impossible dates in windows
- if (tm == NULL) tm = localtime(&zero);
- strftime(file->time, sizeof(file->time), "%H:%M", tm);
- strftime(file->date, sizeof(file->date), "%d-%b-%y", tm);
+ if (((mode & S_ISGID) == S_ISGID) && (r_mode2[2] == '-')) r_mode2[2] = 'l';
+ if (mode & (S_ISUID | S_ISGID)) {
+ if (r_mode1[2] == 'x') r_mode1[2] = 's';
+ else r_mode1[2] = 'S';
- /* Size */
- /*
- * Seems st_size is signed 32-bit value in *nix and Windows. This
- * will buy us some time until files get bigger than 4GB or until
- * everyone starts using __USE_FILE_OFFSET64 or equivalent.
- */
- size = (double)file->s.st_size;
+ if (r_mode2[2] == 'x') r_mode2[2] = 's';
+ }
- if (size > 1024.0 * 1024.0 * 1024.0 * 1024.0) {
- BLI_snprintf(file->size, sizeof(file->size), "%.1f TiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0));
- }
- else if (size > 1024.0 * 1024.0 * 1024.0) {
- BLI_snprintf(file->size, sizeof(file->size), "%.1f GiB", size / (1024.0 * 1024.0 * 1024.0));
- }
- else if (size > 1024.0 * 1024.0) {
- BLI_snprintf(file->size, sizeof(file->size), "%.1f MiB", size / (1024.0 * 1024.0));
- }
- else if (size > 1024.0) {
- BLI_snprintf(file->size, sizeof(file->size), "%.1f KiB", size / 1024.0);
- }
- else {
- BLI_snprintf(file->size, sizeof(file->size), "%d B", (int)size);
- }
+ if (mode & S_ISVTX) {
+ if (r_mode3[2] == 'x') r_mode3[2] = 't';
+ else r_mode3[2] = 'T';
}
+#endif
}
/**
- * Scans the contents of the directory named *dirname, and allocates and fills in an
- * array of entries describing them in *filelist.
+ * Convert given entry's owner into human-readable strings.
*
- * \return The length of filelist array.
*/
-unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **filelist)
+void BLI_filelist_entry_owner_to_string(
+ const struct stat *st, const bool UNUSED(compact), char r_owner[FILELIST_DIRENTRY_OWNER_LEN])
{
- struct BuildDirCtx dir_ctx;
+#ifdef WIN32
+ strcpy(r_owner, "unknown");
+#else
+ struct passwd *pwuser = getpwuid(st->st_uid);
- dir_ctx.nrfiles = 0;
- dir_ctx.files = NULL;
+ if (pwuser) {
+ BLI_strncpy(r_owner, pwuser->pw_name, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN);
+ }
+ else {
+ BLI_snprintf(r_owner, sizeof(*r_owner) * FILELIST_DIRENTRY_OWNER_LEN, "%u", st->st_uid);
+ }
+#endif
+}
- bli_builddir(&dir_ctx, dirname);
- bli_adddirstrings(&dir_ctx);
+/**
+ * Convert given entry's time into human-readable strings.
+ */
+void BLI_filelist_entry_datetime_to_string(
+ const struct stat *st, const int64_t ts, const bool compact,
+ char r_time[FILELIST_DIRENTRY_TIME_LEN], char r_date[FILELIST_DIRENTRY_DATE_LEN])
+{
+ const struct tm *tm = localtime(st ? &st->st_mtime : &ts);
+ const time_t zero = 0;
- if (dir_ctx.files) {
- *filelist = dir_ctx.files;
+ /* Prevent impossible dates in windows. */
+ if (tm == NULL) {
+ tm = localtime(&zero);
}
- else {
- // keep blender happy. Blender stores this in a variable
- // where 0 has special meaning.....
- *filelist = MEM_mallocN(sizeof(**filelist), __func__);
+
+ if (r_time) {
+ strftime(r_time, sizeof(*r_time) * FILELIST_DIRENTRY_TIME_LEN, "%H:%M", tm);
+ }
+ if (r_date) {
+ strftime(r_date, sizeof(*r_date) * FILELIST_DIRENTRY_DATE_LEN, compact ? "%d/%m/%y" : "%d-%b-%y", tm);
}
+}
- return dir_ctx.nrfiles;
+/**
+ * Deep-duplicate of a single direntry.
+ *
+ * \param dup_poin If given, called for each non-NULL direntry->poin. Otherwise, pointer is always simply copied over.
+ */
+void BLI_filelist_entry_duplicate(struct direntry *dst, const struct direntry *src)
+{
+ *dst = *src;
+ if (dst->relname) {
+ dst->relname = MEM_dupallocN(src->relname);
+ }
+ if (dst->path) {
+ dst->path = MEM_dupallocN(src->path);
+ }
}
/**
@@ -342,48 +371,39 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **f
* \param dup_poin If given, called for each non-NULL direntry->poin. Otherwise, pointer is always simply copied over.
*/
void BLI_filelist_duplicate(
- struct direntry **dest_filelist, struct direntry *src_filelist, unsigned int nrentries,
- void *(*dup_poin)(void *))
+ struct direntry **dest_filelist, struct direntry * const src_filelist, const unsigned int nrentries)
{
unsigned int i;
*dest_filelist = MEM_mallocN(sizeof(**dest_filelist) * (size_t)(nrentries), __func__);
for (i = 0; i < nrentries; ++i) {
struct direntry * const src = &src_filelist[i];
- struct direntry *dest = &(*dest_filelist)[i];
- *dest = *src;
- if (dest->image) {
- dest->image = IMB_dupImBuf(src->image);
- }
- if (dest->relname) {
- dest->relname = MEM_dupallocN(src->relname);
- }
- if (dest->path) {
- dest->path = MEM_dupallocN(src->path);
- }
- if (dest->poin && dup_poin) {
- dest->poin = dup_poin(src->poin);
- }
+ struct direntry *dst = &(*dest_filelist)[i];
+ BLI_filelist_entry_duplicate(dst, src);
+ }
+}
+
+/**
+ * frees storage for a single direntry, not the direntry itself.
+ */
+void BLI_filelist_entry_free(struct direntry *entry)
+{
+ if (entry->relname) {
+ MEM_freeN((void *)entry->relname);
+ }
+ if (entry->path) {
+ MEM_freeN((void *)entry->path);
}
}
/**
* frees storage for an array of direntries, including the array itself.
*/
-void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (*free_poin)(void *))
+void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries)
{
unsigned int i;
for (i = 0; i < nrentries; ++i) {
- struct direntry *entry = filelist + i;
- if (entry->image) {
- IMB_freeImBuf(entry->image);
- }
- if (entry->relname)
- MEM_freeN((void *)entry->relname);
- if (entry->path)
- MEM_freeN((void *)entry->path);
- if (entry->poin && free_poin)
- free_poin(entry->poin);
+ BLI_filelist_entry_free(&filelist[i]);
}
if (filelist != NULL) {
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index b81ea55cca8..186a2a26825 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -33,6 +33,7 @@
struct ARegion;
struct FileSelectParams;
+struct ScrArea;
struct SpaceFile;
struct bContext;
struct wmWindowManager;
@@ -40,17 +41,13 @@ struct wmWindowManager;
#define FILE_LAYOUT_HOR 1
#define FILE_LAYOUT_VER 2
-#define MAX_FILE_COLUMN 8
+#define MAX_FILE_COLUMN 4
typedef enum FileListColumns {
COLUMN_NAME = 0,
COLUMN_DATE,
COLUMN_TIME,
COLUMN_SIZE,
- COLUMN_MODE1,
- COLUMN_MODE2,
- COLUMN_MODE3,
- COLUMN_OWNER
} FileListColumns;
typedef struct FileLayout {
@@ -71,6 +68,9 @@ typedef struct FileLayout {
int dirty;
int textheight;
float column_widths[MAX_FILE_COLUMN];
+
+ /* When we change display size, we may have to update static strings like size of files... */
+ short curr_size;
} FileLayout;
typedef struct FileSelection {
@@ -100,9 +100,9 @@ void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y);
void ED_operatormacros_file(void);
-void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile);
+void ED_fileselect_clear(struct wmWindowManager *wm, struct ScrArea *sa, struct SpaceFile *sfile);
-void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile);
+void ED_fileselect_exit(struct wmWindowManager *wm, struct ScrArea *sa, struct SpaceFile *sfile);
int ED_file_extension_icon(const char *relname);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 95139c5656e..5ef3b69c851 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -757,7 +757,7 @@ static void init_iconfile_list(struct ListBase *list)
}
}
- BLI_filelist_free(dir, totfile, NULL);
+ BLI_filelist_free(dir, totfile);
dir = NULL;
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 6b2f12eef45..44e130ed896 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -3343,7 +3343,8 @@ void uiTemplateOperatorSearch(uiLayout *layout)
#define B_STOPCOMPO 4
#define B_STOPSEQ 5
#define B_STOPCLIP 6
-#define B_STOPOTHER 7
+#define B_STOPFILE 7
+#define B_STOPOTHER 8
static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
{
@@ -3366,6 +3367,9 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event)
case B_STOPCLIP:
WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
break;
+ case B_STOPFILE:
+ WM_jobs_stop(CTX_wm_manager(C), CTX_wm_area(C), NULL);
+ break;
case B_STOPOTHER:
G.is_break = true;
break;
@@ -3396,6 +3400,12 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
owner = sa;
handle_event = B_STOPCLIP;
}
+ else if (sa->spacetype == SPACE_FILE) {
+ if (WM_jobs_test(wm, sa, WM_JOB_TYPE_FILESEL_READDIR)) {
+ owner = sa;
+ }
+ handle_event = B_STOPFILE;
+ }
else {
Scene *scene;
/* another scene can be rendering too, for example via compositor */
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index 185ba24eac7..fd701a8be4c 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC
../../makesrna
../../render/extern/include
../../windowmanager
+ ../../../../intern/atomic
../../../../intern/guardedalloc
../../../../intern/glew-mx
)
diff --git a/source/blender/editors/space_file/SConscript b/source/blender/editors/space_file/SConscript
index 6cb7c5a69d4..a66a14a32de 100644
--- a/source/blender/editors/space_file/SConscript
+++ b/source/blender/editors/space_file/SConscript
@@ -29,6 +29,7 @@ Import ('env')
sources = env.Glob('*.c')
incs = [
+ '#/intern/atomic',
'#/intern/guardedalloc',
env['BF_GLEW_INC'],
'#/intern/glew-mx',
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 9922290bf6f..c809b79bd61 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -35,6 +35,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_fileops_types.h"
+#include "BLI_math.h"
#ifdef WIN32
# include "BLI_winstuff.h"
@@ -47,6 +48,8 @@
#include "BKE_global.h"
#include "BKE_main.h"
+#include "BLO_readfile.h"
+
#include "BLT_translation.h"
#include "IMB_imbuf_types.h"
@@ -64,12 +67,20 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "WM_api.h"
#include "WM_types.h"
#include "filelist.h"
#include "file_intern.h" // own include
+/* Dummy helper - we need dynamic tooltips here. */
+static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
+{
+ char *dyn_tooltip = argN;
+ return BLI_strdup(dyn_tooltip);
+}
+
/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d.
* The controls are laid out as follows:
*
@@ -157,9 +168,9 @@ void file_draw_buttons(const bContext *C, ARegion *ar)
/* Text input fields for directory and file. */
if (available_w > 0) {
- const struct direntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
+ const struct FileDirEntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
int overwrite_alert = file_draw_check_exists(sfile);
- const bool is_active_dir = file && file->path && BLI_is_dir(file->path);
+ const bool is_active_dir = file && (file->typeflag & FILE_TYPE_FOLDER);
/* callbacks for operator check functions */
UI_block_func_set(block, file_draw_check_cb, NULL, NULL);
@@ -220,8 +231,8 @@ void file_draw_buttons(const bContext *C, ARegion *ar)
/* Execute / cancel buttons. */
if (loadbutton) {
- const struct direntry *file = filelist_file(sfile->files, params->active_file);
- const char *str_exec = (file && file->path && BLI_is_dir(file->path)) ?
+ const struct FileDirEntry *file = sfile->files ? filelist_file(sfile->files, params->active_file) : NULL;
+ const char *str_exec = (file && (file->typeflag & FILE_TYPE_FOLDER)) ?
/* params->title is already translated! */
IFACE_("Open Directory") : params->title;
@@ -244,44 +255,6 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
}
-static int get_file_icon(struct direntry *file)
-{
- if (file->type & S_IFDIR) {
- if (FILENAME_IS_PARENT(file->relname)) {
- return ICON_FILE_PARENT;
- }
- if (file->flags & FILE_TYPE_APPLICATIONBUNDLE) {
- return ICON_UGLYPACKAGE;
- }
- if (file->flags & FILE_TYPE_BLENDER) {
- return ICON_FILE_BLEND;
- }
- return ICON_FILE_FOLDER;
- }
- else if (file->flags & FILE_TYPE_BLENDER)
- return ICON_FILE_BLEND;
- else if (file->flags & FILE_TYPE_BLENDER_BACKUP)
- return ICON_FILE_BACKUP;
- else if (file->flags & FILE_TYPE_IMAGE)
- return ICON_FILE_IMAGE;
- else if (file->flags & FILE_TYPE_MOVIE)
- return ICON_FILE_MOVIE;
- else if (file->flags & FILE_TYPE_PYSCRIPT)
- return ICON_FILE_SCRIPT;
- else if (file->flags & FILE_TYPE_SOUND)
- return ICON_FILE_SOUND;
- else if (file->flags & FILE_TYPE_FTFONT)
- return ICON_FILE_FONT;
- else if (file->flags & FILE_TYPE_BTX)
- return ICON_FILE_BLANK;
- else if (file->flags & FILE_TYPE_COLLADA)
- return ICON_FILE_BLANK;
- else if (file->flags & FILE_TYPE_TEXT)
- return ICON_FILE_TEXT;
- else
- return ICON_FILE_BLANK;
-}
-
static void file_draw_icon(uiBlock *block, const char *path, int sx, int sy, int icon, int width, int height, bool drag)
{
uiBut *but;
@@ -293,10 +266,12 @@ static void file_draw_icon(uiBlock *block, const char *path, int sx, int sy, int
/*if (icon == ICON_FILE_BLANK) alpha = 0.375f;*/
- but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, 0.0f, 0.0f, "");
+ but = uiDefIconBut(block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, 0.0f, 0.0f, NULL);
+ UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
if (drag) {
- UI_but_drag_set_path(but, path, false);
+ /* path is no more static, cannot give it directly to but... */
+ UI_but_drag_set_path(but, BLI_strdup(path), true);
}
}
@@ -338,7 +313,9 @@ void file_calc_previews(const bContext *C, ARegion *ar)
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
}
-static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag)
+static void file_draw_preview(
+ uiBlock *block, const char *path, int sx, int sy, const float icon_aspect,
+ ImBuf *imb, const int icon, FileLayout *layout, const bool is_icon, const int typeflags, const bool drag)
{
uiBut *but;
float fx, fy;
@@ -348,7 +325,7 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
float scaledx, scaledy;
float scale;
int ex, ey;
- bool use_dropshadow = !is_icon && (file->flags & FILE_TYPE_IMAGE);
+ bool use_dropshadow = !is_icon && (typeflags & FILE_TYPE_IMAGE);
BLI_assert(imb != NULL);
@@ -394,7 +371,7 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
glEnable(GL_BLEND);
/* the image */
- if (!is_icon && file->flags & FILE_TYPE_FTFONT) {
+ if (!is_icon && typeflags & FILE_TYPE_FTFONT) {
UI_ThemeColor(TH_TEXT);
}
else {
@@ -402,16 +379,23 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
}
glaDrawPixelsTexScaled((float)xco, (float)yco, imb->x, imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, imb->rect, scale, scale);
+ if (icon) {
+ UI_icon_draw_aspect((float)xco, (float)yco, icon, icon_aspect, 1.0f);
+ }
+
/* border */
if (use_dropshadow) {
glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
fdrawbox((float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
}
+ but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, NULL);
+ UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
+
/* dragregion */
if (drag) {
- but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, "");
- UI_but_drag_set_image(but, file->path, get_file_icon(file), imb, scale, false);
+ /* path is no more static, cannot give it directly to but... */
+ UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
}
glDisable(GL_BLEND);
@@ -424,6 +408,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
char filename[FILE_MAX + 12];
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
+ ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
BLI_make_file_string(G.main->name, orgname, sfile->params->dir, oldname);
@@ -435,7 +420,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
if (!BLI_exists(newname)) {
BLI_rename(orgname, newname);
/* to make sure we show what is on disk */
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
}
ED_region_tag_redraw(ar);
@@ -500,7 +485,8 @@ void file_draw_list(const bContext *C, ARegion *ar)
FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
View2D *v2d = &ar->v2d;
struct FileList *files = sfile->files;
- struct direntry *file;
+ struct FileDirEntry *file;
+ const char *root = filelist_dir(files);
ImBuf *imb;
uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
int numfiles;
@@ -513,8 +499,11 @@ void file_draw_list(const bContext *C, ARegion *ar)
short align;
bool do_drag;
int column_space = 0.6f * UI_UNIT_X;
+ const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
+ const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
+ const float thumb_icon_aspect = sqrtf(64.0f / (float)(params->thumbnail_size));
- numfiles = filelist_numfiles(files);
+ numfiles = filelist_files_ensure(files);
if (params->display != FILE_IMGDISPLAY) {
@@ -536,27 +525,61 @@ void file_draw_list(const bContext *C, ARegion *ar)
numfiles_layout += layout->columns;
}
+ filelist_file_cache_slidingwindow_set(files, numfiles_layout);
+
textwidth = (FILE_IMGDISPLAY == params->display) ? layout->tile_w : (int)layout->column_widths[COLUMN_NAME];
textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5);
align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT;
+ if (numfiles > 0) {
+ const bool success = filelist_file_cache_block(files, min_ii(offset + (numfiles_layout / 2), numfiles - 1));
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+
+ filelist_cache_previews_update(files);
+
+ /* Handle preview timer here, since it's filelist_file_cache_block() and filelist_cache_previews_update()
+ * which controlls previews task. */
+ {
+ const bool previews_running = filelist_cache_previews_running(files);
+// printf("%s: preview task: %d\n", __func__, previews_running);
+ if (previews_running && !sfile->previews_timer) {
+ sfile->previews_timer = WM_event_add_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C),
+ NC_SPACE | ND_SPACE_FILE_PREVIEW, 0.01);
+ }
+ if (!previews_running && sfile->previews_timer) {
+ /* Preview is not running, no need to keep generating update events! */
+// printf("%s: Inactive preview task, sleeping!\n", __func__);
+ WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), sfile->previews_timer);
+ sfile->previews_timer = NULL;
+ }
+ }
+ }
+
for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) {
+ unsigned int file_selflag;
+ char path[FILE_MAX_LIBEXTRA];
ED_fileselect_layout_tilepos(layout, i, &sx, &sy);
sx += (int)(v2d->tot.xmin + 0.1f * UI_UNIT_X);
sy = (int)(v2d->tot.ymax - sy);
file = filelist_file(files, i);
+ file_selflag = filelist_entry_select_get(sfile->files, file, CHECK_ALL);
+
+ BLI_join_dirfile(path, sizeof(path), root, file->relpath);
UI_ThemeColor4(TH_TEXT);
- if (!(file->selflag & FILE_SEL_EDITING)) {
- if ((params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) || (file->selflag & FILE_SEL_SELECTED)) {
- int colorid = (file->selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
- int shade = (params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0;
+ if (!(file_selflag & FILE_SEL_EDITING)) {
+ if ((params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ||
+ (file_selflag & FILE_SEL_SELECTED))
+ {
+ int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
+ int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0;
- BLI_assert(i > 0 || FILENAME_IS_CURRPAR(file->relname));
+ BLI_assert(i > 0 || FILENAME_IS_CURRPAR(file->relpath));
draw_tile(sx, sy - 1, layout->tile_w + 4, sfile->layout->tile_h + layout->tile_border_y, colorid, shade);
}
@@ -564,26 +587,29 @@ void file_draw_list(const bContext *C, ARegion *ar)
UI_draw_roundbox_corner_set(UI_CNR_NONE);
/* don't drag parent or refresh items */
- do_drag = !(FILENAME_IS_CURRPAR(file->relname));
+ do_drag = !(FILENAME_IS_CURRPAR(file->relpath));
if (FILE_IMGDISPLAY == params->display) {
+ const int icon = filelist_geticon(files, i, false);
is_icon = 0;
imb = filelist_getimage(files, i);
if (!imb) {
- imb = filelist_geticon(files, i);
+ imb = filelist_geticon_image(files, i);
is_icon = 1;
}
- file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag);
+ file_draw_preview(block, path, sx, sy, thumb_icon_aspect,
+ imb, icon, layout, is_icon, file->typeflag, do_drag);
}
else {
- file_draw_icon(block, file->path, sx, sy - (UI_UNIT_Y / 6), get_file_icon(file), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag);
+ file_draw_icon(block, path, sx, sy - (UI_UNIT_Y / 6), filelist_geticon(files, i, true),
+ ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag);
sx += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
}
UI_ThemeColor4(TH_TEXT);
- if (file->selflag & FILE_SEL_EDITING) {
+ if (file_selflag & FILE_SEL_EDITING) {
uiBut *but;
short width;
@@ -591,9 +617,7 @@ void file_draw_list(const bContext *C, ARegion *ar)
width = layout->tile_w - (ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X);
}
else if (params->display == FILE_LONGDISPLAY) {
- width = layout->column_widths[COLUMN_NAME] + layout->column_widths[COLUMN_MODE1] +
- layout->column_widths[COLUMN_MODE2] + layout->column_widths[COLUMN_MODE3] +
- (column_space * 3.5f);
+ width = layout->column_widths[COLUMN_NAME] + (column_space * 3.5f);
}
else {
BLI_assert(params->display == FILE_IMGDISPLAY);
@@ -601,53 +625,51 @@ void file_draw_list(const bContext *C, ARegion *ar)
}
but = uiDefBut(block, UI_BTYPE_TEXT, 1, "", sx, sy - layout->tile_h - 0.15f * UI_UNIT_X,
- width, textheight, sfile->params->renameedit, 1.0f, (float)sizeof(sfile->params->renameedit), 0, 0, "");
+ width, textheight, sfile->params->renameedit, 1.0f,
+ (float)sizeof(sfile->params->renameedit), 0, 0, "");
UI_but_func_rename_set(but, renamebutton_cb, file);
UI_but_flag_enable(but, UI_BUT_NO_UTF8); /* allow non utf8 names */
UI_but_flag_disable(but, UI_BUT_UNDO);
if (false == UI_but_active_only(C, ar, block, but)) {
- file->selflag &= ~FILE_SEL_EDITING;
+ file_selflag = filelist_entry_select_set(
+ sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL);
}
}
- if (!(file->selflag & FILE_SEL_EDITING)) {
+ if (!(file_selflag& FILE_SEL_EDITING)) {
int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight : sy;
- file_draw_string(sx + 1, tpos, file->relname, (float)textwidth, textheight, align);
+ file_draw_string(sx + 1, tpos, file->name, (float)textwidth, textheight, align);
}
if (params->display == FILE_SHORTDISPLAY) {
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
- if (!(file->type & S_IFDIR)) {
- file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
+ if (!(file->typeflag & FILE_TYPE_DIR)) {
+ if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
+ BLI_filelist_entry_size_to_string(NULL, file->entry->size, small_size, file->entry->size_str);
+ }
+ file_draw_string(
+ sx, sy, file->entry->size_str, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
}
}
else if (params->display == FILE_LONGDISPLAY) {
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
-#ifndef WIN32
- /* rwx rwx rwx */
- file_draw_string(sx, sy, file->mode1, layout->column_widths[COLUMN_MODE1], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_MODE1] + column_space;
-
- file_draw_string(sx, sy, file->mode2, layout->column_widths[COLUMN_MODE2], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_MODE2] + column_space;
-
- file_draw_string(sx, sy, file->mode3, layout->column_widths[COLUMN_MODE3], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_MODE3] + column_space;
-
- file_draw_string(sx, sy, file->owner, layout->column_widths[COLUMN_OWNER], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_OWNER] + column_space;
-#endif
-
- file_draw_string(sx, sy, file->date, layout->column_widths[COLUMN_DATE], layout->tile_h, align);
+ if ((file->entry->date_str[0] == '\0') || update_stat_strings) {
+ BLI_filelist_entry_datetime_to_string(
+ NULL, file->entry->time, small_size, file->entry->time_str, file->entry->date_str);
+ }
+ file_draw_string(sx, sy, file->entry->date_str, layout->column_widths[COLUMN_DATE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
-
- file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align);
+ file_draw_string(sx, sy, file->entry->time_str, layout->column_widths[COLUMN_TIME], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
- if (!(file->type & S_IFDIR)) {
- file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
+ if (!(file->typeflag & FILE_TYPE_DIR)) {
+ if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
+ BLI_filelist_entry_size_to_string(NULL, file->entry->size, small_size, file->entry->size_str);
+ }
+ file_draw_string(
+ sx, sy, file->entry->size_str, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
}
}
@@ -656,4 +678,5 @@ void file_draw_list(const bContext *C, ARegion *ar)
UI_block_end(C, block);
UI_block_draw(C, block);
+ layout->curr_size = params->thumbnail_size;
}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index b24780c6e05..baafefab1f6 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -48,6 +48,8 @@ struct ARegion *file_tools_region(struct ScrArea *sa);
#define IMASEL_BUTTONS_HEIGHT (UI_UNIT_Y * 2)
#define IMASEL_BUTTONS_MARGIN (UI_UNIT_Y / 6)
+#define SMALL_SIZE_CHECK(_size) ((_size) < 64) /* Related to FileSelectParams.thumbnail_size. */
+
void file_draw_buttons(const bContext *C, ARegion *ar);
void file_calc_previews(const bContext *C, ARegion *ar);
void file_draw_list(const bContext *C, ARegion *ar);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 6bbde1e1c17..4e56b5b2d7d 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -99,9 +99,9 @@ static void file_deselect_all(SpaceFile *sfile, unsigned int flag)
{
FileSelection sel;
sel.first = 0;
- sel.last = filelist_numfiles(sfile->files) - 1;
+ sel.last = filelist_files_ensure(sfile->files) - 1;
- filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
+ filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
}
typedef enum FileSelect {
@@ -139,7 +139,7 @@ static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill
{
ARegion *ar = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- int numfiles = filelist_numfiles(sfile->files);
+ int numfiles = filelist_files_ensure(sfile->files);
FileSelection sel;
sel = find_file_mouse_rect(sfile, ar, rect);
@@ -152,7 +152,7 @@ static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill
if (fill && (sel.last >= 0) && (sel.last < numfiles) ) {
int f = sel.last;
while (f >= 0) {
- if (filelist_is_selected(sfile->files, f, CHECK_ALL) )
+ if (filelist_entry_select_index_get(sfile->files, f, CHECK_ALL) )
break;
f--;
}
@@ -168,8 +168,8 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
FileSelect retval = FILE_SELECT_NOTHING;
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_params(sfile);
- int numfiles = filelist_numfiles(sfile->files);
- struct direntry *file;
+ int numfiles = filelist_files_ensure(sfile->files);
+ const FileDirEntry *file;
/* make the selected file active */
if ((selected_idx >= 0) &&
@@ -177,27 +177,33 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
(file = filelist_file(sfile->files, selected_idx)))
{
params->highlight_file = selected_idx;
- sfile->params->active_file = selected_idx;
+ params->active_file = selected_idx;
- if (S_ISDIR(file->type)) {
- const bool is_parent_dir = FILENAME_IS_PARENT(file->relname);
+ if (file->typeflag & FILE_TYPE_DIR) {
+ const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath);
if (do_diropen == false) {
params->file[0] = '\0';
retval = FILE_SELECT_DIR;
}
/* the path is too long and we are not going up! */
- else if (!is_parent_dir && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) {
+ else if (!is_parent_dir && strlen(params->dir) + strlen(file->relpath) >= FILE_MAX) {
// XXX error("Path too long, cannot enter this directory");
}
else {
if (is_parent_dir) {
/* avoids /../../ */
BLI_parent_dir(params->dir);
+
+ if (params->recursion_level > 1) {
+ /* Disable 'dirtree' recursion when going up in tree. */
+ params->recursion_level = 0;
+ filelist_setrecursion(sfile->files, params->recursion_level);
+ }
}
else {
BLI_cleanup_dir(G.main->name, params->dir);
- strcat(params->dir, file->relname);
+ strcat(params->dir, file->relpath);
BLI_add_slash(params->dir);
}
@@ -218,11 +224,12 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
*/
static bool file_is_any_selected(struct FileList *files)
{
- const int numfiles = filelist_numfiles(files);
+ const int numfiles = filelist_files_ensure(files);
int i;
+ /* Is any file selected ? */
for (i = 0; i < numfiles; ++i) {
- if (filelist_is_selected(files, i, CHECK_ALL)) {
+ if (filelist_entry_select_index_get(files, i, CHECK_ALL)) {
return true;
}
}
@@ -239,7 +246,7 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select,
const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_ALL;
/* flag the files as selected in the filelist */
- filelist_select(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
+ filelist_entries_select_index_range_set(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
/* Don't act on multiple selected files */
if (sel.first != sel.last) select = 0;
@@ -247,7 +254,7 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select,
/* Do we have a valid selection and are we actually selecting */
if ((sel.last >= 0) && (select != FILE_SEL_REMOVE)) {
/* Check last selection, if selected, act on the file or dir */
- if (filelist_is_selected(sfile->files, sel.last, check_type)) {
+ if (filelist_entry_select_index_get(sfile->files, sel.last, check_type)) {
retval = file_select_do(C, sel.last, do_diropen);
}
}
@@ -305,25 +312,24 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *
result = WM_border_select_modal(C, op, event);
if (result == OPERATOR_RUNNING_MODAL) {
-
WM_operator_properties_border_to_rcti(op, &rect);
BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
sel = file_selection_get(C, &rect, 0);
- if ( (sel.first != params->sel_first) || (sel.last != params->sel_last) ) {
+ if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) {
int idx;
file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
- filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
+ filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
for (idx = sel.last; idx >= 0; idx--) {
- struct direntry *file = filelist_file(sfile->files, idx);
+ const FileDirEntry *file = filelist_file(sfile->files, idx);
/* dont highlight readonly file (".." or ".") on border select */
- if (FILENAME_IS_CURRPAR(file->relname)) {
- file->selflag &= ~FILE_SEL_HIGHLIGHTED;
+ if (FILENAME_IS_CURRPAR(file->relpath)) {
+ filelist_entry_select_set(sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
}
/* make sure highlight_file is no readonly file */
@@ -366,7 +372,7 @@ static int file_border_select_exec(bContext *C, wmOperator *op)
ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
/* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */
- filelist_select_file(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
if (FILE_SELECT_DIR == ret) {
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
@@ -416,8 +422,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (sfile && sfile->params) {
int idx = sfile->params->highlight_file;
+ int numfiles = filelist_files_ensure(sfile->files);
- if (idx >= 0) {
+ if ((idx >= 0) && (idx < numfiles)) {
/* single select, deselect all selected first */
if (!extend) {
file_deselect_all(sfile, FILE_SEL_SELECTED);
@@ -429,7 +436,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (extend) {
/* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */
- filelist_select_file(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
}
if (FILE_SELECT_DIR == ret)
@@ -484,12 +491,13 @@ static bool file_walk_select_selection_set(
if (has_selection) {
if (extend &&
- filelist_is_selected(files, active_old, FILE_SEL_SELECTED) &&
- filelist_is_selected(files, active_new, FILE_SEL_SELECTED))
+ filelist_entry_select_index_get(files, active_old, FILE_SEL_SELECTED) &&
+ filelist_entry_select_index_get(files, active_new, FILE_SEL_SELECTED))
{
/* conditions for deselecting: initial file is selected, new file is
* selected and either other_side isn't selected/found or we use fill */
- deselect = (fill || other_site == -1 || !filelist_is_selected(files, other_site, FILE_SEL_SELECTED));
+ deselect = (fill || other_site == -1 ||
+ !filelist_entry_select_index_get(files, other_site, FILE_SEL_SELECTED));
/* don't change highlight_file here since we either want to deselect active or we want to
* walk through a block of selected files without selecting/deselecting anything */
@@ -527,7 +535,7 @@ static bool file_walk_select_selection_set(
params->highlight_file = params->active_file;
/* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */
- filelist_select_file(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_select_index_set(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
}
else {
/* deselect all first */
@@ -548,15 +556,15 @@ static bool file_walk_select_selection_set(
}
/* fill selection between last and first selected file */
- filelist_select(
+ filelist_entries_select_index_range_set(
files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
/* entire sel is cleared here, so select active again */
if (deselect) {
- filelist_select_file(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entry_select_index_set(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
}
}
else {
- filelist_select_file(
+ filelist_entry_select_index_set(
files, active, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
}
@@ -576,7 +584,7 @@ static bool file_walk_select_do(
const bool extend, const bool fill)
{
struct FileList *files = sfile->files;
- const int numfiles = filelist_numfiles(files);
+ const int numfiles = filelist_files_ensure(files);
const bool has_selection = file_is_any_selected(files);
const int active_old = params->active_file;
int active_new = -1;
@@ -692,7 +700,7 @@ static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
ScrArea *sa = CTX_wm_area(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelection sel;
- const int numfiles = filelist_numfiles(sfile->files);
+ const int numfiles = filelist_files_ensure(sfile->files);
const bool has_selection = file_is_any_selected(sfile->files);
sel.first = 0;
@@ -700,18 +708,18 @@ static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
/* select all only if previously no file was selected */
if (has_selection) {
- filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
+ filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
sfile->params->active_file = -1;
}
else {
const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES;
int i;
- filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type);
+ filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type);
/* set active_file to first selected */
for (i = 0; i < numfiles; i++) {
- if (filelist_is_selected(sfile->files, i, check_type)) {
+ if (filelist_entry_select_index_get(sfile->files, i, check_type)) {
sfile->params->active_file = i;
break;
}
@@ -1029,7 +1037,7 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
if (sfile == NULL || sfile->files == NULL) return 0;
- numfiles = filelist_numfiles(sfile->files);
+ numfiles = filelist_files_ensure(sfile->files);
params = ED_fileselect_get_params(sfile);
origfile = params->highlight_file;
@@ -1143,17 +1151,17 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
/* this is called on operators check() so clear collections first since
* they may be already set. */
{
- int i, numfiles = filelist_numfiles(sfile->files);
+ int i, numfiles = filelist_files_ensure(sfile->files);
if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
PointerRNA itemptr;
int num_files = 0;
RNA_property_collection_clear(op->ptr, prop);
for (i = 0; i < numfiles; i++) {
- if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
- struct direntry *file = filelist_file(sfile->files, i);
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
+ FileDirEntry *file = filelist_file(sfile->files, i);
RNA_property_collection_add(op->ptr, prop, &itemptr);
- RNA_string_set(&itemptr, "name", file->relname);
+ RNA_string_set(&itemptr, "name", file->relpath);
num_files++;
}
}
@@ -1169,10 +1177,10 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
int num_dirs = 0;
RNA_property_collection_clear(op->ptr, prop);
for (i = 0; i < numfiles; i++) {
- if (filelist_is_selected(sfile->files, i, CHECK_DIRS)) {
- struct direntry *file = filelist_file(sfile->files, i);
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_DIRS)) {
+ FileDirEntry *file = filelist_file(sfile->files, i);
RNA_property_collection_add(op->ptr, prop, &itemptr);
- RNA_string_set(&itemptr, "name", file->relname);
+ RNA_string_set(&itemptr, "name", file->relpath);
num_dirs++;
}
}
@@ -1262,19 +1270,17 @@ int file_exec(bContext *C, wmOperator *exec_op)
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- const struct direntry *file = filelist_file(sfile->files, sfile->params->active_file);
+ const struct FileDirEntry *file = filelist_file(sfile->files, sfile->params->active_file);
char filepath[FILE_MAX];
/* directory change */
- if (file && S_ISDIR(file->type)) {
- BLI_assert(file->path == NULL || STRPREFIX(file->path, sfile->params->dir));
-
- if (FILENAME_IS_PARENT(file->relname)) {
+ if (file && (file->typeflag & FILE_TYPE_DIR)) {
+ if (FILENAME_IS_PARENT(file->relpath)) {
BLI_parent_dir(sfile->params->dir);
}
- else if (file->relname) {
+ else if (file->relpath) {
BLI_cleanup_dir(G.main->name, sfile->params->dir);
- strcat(sfile->params->dir, file->relname);
+ strcat(sfile->params->dir, file->relpath);
BLI_add_slash(sfile->params->dir);
}
@@ -1287,10 +1293,11 @@ int file_exec(bContext *C, wmOperator *exec_op)
/* when used as a macro, for doubleclick,
* to prevent closing when doubleclicking on .. item */
if (RNA_boolean_get(exec_op->ptr, "need_active")) {
+ const int numfiles = filelist_files_ensure(sfile->files);
int i, active = 0;
- for (i = 0; i < filelist_numfiles(sfile->files); i++) {
- if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
+ for (i = 0; i < numfiles; i++) {
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
active = 1;
break;
}
@@ -1358,6 +1365,11 @@ int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
else {
ED_file_change_dir(C, true);
}
+ if (sfile->params->recursion_level > 1) {
+ /* Disable 'dirtree' recursion when going up in tree. */
+ sfile->params->recursion_level = 0;
+ filelist_setrecursion(sfile->files, sfile->params->recursion_level);
+ }
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
}
@@ -1384,9 +1396,10 @@ static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
+ ScrArea *sa = CTX_wm_area(C);
struct FSMenu *fsmenu = ED_fsmenu_get();
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
/* refresh system directory menu */
fsmenu_refresh_system_category(fsmenu);
@@ -1476,11 +1489,11 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata)
return OPERATOR_PASS_THROUGH;
- numfiles = filelist_numfiles(sfile->files);
+ numfiles = filelist_files_ensure(sfile->files);
/* check if we are editing a name */
for (i = 0; i < numfiles; ++i) {
- if (filelist_is_selected(sfile->files, i, CHECK_ALL) ) {
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL) ) {
edit_idx = i;
break;
}
@@ -1603,6 +1616,7 @@ int file_directory_new_exec(bContext *C, wmOperator *op)
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
+ ScrArea *sa = CTX_wm_area(C);
if (!sfile->params) {
BKE_report(op->reports, RPT_WARNING, "No parent directory given");
@@ -1655,7 +1669,7 @@ int file_directory_new_exec(bContext *C, wmOperator *op)
sfile->scroll_offset = 0;
/* reload dir to make sure we're seeing what's in the directory */
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
if (RNA_boolean_get(op->ptr, "open")) {
BLI_strncpy(sfile->params->dir, path, sizeof(sfile->params->dir));
@@ -1876,10 +1890,11 @@ static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
+ ScrArea *sa = CTX_wm_area(C);
if (sfile->params) {
sfile->params->flag ^= FILE_HIDE_DOT;
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
@@ -1989,11 +2004,11 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
if (sfile->params) {
int idx = sfile->params->highlight_file;
- int numfiles = filelist_numfiles(sfile->files);
+ int numfiles = filelist_files_ensure(sfile->files);
if ((0 <= idx) && (idx < numfiles)) {
- struct direntry *file = filelist_file(sfile->files, idx);
- filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
- BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
+ FileDirEntry *file = filelist_file(sfile->files, idx);
+ filelist_entry_select_index_set(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
+ BLI_strncpy(sfile->params->renameedit, file->relpath, FILE_MAXFILE);
sfile->params->renamefile[0] = '\0';
}
ED_area_tag_redraw(sa);
@@ -2005,29 +2020,34 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
static int file_rename_poll(bContext *C)
{
- int poll = ED_operator_file_active(C);
+ bool poll = ED_operator_file_active(C);
SpaceFile *sfile = CTX_wm_space_file(C);
if (sfile && sfile->params) {
int idx = sfile->params->highlight_file;
+ int numfiles = filelist_files_ensure(sfile->files);
- if (idx >= 0) {
- struct direntry *file = filelist_file(sfile->files, idx);
- if (FILENAME_IS_CURRPAR(file->relname)) {
- poll = 0;
+ if ((0 <= idx) && (idx < numfiles)) {
+ FileDirEntry *file = filelist_file(sfile->files, idx);
+ if (FILENAME_IS_CURRPAR(file->relpath)) {
+ poll = false;
}
}
if (sfile->params->highlight_file < 0) {
- poll = 0;
+ poll = false;
}
else {
char dir[FILE_MAX];
- if (filelist_islibrary(sfile->files, dir, NULL)) poll = 0;
+ if (filelist_islibrary(sfile->files, dir, NULL)) {
+ poll = false;
+ }
}
}
- else
- poll = 0;
+ else {
+ poll = false;
+ }
+
return poll;
}
@@ -2051,13 +2071,13 @@ static int file_delete_poll(bContext *C)
if (sfile && sfile->params) {
char dir[FILE_MAX];
- int numfiles = filelist_numfiles(sfile->files);
+ int numfiles = filelist_files_ensure(sfile->files);
int i;
int num_selected = 0;
if (filelist_islibrary(sfile->files, dir, NULL)) poll = 0;
for (i = 0; i < numfiles; i++) {
- if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
num_selected++;
}
}
@@ -2076,19 +2096,20 @@ int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
char str[FILE_MAX];
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- struct direntry *file;
- int numfiles = filelist_numfiles(sfile->files);
+ ScrArea *sa = CTX_wm_area(C);
+ FileDirEntry *file;
+ int numfiles = filelist_files_ensure(sfile->files);
int i;
for (i = 0; i < numfiles; i++) {
- if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) {
file = filelist_file(sfile->files, i);
- BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
+ BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relpath);
BLI_delete(str, false, false);
}
}
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 21a072d0adf..02b31b494c2 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,128 @@ 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;
+
+ /* 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;
+
+ /* Allows to quickly get a cached entry from its UUID. */
+ GHash *uuids;
-typedef struct FileImage {
- struct FileImage *next, *prev;
+ /* Previews handling. */
+ 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 +351,187 @@ 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;
+
+ name1 = entry1->name;
+ name2 = entry2->name;
- return BLI_natstrcmp(entry1->relname, entry2->relname);
+ 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;
- return BLI_natstrcmp(entry1->relname, entry2->relname);
+ name1 = entry1->name;
+ name2 = entry2->name;
+
+ 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;
- return BLI_strcasecmp(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 = "";
-bool filelist_need_sorting(struct FileList *filelist)
-{
- return filelist->need_sorting && (filelist->sort != FILE_SORT_NONE);
+ if ((ret = BLI_strcasecmp(sufix1, sufix2))) {
+ return ret;
+ }
+ }
+
+ 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;
}
filelist_filter_clear(filelist);
+ filelist->flags &= ~FL_NEED_SORTING;
}
}
@@ -414,7 +539,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 +547,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 +561,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 +569,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 +620,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];
+ bool is_filtered;
+ char path[FILE_MAX_LIBEXTRA], dir[FILE_MAXDIR], *group, *name;
- if (BLO_library_path_explode(root, dir, NULL, NULL)) {
- 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 +667,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 +812,489 @@ 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)
+{
+ 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);
+ 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 = BLI_task_scheduler_get();
+ TaskPool *pool;
+ int num_tasks = max_ii(2, BLI_system_thread_count() / 2);
+
+ 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);
+ 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)
+{
+ 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)
+{
+ if (!(cache->flags & FLC_IS_INIT)) {
+ return;
+ }
+
+ filelist_cache_previews_free(cache, true);
+
+ /* Note we nearly have nothing to do here, entries are just 'borrowed', not owned by cache... */
+ 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);
+}
+
+static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
+{
+ if (!(cache->flags & FLC_IS_INIT)) {
+ return;
+ }
+
+ filelist_cache_previews_clear(cache);
+
+ /* Note we nearly have nothing to do here, entries are just 'borrowed', not owned by 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;
+}
+
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 +1302,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 +1328,194 @@ 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));
+#ifndef NDEBUG
+ size_t len = strlen(r_dir);
+ BLI_assert((len < FILE_MAX_LIBEXTRA) && ELEM(r_dir[len - 1], SEP, ALTSEP));
+#endif
+
+ BLI_cleanup_dir(G.main->name, r_dir);
+ BLI_add_slash(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];
+ 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(&filelist->filelist.entries, ret);
+ return ret;
+}
+
+static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry)
+{
+ BLI_remlink(&filelist->filelist.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 +1523,375 @@ 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 = filelist_file_create_entry(filelist, idx);
+ cache->block_entries[cursor] = entry;
+ BLI_ghash_insert(cache->uuids, entry->uuid, 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);
+
+ /* entry might have been removed from cache in the mean while, we do not want to cache it again here. */
+ FileDirEntry *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 +1966,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 +1977,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)
{
- return BLO_library_path_explode(filelist->dir, dir, group, NULL);
+ return BLO_library_path_explode(filelist->filelist.root, dir, group, NULL);
}
static int groupname_to_code(const char *group)
@@ -1054,6 +2115,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 +2125,231 @@ 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)
+{
+ 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 bacground 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);
+ if (S_ISDIR(files[i].s.st_mode)) {
+ entry->typeflag |= FILE_TYPE_DIR;
+ }
+ entry->st = files[i].s;
+
+ /* Set file type. */
+ /* If we are considering .blend files as libs, promote them to directory status! */
+ if (do_lib && BLO_has_bfile_extension(entry->relpath)) {
+ 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)) {
+ if (filter_glob[0] && BLI_testextensie_glob(entry->relpath, filter_glob)) {
+ entry->typeflag = FILE_TYPE_OPERATOR;
+ }
+ else {
+ entry->typeflag = file_extension_type(root, entry->relpath);
+ }
+ }
+
+ 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)
{
- LinkNode *l, *names, *previews;
- struct ImBuf *ima;
- int ok, i, nprevs, nnames, idcode;
- char filename[FILE_MAX];
+ 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 = group ? groupname_to_code(group) : 0;
-
- /* 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));
-
- 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;
+ BLO_blendhandle_close(libfiledata);
- 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 (previews && (nnames != nprevs)) {
- printf("filelist_from_library: error, found %d items, %d previews\n", nnames, nprevs);
+ 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++;
}
- 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 +2358,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];
+ 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 +2402,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 uint64, 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! */
+ *((uint64_t *)entry->uuid) = atomic_add_uint64((uint64_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;
+
+ BLI_mutex_lock(&flrj->lock);
- if (!*tj->stop) {
- tj->filelist->need_thumbnails = false;
+ 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));
+
+ filelist_freelib(flrj->tmp_filelist);
+ filelist_free(flrj->tmp_filelist);
+ MEM_freeN(flrj->tmp_filelist);
+ }
+
+ BLI_mutex_end(&flrj->lock);
-void thumbnails_start(FileList *filelist, const bContext *C)
+ 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);
}
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 83f7718f0de..d70faab1d6a 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -40,9 +40,10 @@ extern "C" {
struct BlendHandle;
struct FileList;
struct FileSelection;
-struct direntry;
struct wmWindowManager;
+struct FileDirEntry;
+
typedef enum FileSelType {
FILE_SEL_REMOVE = 0,
FILE_SEL_ADD = 1,
@@ -65,11 +66,10 @@ int folderlist_clear_next(struct SpaceFile *sfile);
void filelist_setsorting(struct FileList *filelist, const short sort);
-bool filelist_need_sorting(struct FileList *filelist);
void filelist_sort(struct FileList *filelist);
void filelist_setfilter_options(struct 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);
void filelist_filter(struct FileList *filelist);
@@ -77,34 +77,48 @@ void filelist_init_icons(void);
void filelist_free_icons(void);
void filelist_imgsize(struct FileList *filelist, short w, short h);
struct ImBuf * filelist_getimage(struct FileList *filelist, const int index);
-struct ImBuf * filelist_geticon(struct FileList *filelist, const int index);
+struct ImBuf * filelist_geticon_image(struct FileList *filelist, const int index);
+int filelist_geticon(struct FileList *filelist, const int index, const bool is_main);
struct FileList * filelist_new(short type);
+void filelist_clear(struct FileList *filelist);
+void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection);
void filelist_free(struct FileList *filelist);
const char * filelist_dir(struct FileList *filelist);
-void filelist_readdir(struct FileList *filelist);
-void filelist_setdir(struct FileList *filelist, const char *dir);
+void filelist_setdir(struct FileList *filelist, char *r_dir);
+int filelist_files_ensure(struct FileList *filelist);
int filelist_empty(struct FileList *filelist);
-int filelist_numfiles(struct FileList *filelist);
-struct direntry * filelist_file(struct FileList *filelist, int index);
-int filelist_find(struct FileList *filelist, const char *file);
+FileDirEntry * filelist_file(struct FileList *filelist, int index);
+int filelist_file_findpath(struct FileList *filelist, const char *file);
+FileDirEntry * filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]);
+void filelist_file_cache_slidingwindow_set(struct FileList *filelist, size_t window_size);
+bool filelist_file_cache_block(struct FileList *filelist, const int index);
+
+bool filelist_force_reset(struct FileList *filelist);
+bool filelist_pending(struct FileList *filelist);
+bool filelist_is_ready(struct FileList *filelist);
-short filelist_changed(struct FileList *filelist);
+unsigned int filelist_entry_select_set(const struct FileList *filelist, const struct FileDirEntry *entry, FileSelType select, unsigned int flag, FileCheckType check);
+void filelist_entry_select_index_set(struct FileList *filelist, const int index, FileSelType select, unsigned int flag, FileCheckType check);
+void filelist_entries_select_index_range_set(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check);
+unsigned int filelist_entry_select_get(struct FileList *filelist, struct FileDirEntry *entry, FileCheckType check);
+unsigned int filelist_entry_select_index_get(struct FileList *filelist, const int index, FileCheckType check);
-void filelist_select(struct FileList *filelist, FileSelection *sel, FileSelType select, unsigned int flag, FileCheckType check);
-void filelist_select_file(struct FileList *filelist, int index, FileSelType select, unsigned int flag, FileCheckType check);
-bool filelist_is_selected(struct FileList *filelist, int index, FileCheckType check);
+void filelist_setrecursion(struct FileList *filelist, const int recursion_level);
struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **group);
void filelist_freelib(struct FileList *filelist);
-bool filelist_need_thumbnails(struct FileList *filelist);
-void thumbnails_start(struct FileList *filelist, const struct bContext *C);
-void thumbnails_stop(struct wmWindowManager *wm, struct FileList *filelist);
-int thumbnails_running(struct wmWindowManager *wm, struct FileList *filelist);
+void filelist_readjob_start(struct FileList *filelist, const struct bContext *C);
+void filelist_readjob_stop(struct wmWindowManager *wm, struct ScrArea *sa);
+int filelist_readjob_running(struct wmWindowManager *wm, struct ScrArea *sa);
+
+bool filelist_cache_previews_update(struct FileList *filelist);
+void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews);
+bool filelist_cache_previews_running(struct FileList *filelist);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 21db41b27a5..f1f202159be 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -163,6 +163,8 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->filter = 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_blender")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER : 0;
+ if ((prop = RNA_struct_find_property(op->ptr, "filter_blenlib")))
+ params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDERLIB : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_backup")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER_BACKUP : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_image")))
@@ -200,6 +202,13 @@ short ED_fileselect_set_params(SpaceFile *sfile)
}
}
+ /* For now, always init filterid to 'all true' */
+ params->filter_id = FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD |
+ FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA |
+ FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB |
+ FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | FILTER_ID_TE |
+ FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO;
+
if (U.uiflag & USER_HIDE_DOT) {
params->flag |= FILE_HIDE_DOT;
}
@@ -292,9 +301,9 @@ void ED_fileselect_reset_params(SpaceFile *sfile)
*/
void fileselect_file_set(SpaceFile *sfile, const int index)
{
- const struct direntry *file = filelist_file(sfile->files, index);
- if (file && file->relname[0] && file->path && !BLI_is_dir(file->path)) {
- BLI_strncpy(sfile->params->file, file->relname, FILE_MAXFILE);
+ const struct FileDirEntry *file = filelist_file(sfile->files, index);
+ if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_FOLDER)) {
+ BLI_strncpy(sfile->params->file, file->relpath, FILE_MAXFILE);
}
}
@@ -446,37 +455,20 @@ float file_font_pointsize(void)
#endif
}
-static void column_widths(struct FileList *files, struct FileLayout *layout)
+static void column_widths(FileSelectParams *params, struct FileLayout *layout)
{
int i;
- int numfiles = filelist_numfiles(files);
+ const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
for (i = 0; i < MAX_FILE_COLUMN; ++i) {
layout->column_widths[i] = 0;
}
- for (i = 0; (i < numfiles); ++i) {
- struct direntry *file = filelist_file(files, i);
- if (file) {
- float len;
- len = file_string_width(file->relname);
- if (len > layout->column_widths[COLUMN_NAME]) layout->column_widths[COLUMN_NAME] = len;
- len = file_string_width(file->date);
- if (len > layout->column_widths[COLUMN_DATE]) layout->column_widths[COLUMN_DATE] = len;
- len = file_string_width(file->time);
- if (len > layout->column_widths[COLUMN_TIME]) layout->column_widths[COLUMN_TIME] = len;
- len = file_string_width(file->size);
- if (len > layout->column_widths[COLUMN_SIZE]) layout->column_widths[COLUMN_SIZE] = len;
- len = file_string_width(file->mode1);
- if (len > layout->column_widths[COLUMN_MODE1]) layout->column_widths[COLUMN_MODE1] = len;
- len = file_string_width(file->mode2);
- if (len > layout->column_widths[COLUMN_MODE2]) layout->column_widths[COLUMN_MODE2] = len;
- len = file_string_width(file->mode3);
- if (len > layout->column_widths[COLUMN_MODE3]) layout->column_widths[COLUMN_MODE3] = len;
- len = file_string_width(file->owner);
- if (len > layout->column_widths[COLUMN_OWNER]) layout->column_widths[COLUMN_OWNER] = len;
- }
- }
+ layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;;
+ /* Biggest possible reasonable values... */
+ layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89");
+ layout->column_widths[COLUMN_TIME] = file_string_width("23:59");
+ layout->column_widths[COLUMN_SIZE] = file_string_width(small_size ? "98.7 M" : "98.7 MiB");
}
void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
@@ -496,7 +488,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
return;
}
- numfiles = filelist_numfiles(sfile->files);
+ numfiles = filelist_files_ensure(sfile->files);
textheight = (int)file_font_pointsize();
layout = sfile->layout;
layout->textheight = textheight;
@@ -535,7 +527,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y);
layout->rows = layout->height / (layout->tile_h + 2 * layout->tile_border_y);
- column_widths(sfile->files, layout);
+ column_widths(params, layout);
if (params->display == FILE_SHORTDISPLAY) {
maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
@@ -545,12 +537,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
else {
maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
(int)layout->column_widths[COLUMN_NAME] + column_space +
-#ifndef WIN32
- (int)layout->column_widths[COLUMN_MODE1] + column_space +
- (int)layout->column_widths[COLUMN_MODE2] + column_space +
- (int)layout->column_widths[COLUMN_MODE3] + column_space +
- (int)layout->column_widths[COLUMN_OWNER] + column_space +
-#endif
(int)layout->column_widths[COLUMN_DATE] + column_space +
(int)layout->column_widths[COLUMN_TIME] + column_space +
(int)layout->column_widths[COLUMN_SIZE] + column_space;
@@ -581,9 +567,10 @@ void ED_file_change_dir(bContext *C, const bool checkdir)
{
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
+ ScrArea *sa = CTX_wm_area(C);
if (sfile->params) {
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
/* Clear search string, it is very rare to want to keep that filter while changing dir,
* and usually very annoying to keep it actually! */
@@ -610,8 +597,8 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche
int match = 0;
int i;
- struct direntry *file;
- int n = filelist_numfiles(sfile->files);
+ FileDirEntry *file;
+ int n = filelist_files_ensure(sfile->files);
/* select any file that matches the pattern, this includes exact match
* if the user selects a single file by entering the filename
@@ -619,10 +606,10 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche
for (i = 0; i < n; i++) {
file = filelist_file(sfile->files, i);
/* Do not check wether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */
- if (fnmatch(pattern, file->relname, 0) == 0) {
- file->selflag |= FILE_SEL_SELECTED;
+ if (fnmatch(pattern, file->relpath, 0) == 0) {
+ filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
if (!match) {
- BLI_strncpy(matched_file, file->relname, FILE_MAX);
+ BLI_strncpy(matched_file, file->relpath, FILE_MAX);
}
match++;
}
@@ -687,14 +674,12 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
/* search if str matches the beginning of name */
if (str[0] && sfile->files) {
AutoComplete *autocpl = UI_autocomplete_begin(str, FILE_MAX);
- int nentries = filelist_numfiles(sfile->files);
+ int nentries = filelist_files_ensure(sfile->files);
int i;
for (i = 0; i < nentries; ++i) {
- struct direntry *file = filelist_file(sfile->files, i);
- if (file && (S_ISREG(file->type) || S_ISDIR(file->type))) {
- UI_autocomplete_update_name(autocpl, file->relname);
- }
+ FileDirEntry *file = filelist_file(sfile->files, i);
+ UI_autocomplete_update_name(autocpl, file->relpath);
}
match = UI_autocomplete_end(autocpl, str);
}
@@ -702,20 +687,20 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
return match;
}
-void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile)
+void ED_fileselect_clear(wmWindowManager *wm, ScrArea *sa, SpaceFile *sfile)
{
/* only NULL in rare cases - [#29734] */
if (sfile->files) {
- thumbnails_stop(wm, sfile->files);
+ filelist_readjob_stop(wm, sa);
filelist_freelib(sfile->files);
- filelist_free(sfile->files);
+ filelist_clear(sfile->files);
}
sfile->params->highlight_file = -1;
WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
-void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile)
+void ED_fileselect_exit(wmWindowManager *wm, ScrArea *sa, SpaceFile *sfile)
{
if (!sfile) return;
if (sfile->op) {
@@ -727,7 +712,8 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile)
folderlist_free(sfile->folders_next);
if (sfile->files) {
- ED_fileselect_clear(wm, sfile);
+ ED_fileselect_clear(wm, sa, sfile);
+ filelist_free(sfile->files);
MEM_freeN(sfile->files);
sfile->files = NULL;
}
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index a8b71238e14..eca573c27f0 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -117,6 +117,8 @@ static void file_free(SpaceLink *sl)
{
SpaceFile *sfile = (SpaceFile *) sl;
+ BLI_assert(sfile->previews_timer == NULL);
+
if (sfile->files) {
// XXXXX would need to do thumbnails_stop here, but no context available
filelist_freelib(sfile->files);
@@ -170,7 +172,12 @@ static void file_exit(wmWindowManager *wm, ScrArea *sa)
{
SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
- ED_fileselect_exit(wm, sfile);
+ if (sfile->previews_timer) {
+ WM_event_remove_timer_notifier(wm, NULL, sfile->previews_timer);
+ sfile->previews_timer = NULL;
+ }
+
+ ED_fileselect_exit(wm, sa, sfile);
}
static SpaceLink *file_duplicate(SpaceLink *sl)
@@ -211,13 +218,15 @@ static void file_refresh(const bContext *C, ScrArea *sa)
}
if (!sfile->files) {
sfile->files = filelist_new(params->type);
- filelist_setdir(sfile->files, params->dir);
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
+ filelist_setdir(sfile->files, params->dir);
+ filelist_setrecursion(sfile->files, params->recursion_level);
filelist_setsorting(sfile->files, params->sort);
- filelist_setfilter_options(sfile->files, params->flag & FILE_HIDE_DOT,
+ filelist_setfilter_options(sfile->files, (params->flag & FILE_HIDE_DOT) != 0,
false, /* TODO hide_parent, should be controllable? */
params->flag & FILE_FILTER ? params->filter : 0,
+ params->filter_id,
params->filter_glob,
params->filter_search);
@@ -227,39 +236,45 @@ static void file_refresh(const bContext *C, ScrArea *sa)
sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir);
sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
- if (filelist_empty(sfile->files)) {
- thumbnails_stop(wm, sfile->files);
- filelist_readdir(sfile->files);
- filelist_sort(sfile->files);
- BLI_strncpy(params->dir, filelist_dir(sfile->files), FILE_MAX);
- }
- else if (filelist_need_sorting(sfile->files)) {
- thumbnails_stop(wm, sfile->files);
- filelist_sort(sfile->files);
+ if (filelist_force_reset(sfile->files)) {
+ filelist_readjob_stop(wm, sa);
+ filelist_clear(sfile->files);
}
- if ((params->display == FILE_IMGDISPLAY) && filelist_need_thumbnails(sfile->files)) {
- if (!thumbnails_running(wm, sfile->files)) {
- thumbnails_start(sfile->files, C);
+ if (filelist_empty(sfile->files)) {
+ if (!filelist_pending(sfile->files)) {
+ filelist_readjob_start(sfile->files, C);
}
}
- else {
- /* stop any running thumbnail jobs if we're not displaying them - speedup for NFS */
- thumbnails_stop(wm, sfile->files);
- }
+ filelist_sort(sfile->files);
filelist_filter(sfile->files);
+ if (params->display == FILE_IMGDISPLAY) {
+ filelist_cache_previews_set(sfile->files, true);
+ }
+ else {
+ filelist_cache_previews_set(sfile->files, false);
+ if (sfile->previews_timer) {
+ WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer);
+ sfile->previews_timer = NULL;
+ }
+ }
+
if (params->renamefile[0] != '\0') {
- int idx = filelist_find(sfile->files, params->renamefile);
+ int idx = filelist_file_findpath(sfile->files, params->renamefile);
if (idx >= 0) {
- struct direntry *file = filelist_file(sfile->files, idx);
+ FileDirEntry *file = filelist_file(sfile->files, idx);
if (file) {
- file->selflag |= FILE_SEL_EDITING;
+ filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
}
}
BLI_strncpy(sfile->params->renameedit, sfile->params->renamefile, sizeof(sfile->params->renameedit));
- params->renamefile[0] = '\0';
+ /* File listing is now async, do not clear renamefile if matching entry not found
+ * and dirlist is not finished! */
+ if (idx >= 0 || filelist_is_ready(sfile->files)) {
+ params->renamefile[0] = '\0';
+ }
}
if (sfile->layout) {
@@ -278,7 +293,7 @@ static void file_refresh(const bContext *C, ScrArea *sa)
static void file_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
{
- /* SpaceFile *sfile = (SpaceFile *)sa->spacedata.first; */
+ SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
/* context changes */
switch (wmn->category) {
@@ -292,6 +307,12 @@ static void file_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
ED_area_tag_refresh(sa);
ED_area_tag_redraw(sa);
break;
+ case ND_SPACE_FILE_PREVIEW:
+ if (filelist_cache_previews_update(sfile->files)) {
+ ED_area_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ }
+ break;
}
break;
}
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 2a066726d96..02a36eaef37 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -590,6 +590,7 @@ typedef struct FileSelectParams {
char filter_glob[64]; /* list of filetypes to filter */
char filter_search[64]; /* text items' name must match to be shown. */
+ int filter_id; /* same as filter, but for ID types (aka library groups). */
int active_file; /* active file used for keyboard navigation */
int highlight_file; /* file under cursor */
@@ -603,7 +604,9 @@ typedef struct FileSelectParams {
short flag; /* settings for filter, hiding dots files,... */
short sort; /* sort order */
short display; /* display mode flag */
- short filter; /* filter when (flags & FILE_FILTER) is true */
+ int filter; /* filter when (flags & FILE_FILTER) is true */
+
+ short recursion_level; /* max number of levels in dirtree to show at once, 0 to disable recursion. */
/* XXX --- still unused -- */
short f_fp; /* show font preview */
@@ -635,6 +638,7 @@ typedef struct SpaceFile {
struct wmOperator *op;
struct wmTimer *smoothscroll_timer;
+ struct wmTimer *previews_timer;
struct FileLayout *layout;
@@ -709,7 +713,10 @@ typedef enum eFileSel_Params_Flag {
} eFileSel_Params_Flag;
-/* files in filesel list: file types */
+/* files in filesel list: file types
+ * Note we could use mere values (instead of bitflags) for file types themselves,
+ * but since we do not lack of bytes currently...
+ */
typedef enum eFileSel_File_Types {
FILE_TYPE_BLENDER = (1 << 2),
FILE_TYPE_BLENDER_BACKUP = (1 << 3),
@@ -725,6 +732,9 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_COLLADA = (1 << 13),
FILE_TYPE_OPERATOR = (1 << 14), /* from filter_glob operator property */
FILE_TYPE_APPLICATIONBUNDLE = (1 << 15),
+
+ FILE_TYPE_DIR = (1 << 30), /* An FS directory (i.e. S_ISDIR on its path is true). */
+ FILE_TYPE_BLENDERLIB = (1 << 31),
} eFileSel_File_Types;
/* Selection Flags in filesel: struct direntry, unsigned char selflag */
@@ -735,6 +745,117 @@ typedef enum eDirEntry_SelectFlag {
FILE_SEL_EDITING = (1 << 4),
} eDirEntry_SelectFlag;
+#define FILE_LIST_MAX_RECURSION 4
+
+/* ***** Related to file browser, but never saved in DNA, only here to help with RNA. ***** */
+
+/* About Unique identifier.
+ * Stored in a CustomProps once imported.
+ * Each engine is free to use it as it likes - it will be the only thing passed to it by blender to identify
+ * asset/variant/version (concatenating the three into a single 48 bytes one).
+ * Assumed to be 128bits, handled as four integers due to lack of real bytes proptype in RNA :|.
+ */
+#define ASSET_UUID_LENGTH 16
+
+/* Used to communicate with asset engines outside of 'import' context. */
+typedef struct AssetUUID {
+ int uuid_asset[4];
+ int uuid_variant[4];
+ int uuid_revision[4];
+} AssetUUID;
+
+typedef struct AssetUUIDList {
+ AssetUUID *uuids;
+ int nbr_uuids, pad;
+} AssetUUIDList;
+
+/* Container for a revision, only relevant in asset context. */
+typedef struct FileDirEntryRevision {
+ struct FileDirEntryRevision *next, *prev;
+
+ int uuid[4];
+ char *comment;
+
+ uint64_t size;
+ int64_t time;
+ /* Temp caching of UI-generated strings... */
+ char size_str[16];
+ char time_str[8];
+ char date_str[16];
+} FileDirEntryRevision;
+
+/* Container for a variant, only relevant in asset context.
+ * In case there are no variants, a single one shall exist, with NULL name/description. */
+typedef struct FileDirEntryVariant {
+ struct FileDirEntryVariant *next, *prev;
+
+ int uuid[4];
+ char *name;
+ char *description;
+
+ ListBase revisions;
+ int nbr_revisions;
+ int act_revision;
+} FileDirEntryVariant;
+
+/* Container for mere direntry, with additional asset-related data. */
+typedef struct FileDirEntry {
+ struct FileDirEntry *next, *prev;
+
+ int uuid[4];
+ char *name;
+ char *description;
+
+ /* Either point to active variant/revision if available, or own entry (in mere filebrowser case). */
+ FileDirEntryRevision *entry;
+
+ int typeflag; /* eFileSel_File_Types */
+ int blentype; /* ID type, in case typeflag has FILE_TYPE_BLENDERLIB set. */
+
+ char *relpath;
+
+ void *poin; /* TODO: make this a real ID pointer? */
+ struct ImBuf *image;
+
+ /* Tags are for info only, most of filtering is done in asset engine. */
+ char **tags;
+ int nbr_tags;
+
+ short status;
+ short flags;
+
+ ListBase variants;
+ int nbr_variants;
+ int act_variant;
+} FileDirEntry;
+
+/* Array of direntries. */
+/* This struct is used in various, different contexts.
+ * In Filebrowser UI, it stores the total number of available entries, the number of visible (filtered) entries,
+ * and a subset of those in 'entries' ListBase, from idx_start (included) to idx_end (excluded).
+ * In AssetEngine context (i.e. outside of 'browsing' context), entries contain all needed data, there is no filtering,
+ * so nbr_entries_filtered, entry_idx_start and entry_idx_end should all be set to -1.
+ */
+typedef struct FileDirEntryArr {
+ ListBase entries;
+ int nbr_entries;
+ int nbr_entries_filtered;
+ int entry_idx_start, entry_idx_end;
+
+ char root[1024]; /* FILE_MAX */
+} FileDirEntryArr;
+
+/* FileDirEntry.status */
+enum {
+ ASSET_STATUS_LOCAL = 1 << 0, /* If active uuid is available localy/immediately. */
+ ASSET_STATUS_LATEST = 1 << 1, /* If active uuid is latest available version. */
+};
+
+/* FileDirEntry.flags */
+enum {
+ FILE_ENTRY_INVALID_PREVIEW = 1 << 0, /* The preview for this entry could not be generated. */
+};
+
/* Image/UV Editor ======================================== */
/* Image/UV Editor */
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 261644c739a..864544aaddd 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -219,6 +219,16 @@ static EnumPropertyItem buttons_texture_context_items[] = {
{0, NULL, 0, NULL, NULL}
};
+
+static EnumPropertyItem fileselectparams_recursion_level_items[] = {
+ {0, "NONE", 0, "None", "Only list current directory's content, with no recursion"},
+ {1, "BLEND", 0, "Blend File", "List .blend files' content"},
+ {2, "ALL_1", 0, "One Level", "List all sub-directories' content, one level of recursion"},
+ {3, "ALL_2", 0, "Two Levels", "List all sub-directories' content, two levels of recursion"},
+ {4, "ALL_3", 0, "Three Levels", "List all sub-directories' content, three levels of recursion"},
+ {0, NULL, 0, NULL, NULL}
+};
+
#ifdef RNA_RUNTIME
#include "DNA_anim_types.h"
@@ -1522,6 +1532,37 @@ static void rna_SpaceClipEditor_view_type_update(Main *UNUSED(bmain), Scene *UNU
/* File browser. */
+static int rna_FileSelectParams_use_lib_get(PointerRNA *ptr)
+{
+ FileSelectParams *params = ptr->data;
+
+ return params && (params->type == FILE_LOADLIB);
+}
+
+static EnumPropertyItem *rna_FileSelectParams_recursion_level_itemf(
+ bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ FileSelectParams *params = ptr->data;
+
+ if (params && params->type != FILE_LOADLIB) {
+ EnumPropertyItem *item = NULL;
+ int totitem = 0;
+
+ RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 0);
+ RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 2);
+ RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 3);
+ RNA_enum_items_add_value(&item, &totitem, fileselectparams_recursion_level_items, 4);
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+ }
+
+ *r_free = false;
+ return fileselectparams_recursion_level_items;
+}
+
static void rna_FileBrowser_FSMenuEntry_path_get(PointerRNA *ptr, char *value)
{
char *path = ED_fsmenu_entry_get_path(ptr->data);
@@ -3710,6 +3751,58 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
+ static EnumPropertyItem file_filter_idtypes_items[] = {
+ {FILTER_ID_AC, "ACTION", ICON_ANIM_DATA, "Actions", "Show/hide Action datablocks"},
+ {FILTER_ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armatures", "Show/hide Armature datablocks"},
+ {FILTER_ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brushes", "Show/hide Brushes datablocks"},
+ {FILTER_ID_CA, "CAMERA", ICON_CAMERA_DATA, "Cameras", "Show/hide Camera datablocks"},
+ {FILTER_ID_CU, "CURVE", ICON_CURVE_DATA, "Curves", "Show/hide Curve datablocks"},
+ {FILTER_ID_GD, "GREASE_PENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Show/hide Grease pencil datablocks"},
+ {FILTER_ID_GR, "GROUP", ICON_GROUP, "Groups", "Show/hide Group datablocks"},
+ {FILTER_ID_IM, "IMAGE", ICON_IMAGE_DATA, "Images", "Show/hide Image datablocks"},
+ {FILTER_ID_LA, "LAMP", ICON_LAMP_DATA, "Lamps", "Show/hide Lamp datablocks"},
+ {FILTER_ID_LS, "LINESTYLE", ICON_LINE_DATA, "Freestyle Linestyles", "Show/hide Freestyle's Line Style datablocks"},
+ {FILTER_ID_LT, "LATTICE", ICON_LATTICE_DATA, "Lattices", "Show/hide Lattice datablocks"},
+ {FILTER_ID_MA, "MATERIAL", ICON_MATERIAL_DATA, "Materials", "Show/hide Material datablocks"},
+ {FILTER_ID_MB, "METABALL", ICON_META_DATA, "Metaballs", "Show/hide Mateball datablocks"},
+ {FILTER_ID_MC, "MOVIE_CLIP", ICON_CLIP, "Movie Clips", "Show/hide Movie Clip datablocks"},
+ {FILTER_ID_ME, "MESH", ICON_MESH_DATA, "Meshes", "Show/hide Mesh datablocks"},
+ {FILTER_ID_MSK, "MASK", ICON_MOD_MASK, "Masks", "Show/hide Mask datablocks"},
+ {FILTER_ID_NT, "NODE_TREE", ICON_NODETREE, "Node Trees", "Show/hide Node Tree datablocks"},
+ {FILTER_ID_OB, "OBJECT", ICON_OBJECT_DATA, "Objects", "Show/hide Object datablocks"},
+ {FILTER_ID_PAL, "PALETTE", ICON_COLOR, "Palettes", "Show/hide Palette datablocks"},
+ {FILTER_ID_PC, "PAINT_CURVE", ICON_CURVE_BEZCURVE, "Paint Curves", "Show/hide Paint Curve datablocks"},
+ {FILTER_ID_SCE, "SCENE", ICON_SCENE_DATA, "Scenes", "Show/hide Scene datablocks"},
+ {FILTER_ID_SPK, "SPEAKER", ICON_SPEAKER, "Speakers", "Show/hide Speaker datablocks"},
+ {FILTER_ID_SO, "SOUND", ICON_SOUND, "Sounds", "Show/hide Sound datablocks"},
+ {FILTER_ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Textures", "Show/hide Texture datablocks"},
+ {FILTER_ID_TXT, "TEXT", ICON_TEXT, "Texts", "Show/hide Text datablocks"},
+ {FILTER_ID_VF, "FONT", ICON_FONT_DATA, "Fonts", "Show/hide Font datablocks"},
+ {FILTER_ID_WO, "WORLD", ICON_WORLD_DATA, "Worlds", "Show/hide World datablocks"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem file_filter_idcategories_items[] = {
+ {FILTER_ID_SCE,
+ "SCENE", ICON_SCENE_DATA, "Scenes", "Show/hide scenes"},
+ {FILTER_ID_AC,
+ "ANIMATION", ICON_ANIM_DATA, "Animations", "Show/hide animation data"},
+ {FILTER_ID_OB | FILTER_ID_GR,
+ "OBJECT", ICON_GROUP, "Objects & Groups", "Show/hide objects and groups"},
+ {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME,
+ "GEOMETRY", ICON_MESH_DATA, "Geometry", "Show/hide meshes, curves, lattice, armatures and metaballs data"},
+ {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE,
+ "SHADING", ICON_MATERIAL_DATA, "Shading",
+ "Show/hide materials, nodetrees, textures and Freestyle's linestyles"},
+ {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO,
+ "IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"},
+ {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO,
+ "ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"},
+ {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF,
+ "MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
srna = RNA_def_struct(brna, "FileSelectParams", NULL);
RNA_def_struct_ui_text(srna, "File Select Parameters", "File Select Parameters");
@@ -3728,12 +3821,23 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "File Name", "Active file in the file browser");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ prop = RNA_def_property(srna, "use_library_browsing", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Library Browser", "Whether we may browse blender files' content or not");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_FileSelectParams_use_lib_get", NULL);
+
prop = RNA_def_property(srna, "display_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "display");
RNA_def_property_enum_items(prop, file_display_items);
RNA_def_property_ui_text(prop, "Display Mode", "Display mode for the file list");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ prop = RNA_def_property(srna, "recursion_level", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, fileselectparams_recursion_level_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_FileSelectParams_recursion_level_itemf");
+ RNA_def_property_ui_text(prop, "Recursion", "Numbers of dirtree levels to show simultaneously");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
prop = RNA_def_property(srna, "use_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_FILTER);
RNA_def_property_ui_text(prop, "Filter Files", "Enable filtering of files");
@@ -3803,7 +3907,27 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Filter Folder", "Show folders");
RNA_def_property_ui_icon(prop, ICON_FILE_FOLDER, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
-
+
+ prop = RNA_def_property(srna, "use_filter_blendid", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "filter", FILE_TYPE_BLENDERLIB);
+ RNA_def_property_ui_text(prop, "Filter Blender IDs", "Show .blend files items (objects, materials, etc.)");
+ RNA_def_property_ui_icon(prop, ICON_BLENDER, 0);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
+ prop = RNA_def_property(srna, "filter_id", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "filter_id");
+ RNA_def_property_enum_items(prop, file_filter_idtypes_items);
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_ui_text(prop, "Filter ID types", "Which ID types to show/hide, when browsing a library");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
+ prop = RNA_def_property(srna, "filter_id_category", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "filter_id");
+ RNA_def_property_enum_items(prop, file_filter_idcategories_items);
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_ui_text(prop, "Filter ID categories", "Which ID categories to show/hide, when browsing a library");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
prop = RNA_def_property(srna, "filter_glob", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "filter_glob");
RNA_def_property_ui_text(prop, "Extension Filter", "");
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 274265773a4..745f211a0da 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -417,7 +417,7 @@ enum {
WM_JOB_TYPE_OBJECT_SIM_FLUID,
WM_JOB_TYPE_OBJECT_BAKE_TEXTURE,
WM_JOB_TYPE_OBJECT_BAKE,
- WM_JOB_TYPE_FILESEL_THUMBNAIL,
+ WM_JOB_TYPE_FILESEL_READDIR,
WM_JOB_TYPE_CLIP_BUILD_PROXY,
WM_JOB_TYPE_CLIP_TRACK_MARKERS,
WM_JOB_TYPE_CLIP_SOLVE_CAMERA,
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 109ccc27d79..3c0e99bddd0 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -361,6 +361,7 @@ typedef struct wmNotifier {
#define ND_SPACE_NODE_VIEW (17<<16)
#define ND_SPACE_CHANGED (18<<16) /*sent to a new editor type after it's replaced an old one*/
#define ND_SPACE_CLIP (19<<16)
+#define ND_SPACE_FILE_PREVIEW (20<<16)
/* subtype, 256 entries too */
#define NOTE_SUBTYPE 0x0000FF00
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 15aab9faa00..516164b540a 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -1262,6 +1262,8 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type,
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_int(ot->srna, "filemode", type, FILE_LOADLIB, FILE_SPECIAL,
"File Browser Mode", "The setting for the file browser mode to load a .blend file, a library or a special file",
@@ -2594,28 +2596,121 @@ static short wm_link_append_flag(wmOperator *op)
return flag;
}
-static int wm_link_append_exec(bContext *C, wmOperator *op)
+/* Helper.
+ * if `name` is non-NULL, we assume a single-item link/append.
+ * else if `*todo_libraries` is NULL we assume first-run.
+ */
+static void wm_link_append_do_libgroup(
+ bContext *C, wmOperator *op, const char *root, const char *libname, char *group, char *name,
+ const short flag, GSet **todo_libraries)
{
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- Main *mainl = NULL;
+ Main *mainl;
BlendHandle *bh;
Library *lib;
+
+ char path[FILE_MAX_LIBEXTRA], relname[FILE_MAX];
+ int idcode;
+ const bool is_first_run = (*todo_libraries == NULL);
+
+ BLI_assert(group);
+ idcode = BKE_idcode_from_name(group);
+
+ bh = BLO_blendhandle_from_file(libname, op->reports);
+
+ if (bh == NULL) {
+ /* unlikely since we just browsed it, but possible
+ * error reports will have been made by BLO_blendhandle_from_file() */
+ return;
+ }
+
+ /* here appending/linking starts */
+ mainl = BLO_library_append_begin(bmain, &bh, libname);
+ lib = mainl->curlib;
+ BLI_assert(lib);
+
+ if (mainl->versionfile < 250) {
+ BKE_reportf(op->reports, RPT_WARNING,
+ "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will "
+ "be done! You may want to re-save your lib file with current Blender",
+ mainl->versionfile, mainl->subversionfile);
+ }
+
+ if (name) {
+ BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
+ }
+ else {
+ if (is_first_run) {
+ *todo_libraries = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
+ }
+
+ RNA_BEGIN (op->ptr, itemptr, "files")
+ {
+ char curr_libname[FILE_MAX];
+ int curr_idcode;
+
+ RNA_string_get(&itemptr, "name", relname);
+
+ BLI_join_dirfile(path, sizeof(path), root, relname);
+
+ if (BLO_library_path_explode(path, curr_libname, &group, &name)) {
+ if (!group || !name) {
+ continue;
+ }
+
+ curr_idcode = BKE_idcode_from_name(group);
+
+ if ((idcode == curr_idcode) && (BLI_path_cmp(curr_libname, libname) == 0)) {
+ BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
+ }
+ else if (is_first_run) {
+ BLI_join_dirfile(path, sizeof(path), curr_libname, group);
+ if (!BLI_gset_haskey(*todo_libraries, path)) {
+ BLI_gset_insert(*todo_libraries, BLI_strdup(path));
+ }
+ }
+ }
+ }
+ RNA_END;
+ }
+ BLO_library_append_end(C, mainl, &bh, idcode, flag);
+
+ BLO_blendhandle_close(bh);
+
+ /* mark all library linked objects to be updated */
+ BKE_main_lib_objects_recalc_all(bmain);
+ IMB_colormanagement_check_file_config(bmain);
+
+ /* append, rather than linking */
+ if ((flag & FILE_LINK) == 0) {
+ BLI_assert(BLI_findindex(&bmain->library, lib) != -1);
+ BKE_library_make_local(bmain, lib, true);
+ }
+}
+
+static int wm_link_append_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
PropertyRNA *prop;
- char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX];
- char *group;
- int idcode, totfiles = 0;
+ char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX];
+ char *group, *name;
+ int totfiles = 0;
short flag;
- RNA_string_get(op->ptr, "filename", name);
- RNA_string_get(op->ptr, "directory", dir);
+ GSet *todo_libraries = NULL;
+
+ RNA_string_get(op->ptr, "filename", relname);
+ RNA_string_get(op->ptr, "directory", root);
+
+ BLI_join_dirfile(path, sizeof(path), root, relname);
/* test if we have a valid data */
- if (BLO_library_path_explode(dir, libname, &group, NULL) == 0) {
+ if (!BLO_library_path_explode(path, libname, &group, &name)) {
BKE_report(op->reports, RPT_ERROR, "Not a library");
return OPERATOR_CANCELLED;
}
- else if ((group == NULL) || (group[0] == '\0')) {
+ else if (!group) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
@@ -2629,35 +2724,17 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
if (prop) {
totfiles = RNA_property_collection_length(op->ptr, prop);
if (totfiles == 0) {
- if (name[0] == '\0') {
+ if (!name) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
}
}
- else if (name[0] == '\0') {
+ else if (!name) {
BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
return OPERATOR_CANCELLED;
}
- bh = BLO_blendhandle_from_file(libname, op->reports);
-
- if (bh == NULL) {
- /* unlikely since we just browsed it, but possible
- * error reports will have been made by BLO_blendhandle_from_file() */
- return OPERATOR_CANCELLED;
- }
-
-
- /* from here down, no error returns */
-
- idcode = BKE_idcode_from_name(group);
-
- /* now we have or selected, or an indicated file */
- if (RNA_boolean_get(op->ptr, "autoselect"))
- BKE_scene_base_deselect_all(scene);
-
-
flag = wm_link_append_flag(op);
/* sanity checks for flag */
@@ -2667,46 +2744,36 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
flag &= ~FILE_GROUP_INSTANCE;
}
+ /* from here down, no error returns */
+ /* now we have or selected, or an indicated file */
+ if (RNA_boolean_get(op->ptr, "autoselect"))
+ BKE_scene_base_deselect_all(scene);
+
/* tag everything, all untagged data can be made local
* its also generally useful to know what is new
*
* take extra care BKE_main_id_flag_all(LIB_LINK_TAG, false) is called after! */
BKE_main_id_flag_all(bmain, LIB_PRE_EXISTING, 1);
- /* here appending/linking starts */
- mainl = BLO_library_append_begin(bmain, &bh, libname);
- lib = mainl->curlib;
- BLI_assert(lib);
-
- if (mainl->versionfile < 250) {
- BKE_reportf(op->reports, RPT_WARNING,
- "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will "
- "be done! You may want to re-save your lib file with current Blender",
- mainl->versionfile, mainl->subversionfile);
+ if (totfiles != 0) {
+ name = NULL;
}
- if (totfiles == 0) {
- BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
- }
- else {
- RNA_BEGIN (op->ptr, itemptr, "files")
- {
- RNA_string_get(&itemptr, "name", name);
- BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
+ wm_link_append_do_libgroup(C, op, root, libname, group, name, flag, &todo_libraries);
+
+ if (todo_libraries) {
+ GSetIterator libs_it;
+
+ GSET_ITER(libs_it, todo_libraries) {
+ char *libpath = (char *)BLI_gsetIterator_getKey(&libs_it);
+
+ BLO_library_path_explode(libpath, libname, &group, NULL);
+
+ wm_link_append_do_libgroup(C, op, root, libname, group, NULL, flag, &todo_libraries);
}
- RNA_END;
- }
- BLO_library_append_end(C, mainl, &bh, idcode, flag);
-
- /* mark all library linked objects to be updated */
- BKE_main_lib_objects_recalc_all(bmain);
- IMB_colormanagement_check_file_config(bmain);
- /* append, rather than linking */
- if ((flag & FILE_LINK) == 0) {
- BLI_assert(BLI_findindex(&bmain->library, lib) != -1);
- BKE_library_make_local(bmain, lib, true);
+ BLI_gset_free(todo_libraries, MEM_freeN);
}
/* important we unset, otherwise these object wont
@@ -2718,10 +2785,9 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
/* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
GPU_materials_free();
- BLO_blendhandle_close(bh);
/* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
- BLI_strncpy(G.lib, dir, FILE_MAX);
+ BLI_strncpy(G.lib, root, FILE_MAX);
WM_event_add_notifier(C, NC_WINDOW, NULL);
@@ -2761,7 +2827,7 @@ static void WM_OT_link(wmOperatorType *ot)
ot->flag |= OPTYPE_UNDO;
WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_LOADLIB, FILE_OPENFILE,
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY);
@@ -2781,7 +2847,7 @@ static void WM_OT_append(wmOperatorType *ot)
ot->flag |= OPTYPE_UNDO;
WM_operator_properties_filesel(
- ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_LOADLIB, FILE_OPENFILE,
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY);