Final 'FileBrowser First Stage' merge.
authorBastien Montagne <montagne29@wanadoo.fr>
Wed, 19 Aug 2015 20:41:39 +0000 (22:41 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Wed, 19 Aug 2015 20:41:39 +0000 (22:41 +0200)
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. :)

21 files changed:
release/scripts/startup/bl_ui/space_filebrowser.py
source/blender/blenlib/BLI_fileops.h
source/blender/blenlib/BLI_fileops_types.h
source/blender/blenlib/intern/BLI_filelist.c
source/blender/editors/include/ED_fileselect.h
source/blender/editors/interface/interface_icons.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/space_file/CMakeLists.txt
source/blender/editors/space_file/SConscript
source/blender/editors/space_file/file_draw.c
source/blender/editors/space_file/file_intern.h
source/blender/editors/space_file/file_ops.c
source/blender/editors/space_file/filelist.c
source/blender/editors/space_file/filelist.h
source/blender/editors/space_file/filesel.c
source/blender/editors/space_file/space_file.c
source/blender/makesdna/DNA_space_types.h
source/blender/makesrna/intern/rna_space.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_operators.c

index 0d900a41f25b620c4552b6a2ec644904c5e3db63..0b9c8474dd99c2a24a5fe4243c87dae40d7bd38e 100644 (file)
@@ -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__)
index 93842731ab7b50b1eab1f48633de42ac0d67c5e8..ba59c412c3a7ce29d852577be47497f968491f6b 100644 (file)
@@ -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 */
 
index 5a1126b05d20f17f75a1faa0fa0d20a22e0800a8..0cf8c8ddb4a3af11138f4be0cde4e044e0c608f4 100644 (file)
 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 {
index 38a6781c1593acaab186956496830eacfe785f9b..62690ff4f3c22a99d401cc0d4c6fb490537f0890 100644 (file)
@@ -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) {
index b81ea55cca829006337420ec5ecefabe35bbcc67..186a2a2682503704665ad4734dc55f64df27774d 100644 (file)
@@ -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);
 
index 95139c5656e6c20408f015e1ec256df400744629..5ef3b69c851cdd31aaf9f077a2962c9a4fa580b2 100644 (file)
@@ -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;
 }
 
index 6b2f12eef45fb0100f08c8113f97d14b6588cd49..44e130ed896825a8210661126d21ea0ce255dace 100644 (file)
@@ -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 */
index 185ba24eac7a5edd59094e98262d67b2befae8b5..fd701a8be4c081b64cc942decae24a6a053e6e4d 100644 (file)
@@ -31,6 +31,7 @@ set(INC
        ../../makesrna
        ../../render/extern/include
        ../../windowmanager
+       ../../../../intern/atomic
        ../../../../intern/guardedalloc
        ../../../../intern/glew-mx
 )
index 6cb7c5a69d43a479cc7343208bfc08e25c3346ce..a66a14a32deb4f38c0c145f180337ee68400300c 100644 (file)
@@ -29,6 +29,7 @@ Import ('env')
 
 sources = env.Glob('*.c')
 incs = [
+    '#/intern/atomic',
     '#/intern/guardedalloc',
     env['BF_GLEW_INC'],
     '#/intern/glew-mx',
index 9922290bf6ff932fc0d52e3702c490fb6b9d269d..c809b79bd615bc55181ae86bef523ff29fdb110d 100644 (file)
@@ -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"
 #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;
 }
index b24780c6e05c0d47272e1656378f279a6bd4f4a5..baafefab1f61821141c9f525482b44efa561a23e 100644 (file)
@@ -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);
index 6bbde1e1c17055f6141595f6c914d8d2af31b05e..4e56b5b2d7da3ec16719858c1670f25f59dc173f 100644 (file)
@@ -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;
index 21a072d0adf2bedb3d9f52dfe235601ef0153978..02b31b494c2745cba3d89de768b2db2f333e9b70 100644 (file)
@@ -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>
 #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
 #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;
+
+       /* 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 FileImage {
-       struct FileImage *next, *prev;
+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;
+
+       BLI_join_dirfile(path, sizeof(path), root, file->relpath);
 
-       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)) {
+       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,218 +812,1122 @@ 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;
 }
 
-/* ********** Main ********** */
+ImBuf *filelist_geticon_image(struct FileList *filelist, const int index)
+{
+       FileDirEntry *file = filelist_geticon_get_file(filelist, index);
 
-FileList *filelist_new(short type)
+       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)
 {
-       FileList *p = MEM_callocN(sizeof(*p), __func__);
+       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;
+               }
+       }
 
-       switch (type) {
-               case FILE_MAIN:
-                       p->readf = filelist_read_main;
-                       p->filterf = is_filtered_main;
-                       break;
-               case FILE_LOADLIB:
-                       p->readf = filelist_read_library;
-                       p->filterf = is_filtered_lib;
-                       break;
-               default:
-                       p->readf = filelist_read_dir;
-                       p->filterf = is_filtered_file;
-                       break;
+       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 p;
+       return is_main ? ICON_FILE_BLANK : ICON_NONE;
 }
 
-void filelist_free(struct FileList *filelist)
+int filelist_geticon(struct FileList *filelist, const int index, const bool is_main)
 {
-       if (!filelist) {
-               printf("Attempting to delete empty filelist.\n");
-               return;
-       }
-       
-       MEM_SAFE_FREE(filelist->fidx);
-       filelist->numfiltered = 0;
-       memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
+       FileDirEntry *file = filelist_geticon_get_file(filelist, index);
 
-       filelist->need_sorting = false;
-       filelist->sort = FILE_SORT_NONE;
+       return filelist_geticon_ex(file->typeflag, file->blentype, file->relpath, is_main, false);
+}
+
+/* ********** Main ********** */
 
-       BLI_filelist_free(filelist->filelist, filelist->numfiles, NULL);
-       filelist->numfiles = 0;
-       filelist->filelist = NULL;
+static void filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir)
+{
+       BLI_make_exist(r_dir);
 }
 
-void filelist_freelib(struct FileList *filelist)
+static void filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir)
 {
-       if (filelist->libfiledata)
-               BLO_blendhandle_close(filelist->libfiledata);
-       filelist->libfiledata = NULL;
+       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);
+       }
 }
 
-BlendHandle *filelist_lib(struct FileList *filelist)
+static void filelist_checkdir_main(struct FileList *filelist, char *r_dir)
 {
-       return filelist->libfiledata;
+       /* TODO */
+       filelist_checkdir_lib(filelist, r_dir);
 }
 
-int filelist_numfiles(struct FileList *filelist)
+static void filelist_entry_clear(FileDirEntry *entry)
 {
-       return filelist->numfiltered;
+       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);
+       }
 }
 
-const char *filelist_dir(struct FileList *filelist)
+static void filelist_entry_free(FileDirEntry *entry)
 {
-       return filelist->dir;
+       filelist_entry_clear(entry);
+       MEM_freeN(entry);
 }
 
-void filelist_setdir(struct FileList *filelist, const char *dir)
+static void filelist_direntryarr_free(FileDirEntryArr *array)
 {
-       BLI_strncpy(filelist->dir, dir, sizeof(filelist->dir));
+       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;
 }
 
-short filelist_changed(struct FileList *filelist)
+static void filelist_intern_entry_free(FileListInternEntry *entry)
 {
-       return filelist->changed;
+       if (entry->relpath) {
+               MEM_freeN(entry->relpath);
+       }
+       if (entry->name) {
+               MEM_freeN(entry->name);
+       }
+       MEM_freeN(entry);
 }
 
-struct direntry *filelist_file(struct FileList *filelist, int index)
+static void filelist_intern_free(FileListIntern *filelist_intern)
 {
-       int fidx = 0;
-       
-       if ((index < 0) || (index >= filelist->numfiltered)) {
-               return NULL;
+       FileListInternEntry *entry, *entry_next;
+
+       for (entry = filelist_intern->entries.first; entry; entry = entry_next) {
+               entry_next = entry->next;
+               filelist_intern_entry_free(entry);
        }
-       fidx = filelist->fidx[index];
+       BLI_listbase_clear(&filelist_intern->entries);
 
-       return &filelist->filelist[fidx];
+       MEM_SAFE_FREE(filelist_intern->filtered);
 }
 
-int filelist_find(struct FileList *filelist, const char *filename)
+static void filelist_cache_previewf(TaskPool *pool, void *taskdata, int UNUSED(threadid))
 {
-       int fidx = -1;
-       
-       if (!filelist->fidx) 
-               return fidx;
+       FileListEntryCache *cache = taskdata;
+       FileListEntryPreview *preview;
 
-       for (fidx = 0; fidx < filelist->numfiltered; fidx++) {
-               int index = filelist->fidx[fidx];
+//     printf("%s: Start (%d)...\n", __func__, threadid);
 
-               if (STREQ(filelist->filelist[index].relname, filename)) {
-                       return fidx;
+       /* 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);
        }
 
-       return -1;
+//     printf("%s: End (%d)...\n", __func__, threadid);
 }
 
-/* would recognize .blend as well */
-static bool file_is_blend_backup(const char *str)
+static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
 {
-       const size_t a = strlen(str);
-       size_t b = 7;
-       bool retval = 0;
+       if (!cache->previews_pool) {
+               TaskScheduler *scheduler = BLI_task_scheduler_get();
+               TaskPool *pool;
+               int num_tasks = max_ii(2, BLI_system_thread_count() / 2);
 
-       if (a == 0 || b >= a) {
-               /* pass */
-       }
-       else {
-               const char *loc;
-               
-               if (a > b + 1)
-                       b++;
-               
-               /* allow .blend1 .blend2 .blend32 */
-               loc = BLI_strcasestr(str + a - b, ".blend");
-               
-               if (loc)
-                       retval = 1;
+               pool = cache->previews_pool = BLI_task_pool_create(scheduler, NULL);
+               cache->previews_todo = BLI_thread_queue_init();
+               cache->previews_done = BLI_thread_queue_init();
+
+               while (num_tasks--) {
+                       BLI_task_pool_push(pool, filelist_cache_previewf, cache, false, TASK_PRIORITY_HIGH);
+               }
+               IMB_thumb_locks_acquire();
        }
-       
-       return (retval);
+       cache->previews_timestamp = 0.0;
 }
 
-static int path_extension_type(const char *path)
+static void filelist_cache_previews_clear(FileListEntryCache *cache)
 {
-       if (BLO_has_bfile_extension(path)) {
-               return FILE_TYPE_BLENDER;
+       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--;
+               }
        }
-       else if (file_is_blend_backup(path)) {
-               return FILE_TYPE_BLENDER_BACKUP;
+//     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();
        }
-       else if (BLI_testextensie(path, ".app")) {
-               return FILE_TYPE_APPLICATIONBUNDLE;
+       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->checkdirf = filelist_checkdir_main;
+                       p->read_jobf = filelist_readjob_main;
+                       p->filterf = is_filtered_main;
+                       break;
+               case FILE_LOADLIB:
+                       p->checkdirf = filelist_checkdir_lib;
+                       p->read_jobf = filelist_readjob_lib;
+                       p->filterf = is_filtered_lib;
+                       break;
+               default:
+                       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) {
+               printf("Attempting to delete empty filelist.\n");
+               return;
+       }
+       
+       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->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
+       filelist->sort = FILE_SORT_NONE;
+}
+
+void filelist_freelib(struct FileList *filelist)
+{
+       if (filelist->libfiledata)
+               BLO_blendhandle_close(filelist->libfiledata);
+       filelist->libfiledata = NULL;
+}
+
+BlendHandle *filelist_lib(struct FileList *filelist)
+{
+       return filelist->libfiledata;
+}
+
+static const char *fileentry_uiname(const char *root, const char *relpath, const int typeflag, char *buff)
+{
+       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->filelist.root;
+}
+
+/**
+ * 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)
+{
+#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;
+       }
+}
+
+void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
+{
+       if (filelist->max_recursion != recursion_level) {
+               filelist->max_recursion = recursion_level;
+               filelist->flags |= FL_FORCE_RESET;
+       }
+}
+
+bool filelist_force_reset(struct FileList *filelist)
+{
+       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;
+       }
+
+//     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_file_findpath(struct FileList *filelist, const char *filename)
+{
+       int fidx = -1;
+       
+       if (filelist->filelist.nbr_entries_filtered < 0) {
+               return 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. */
+
+       for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+               FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
+               if (STREQ(entry->relpath, filename)) {
+                       return fidx;
+               }
+       }
+
+       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)
+{
+       const size_t a = strlen(str);
+       size_t b = 7;
+       bool retval = 0;
+
+       if (a == 0 || b >= a) {
+               /* pass */
+       }
+       else {
+               const char *loc;
+               
+               if (a > b + 1)
+                       b++;
+               
+               /* allow .blend1 .blend2 .blend32 */
+               loc = BLI_strcasestr(str + a - b, ".blend");
+               
+               if (loc)
+                       retval = 1;
+       }
+       
+       return (retval);
+}
+
+static int path_extension_type(const char *path)
+{
+       if (BLO_has_bfile_extension(path)) {
+               return FILE_TYPE_BLENDER;
+       }
+       else if (file_is_blend_backup(path)) {
+               return FILE_TYPE_BLENDER_BACKUP;
+       }
+       else if (BLI_testextensie(path, ".app")) {
+               return FILE_TYPE_APPLICATIONBUNDLE;
        }
        else if (BLI_testextensie(path, ".py")) {
                return FILE_TYPE_PYSCRIPT;
@@ -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;
-
-               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;
-               }
-       }
+       BLO_blendhandle_close(libfiledata);
 
-       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 (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 (!*tj->stop) {
-               tj->filelist->need_thumbnails = false;
+       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);
+
+       MEM_freeN(flrj);
+}
 
-void thumbnails_start(FileList *filelist, const bContext *C)
+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);
 }
index 83f7718f0de41f072da950ce5b70cd84e6c2d997..d70faab1d6a57fb853f3ba9a37f755e193578bf5 100644 (file)
@@ -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
 }
index 21db41b27a54b2880b85422d47658248f6e6b95f..f1f202159bead4e0828b2b70dcb959a6e69748a8 100644 (file)
@@ -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;
        }
index a8b71238e142e0f1a98c806f9b968ca66d621457..eca573c27f029f0e074ce58620c8d3c4627ec3b2 100644 (file)
@@ -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;
        }
index 2a066726d964c4d02eb14822b563bc34dd1fe72f..02a36eaef371a7a63c1d65cfd27a594ed72bf303 100644 (file)
@@ -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 */
index 261644c739ace10ee97040c1a0530a7ee62965ba..864544aaddd2f1969909ecb63a63cded8addde7f 100644 (file)
@@ -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", "");
index 274265773a4370c730a6a94d6a46fa77a39dea3a..745f211a0da583a494b95b72d3788178c2b17471 100644 (file)
@@ -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,
index 109ccc27d7953f621a17d543a18afb86e914cce7..3c0e99bddd0711beb32364a8dd29b0f8d728b1e5 100644 (file)
@@ -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
index 15aab9faa004729def18fc6a2eb551b38be3941e..516164b540a87f9baea7c8448455a43d5e3b87d8 100644 (file)
@@ -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);