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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_file/filelist.c')
-rw-r--r--source/blender/editors/space_file/filelist.c655
1 files changed, 509 insertions, 146 deletions
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index e87142a7096..5dc5f741ac3 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -53,13 +53,17 @@
# include "BLI_winstuff.h"
#endif
+#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_main_idmap.h"
#include "BLO_readfile.h"
+#include "DNA_asset_types.h"
#include "DNA_space_types.h"
#include "ED_datafiles.h"
@@ -82,6 +86,8 @@
#include "filelist.h"
+#define FILEDIR_NBR_ENTRIES_UNSET -1
+
/* ----------------- FOLDERLIST (previous/next) -------------- */
typedef struct FolderList {
@@ -89,12 +95,6 @@ typedef struct FolderList {
char *foldername;
} FolderList;
-ListBase *folderlist_new(void)
-{
- ListBase *p = MEM_callocN(sizeof(*p), __func__);
- return p;
-}
-
void folderlist_popdir(struct ListBase *folderlist, char *dir)
{
const char *prev_dir;
@@ -117,6 +117,10 @@ void folderlist_popdir(struct ListBase *folderlist, char *dir)
void folderlist_pushdir(ListBase *folderlist, const char *dir)
{
+ if (!dir[0]) {
+ return;
+ }
+
struct FolderList *folder, *previous_folder;
previous_folder = folderlist->last;
@@ -153,7 +157,7 @@ int folderlist_clear_next(struct SpaceFile *sfile)
struct FolderList *folder;
/* if there is no folder_next there is nothing we can clear */
- if (!sfile->folders_next) {
+ if (BLI_listbase_is_empty(sfile->folders_next)) {
return 0;
}
@@ -180,23 +184,79 @@ void folderlist_free(ListBase *folderlist)
}
}
-ListBase *folderlist_duplicate(ListBase *folderlist)
+static ListBase folderlist_duplicate(ListBase *folderlist)
{
+ ListBase folderlistn = {NULL};
- if (folderlist) {
- ListBase *folderlistn = MEM_callocN(sizeof(*folderlistn), __func__);
- FolderList *folder;
+ BLI_duplicatelist(&folderlistn, folderlist);
+
+ for (FolderList *folder = folderlistn.first; folder; folder = folder->next) {
+ folder->foldername = MEM_dupallocN(folder->foldername);
+ }
+ return folderlistn;
+}
- BLI_duplicatelist(folderlistn, folderlist);
+/* ----------------- Folder-History (wraps/owns file list above) -------------- */
- for (folder = folderlistn->first; folder; folder = folder->next) {
- folder->foldername = MEM_dupallocN(folder->foldername);
+static FileFolderHistory *folder_history_find(const SpaceFile *sfile, eFileBrowse_Mode browse_mode)
+{
+ LISTBASE_FOREACH (FileFolderHistory *, history, &sfile->folder_histories) {
+ if (history->browse_mode == browse_mode) {
+ return history;
}
- return folderlistn;
}
+
return NULL;
}
+void folder_history_list_ensure_for_active_browse_mode(SpaceFile *sfile)
+{
+ FileFolderHistory *history = folder_history_find(sfile, sfile->browse_mode);
+
+ if (!history) {
+ history = MEM_callocN(sizeof(*history), __func__);
+ history->browse_mode = sfile->browse_mode;
+ BLI_addtail(&sfile->folder_histories, history);
+ }
+
+ sfile->folders_next = &history->folders_next;
+ sfile->folders_prev = &history->folders_prev;
+}
+
+static void folder_history_entry_free(SpaceFile *sfile, FileFolderHistory *history)
+{
+ if (sfile->folders_prev == &history->folders_prev) {
+ sfile->folders_prev = NULL;
+ }
+ if (sfile->folders_next == &history->folders_next) {
+ sfile->folders_next = NULL;
+ }
+ folderlist_free(&history->folders_prev);
+ folderlist_free(&history->folders_next);
+ BLI_freelinkN(&sfile->folder_histories, history);
+}
+
+void folder_history_list_free(SpaceFile *sfile)
+{
+ LISTBASE_FOREACH_MUTABLE (FileFolderHistory *, history, &sfile->folder_histories) {
+ folder_history_entry_free(sfile, history);
+ }
+}
+
+ListBase folder_history_list_duplicate(ListBase *listbase)
+{
+ ListBase histories = {NULL};
+
+ LISTBASE_FOREACH (FileFolderHistory *, history, listbase) {
+ FileFolderHistory *history_new = MEM_dupallocN(history);
+ history_new->folders_prev = folderlist_duplicate(&history->folders_prev);
+ history_new->folders_next = folderlist_duplicate(&history->folders_next);
+ BLI_addtail(&histories, history_new);
+ }
+
+ return histories;
+}
+
/* ------------------FILELIST------------------------ */
typedef struct FileListInternEntry {
@@ -216,6 +276,24 @@ typedef struct FileListInternEntry {
/** not strictly needed, but used during sorting, avoids to have to recompute it there... */
char *name;
+ /**
+ * This is data from the current main, represented by this file. It's crucial that this is
+ * updated correctly on undo, redo and file reading (without UI). The space is responsible to
+ * take care of that.
+ */
+ struct {
+ /** When showing local IDs (FILE_MAIN, FILE_MAIN_ASSET), the ID this file entry represents. */
+ ID *id;
+
+ /* For the few file types that have the preview already in memory. For others, there's delayed
+ * preview reading from disk. Non-owning pointer. */
+ PreviewImage *preview_image;
+ } local_data;
+
+ /** When the file represents an asset read from another file, it is stored here.
+ * Owning pointer. */
+ AssetMetaData *imported_asset_data;
+
/** Defined in BLI_fileops.h */
eFileAttributes attributes;
BLI_stat_t st;
@@ -267,7 +345,11 @@ typedef struct FileListEntryPreview {
char path[FILE_MAX];
uint flags;
int index;
- ImBuf *img;
+ /* Some file types load the memory from runtime data, not from disk. We just wait until it's done
+ * generating (BKE_previewimg_is_finished()). */
+ PreviewImage *in_memory_preview;
+
+ int icon_id;
} FileListEntryPreview;
/* Dummy wrapper around FileListEntryPreview to ensure we do not access freed memory when freeing
@@ -290,11 +372,16 @@ enum {
FLF_HIDE_DOT = 1 << 1,
FLF_HIDE_PARENT = 1 << 2,
FLF_HIDE_LIB_DIR = 1 << 3,
+ FLF_ASSETS_ONLY = 1 << 4,
};
typedef struct FileList {
FileDirEntryArr filelist;
+ eFileSelectType type;
+ /* The library this list was created for. Stored here so we know when to re-read. */
+ FileSelectAssetLibraryUID *asset_library;
+
short flags;
short sort;
@@ -324,10 +411,13 @@ typedef struct FileList {
bool (*checkdirf)(struct FileList *, char *, const bool);
/* Fill filelist (to be called by read job). */
- void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+ void (*read_jobf)(
+ Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
/* Filter an entry of current filelist. */
bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *);
+
+ short tags; /* FileListTags */
} FileList;
/* FileList.flags */
@@ -340,6 +430,14 @@ enum {
FL_SORT_INVERT = 1 << 5,
};
+/* FileList.tags */
+enum FileListTags {
+ /** The file list has references to main data (IDs) and needs special care. */
+ FILELIST_TAGS_USES_MAIN_DATA = (1 << 0),
+ /** The file list type is not thread-safe. */
+ FILELIST_TAGS_NO_THREADS = (1 << 2),
+};
+
#define SPECIAL_IMG_SIZE 256
#define SPECIAL_IMG_ROWS 1
#define SPECIAL_IMG_COLS 7
@@ -357,24 +455,34 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(FileList *filelist,
+static void filelist_readjob_main(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
-static void filelist_readjob_lib(FileList *filelist,
+static void filelist_readjob_lib(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
-static void filelist_readjob_dir(FileList *filelist,
+static void filelist_readjob_dir(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
+static void filelist_readjob_main_assets(Main *current_main,
+ FileList *filelist,
+ const char *main_name,
+ short *stop,
+ short *do_update,
+ float *progress,
+ ThreadMutex *lock);
/* helper, could probably go in BKE actually? */
static int groupname_to_code(const char *group);
@@ -694,6 +802,11 @@ static bool is_filtered_hidden(const char *filename,
return true;
}
#endif
+ /* For data-blocks (but not the group directories), check the asset-only filter. */
+ if (!(file->typeflag & FILE_TYPE_DIR) && (file->typeflag & FILE_TYPE_BLENDERLIB) &&
+ (filter->flags & FLF_ASSETS_ONLY) && !(file->typeflag & FILE_TYPE_ASSET)) {
+ return true;
+ }
return false;
}
@@ -737,51 +850,61 @@ static bool is_filtered_file(FileListInternEntry *file,
return is_filtered;
}
-static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+static bool is_filtered_id_file(const FileListInternEntry *file,
+ const char *id_group,
+ const char *name,
+ const FileListFilter *filter)
{
- bool is_filtered;
- char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
-
- BLI_join_dirfile(path, sizeof(path), root, file->relpath);
-
- if (BLO_library_path_explode(path, dir, &group, &name)) {
- is_filtered = !is_filtered_hidden(file->relpath, filter, file);
- if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
- /* We only check for types if some type are enabled in filtering. */
- if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
- 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;
- }
+ bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
+ if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
+ /* We only check for types if some type are enabled in filtering. */
+ if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
+ 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;
}
}
- if (is_filtered && group) {
- if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
+ else {
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
is_filtered = false;
}
- else {
- uint64_t filter_id = groupname_to_filter_id(group);
- if (!(filter_id & filter->filter_id)) {
- is_filtered = false;
- }
- }
}
}
- /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
- if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
+ if (is_filtered && id_group) {
+ if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
is_filtered = false;
}
+ else {
+ uint64_t filter_id = groupname_to_filter_id(id_group);
+ if (!(filter_id & filter->filter_id)) {
+ is_filtered = false;
+ }
+ }
+ }
+ }
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
+ if (is_filtered && (filter->filter_search[0] != '\0')) {
+ if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
+ is_filtered = false;
}
}
}
+
+ return is_filtered;
+}
+
+static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+{
+ bool is_filtered;
+ char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
+
+ BLI_join_dirfile(path, sizeof(path), root, file->relpath);
+
+ if (BLO_library_path_explode(path, dir, &group, &name)) {
+ is_filtered = is_filtered_id_file(file, group, name, filter);
+ }
else {
is_filtered = is_filtered_file(file, root, filter);
}
@@ -796,6 +919,14 @@ static bool is_filtered_main(FileListInternEntry *file,
return !is_filtered_hidden(file->relpath, filter, file);
}
+static bool is_filtered_main_assets(FileListInternEntry *file,
+ const char *UNUSED(dir),
+ FileListFilter *filter)
+{
+ /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */
+ return is_filtered_id_file(file, file->relpath, file->name, filter);
+}
+
static void filelist_filter_clear(FileList *filelist)
{
filelist->flags |= FL_NEED_FILTERING;
@@ -807,7 +938,7 @@ void filelist_filter(FileList *filelist)
const int num_files = filelist->filelist.nbr_entries;
FileListInternEntry **filtered_tmp, *file;
- if (filelist->filelist.nbr_entries == 0) {
+ if (ELEM(filelist->filelist.nbr_entries, FILEDIR_NBR_ENTRIES_UNSET, 0)) {
return;
}
@@ -858,6 +989,7 @@ void filelist_setfilter_options(FileList *filelist,
const bool hide_parent,
const uint64_t filter,
const uint64_t filter_id,
+ const bool filter_assets_only,
const char *filter_glob,
const char *filter_search)
{
@@ -875,6 +1007,10 @@ void filelist_setfilter_options(FileList *filelist,
filelist->filter_data.flags ^= FLF_HIDE_PARENT;
update = true;
}
+ if (((filelist->filter_data.flags & FLF_ASSETS_ONLY) != 0) != (filter_assets_only != 0)) {
+ filelist->filter_data.flags ^= FLF_ASSETS_ONLY;
+ update = true;
+ }
if (filelist->filter_data.filter != filter) {
filelist->filter_data.filter = filter;
update = true;
@@ -903,6 +1039,50 @@ void filelist_setfilter_options(FileList *filelist,
}
}
+/**
+ * Checks two libraries for equality.
+ * \return True if the libraries match.
+ */
+static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *library_a,
+ const FileSelectAssetLibraryUID *library_b)
+{
+ if (library_a->type != library_b->type) {
+ return false;
+ }
+ if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ return STREQ(library_a->custom_library_identifier, library_b->custom_library_identifier);
+ }
+
+ return true;
+}
+
+/**
+ * \param asset_library: May be NULL to unset the library.
+ */
+void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library)
+{
+ /* Unset if needed. */
+ if (!asset_library) {
+ if (filelist->asset_library) {
+ MEM_SAFE_FREE(filelist->asset_library);
+ filelist->flags |= FL_FORCE_RESET;
+ }
+ return;
+ }
+
+ if (!filelist->asset_library) {
+ filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library),
+ "filelist asset library");
+ *filelist->asset_library = *asset_library;
+
+ filelist->flags |= FL_FORCE_RESET;
+ }
+ else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) {
+ *filelist->asset_library = *asset_library;
+ filelist->flags |= FL_FORCE_RESET;
+ }
+}
+
/* ********** Icon/image helpers ********** */
void filelist_init_icons(void)
@@ -960,7 +1140,12 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index)
{
FileDirEntry *file = filelist_geticon_get_file(filelist, index);
- return file->image;
+ return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
+}
+
+ImBuf *filelist_file_getimage(const FileDirEntry *file)
+{
+ return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
}
static ImBuf *filelist_geticon_image_ex(FileDirEntry *file)
@@ -993,7 +1178,7 @@ static int filelist_geticon_ex(FileDirEntry *file,
const bool is_main,
const bool ignore_libdir)
{
- const int typeflag = file->typeflag;
+ const eFileSel_File_Types typeflag = file->typeflag;
if ((typeflag & FILE_TYPE_DIR) &&
!(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) {
@@ -1160,6 +1345,14 @@ static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const
return filelist_checkdir_lib(filelist, r_dir, do_change);
}
+static bool filelist_checkdir_main_assets(struct FileList *UNUSED(filelist),
+ char *UNUSED(r_dir),
+ const bool UNUSED(do_change))
+{
+ /* Main is always valid. */
+ return true;
+}
+
static void filelist_entry_clear(FileDirEntry *entry)
{
if (entry->name) {
@@ -1174,8 +1367,9 @@ static void filelist_entry_clear(FileDirEntry *entry)
if (entry->redirection_path) {
MEM_freeN(entry->redirection_path);
}
- if (entry->image) {
- IMB_freeImBuf(entry->image);
+ if (entry->preview_icon_id) {
+ BKE_icon_delete(entry->preview_icon_id);
+ entry->preview_icon_id = 0;
}
/* For now, consider FileDirEntryRevision::poin as not owned here,
* so no need to do anything about it */
@@ -1232,8 +1426,8 @@ static void filelist_direntryarr_free(FileDirEntryArr *array)
#else
BLI_assert(BLI_listbase_is_empty(&array->entries));
#endif
- array->nbr_entries = 0;
- array->nbr_entries_filtered = -1;
+ array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET;
array->entry_idx_start = -1;
array->entry_idx_end = -1;
}
@@ -1249,6 +1443,10 @@ static void filelist_intern_entry_free(FileListInternEntry *entry)
if (entry->name) {
MEM_freeN(entry->name);
}
+ /* If we own the asset-data (it was generated from external file data), free it. */
+ if (entry->imported_asset_data) {
+ BKE_asset_metadata_free(&entry->imported_asset_data);
+ }
MEM_freeN(entry);
}
@@ -1272,37 +1470,52 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
FileListEntryPreview *preview = preview_taskdata->preview;
ThumbSource source = 0;
+ bool done = false;
// printf("%s: Start (%d)...\n", __func__, threadid);
- // 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;
+ if (preview->in_memory_preview) {
+ if (BKE_previewimg_is_finished(preview->in_memory_preview, ICON_SIZE_PREVIEW)) {
+ ImBuf *imbuf = BKE_previewimg_to_imbuf(preview->in_memory_preview, ICON_SIZE_PREVIEW);
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
+ done = true;
+ }
}
+ else {
+ // 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));
- IMB_thumb_path_lock(preview->path);
- /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate in
- * case user switch to a bigger preview size. */
- preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source);
- IMB_thumb_path_unlock(preview->path);
+ 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);
+ /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate
+ * in case user switch to a bigger preview size. */
+ ImBuf *imbuf = IMB_thumb_manage(preview->path, THB_LARGE, source);
+ IMB_thumb_path_unlock(preview->path);
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
- /* That way task freeing function won't free th preview, since it does not own it anymore. */
- atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL);
- BLI_thread_queue_push(cache->previews_done, preview);
+ done = true;
+ }
+
+ if (done) {
+ /* That way task freeing function won't free th preview, since it does not own it anymore. */
+ atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL);
+ BLI_thread_queue_push(cache->previews_done, preview);
+ }
// printf("%s: End (%d)...\n", __func__, threadid);
}
@@ -1315,8 +1528,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void
/* preview_taskdata->preview is atomically set to NULL once preview has been processed and sent
* to previews_done queue. */
if (preview != NULL) {
- if (preview->img) {
- IMB_freeImBuf(preview->img);
+ if (preview->icon_id) {
+ BKE_icon_delete(preview->icon_id);
}
MEM_freeN(preview);
}
@@ -1342,8 +1555,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
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);
+ if (preview->icon_id) {
+ BKE_icon_delete(preview->icon_id);
}
MEM_freeN(preview);
}
@@ -1374,10 +1587,11 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
- if (!entry->image && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
+ if (!entry->preview_icon_id && !(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__);
+ FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
if (entry->redirection_path) {
BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
@@ -1389,7 +1603,8 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
preview->index = index;
preview->flags = entry->typeflag;
- preview->img = NULL;
+ preview->in_memory_preview = intern_entry->local_data.preview_image;
+ preview->icon_id = 0;
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
filelist_cache_preview_ensure_running(cache);
@@ -1497,25 +1712,45 @@ FileList *filelist_new(short type)
p->selection_state = BLI_ghash_new(
BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__);
+ p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ filelist_settype(p, type);
- switch (type) {
+ return p;
+}
+
+void filelist_settype(FileList *filelist, short type)
+{
+ if (filelist->type == type) {
+ return;
+ }
+
+ filelist->type = type;
+ filelist->tags = 0;
+ switch (filelist->type) {
case FILE_MAIN:
- p->checkdirf = filelist_checkdir_main;
- p->read_jobf = filelist_readjob_main;
- p->filterf = is_filtered_main;
+ filelist->checkdirf = filelist_checkdir_main;
+ filelist->read_jobf = filelist_readjob_main;
+ filelist->filterf = is_filtered_main;
break;
case FILE_LOADLIB:
- p->checkdirf = filelist_checkdir_lib;
- p->read_jobf = filelist_readjob_lib;
- p->filterf = is_filtered_lib;
+ filelist->checkdirf = filelist_checkdir_lib;
+ filelist->read_jobf = filelist_readjob_lib;
+ filelist->filterf = is_filtered_lib;
+ break;
+ case FILE_MAIN_ASSET:
+ filelist->checkdirf = filelist_checkdir_main_assets;
+ filelist->read_jobf = filelist_readjob_main_assets;
+ filelist->filterf = is_filtered_main_assets;
+ filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS;
break;
default:
- p->checkdirf = filelist_checkdir_dir;
- p->read_jobf = filelist_readjob_dir;
- p->filterf = is_filtered_file;
+ filelist->checkdirf = filelist_checkdir_dir;
+ filelist->read_jobf = filelist_readjob_dir;
+ filelist->filterf = is_filtered_file;
break;
}
- return p;
+
+ filelist->flags |= FL_FORCE_RESET;
}
void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
@@ -1560,6 +1795,8 @@ void filelist_free(struct FileList *filelist)
filelist->selection_state = NULL;
}
+ MEM_SAFE_FREE(filelist->asset_library);
+
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
@@ -1580,7 +1817,7 @@ BlendHandle *filelist_lib(struct FileList *filelist)
static const char *fileentry_uiname(const char *root,
const char *relpath,
- const int typeflag,
+ const eFileSel_File_Types typeflag,
char *buff)
{
char *name = NULL;
@@ -1624,11 +1861,12 @@ bool filelist_is_dir(struct FileList *filelist, const char *path)
*/
void filelist_setdir(struct FileList *filelist, char *r_dir)
{
+ const bool allow_invalid = filelist->asset_library != NULL;
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
- const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true);
- BLI_assert(is_valid_path);
+ const bool is_valid_path = filelist->checkdirf(filelist, r_dir, !allow_invalid);
+ BLI_assert(is_valid_path || allow_invalid);
UNUSED_VARS_NDEBUG(is_valid_path);
if (!STREQ(filelist->filelist.root, r_dir)) {
@@ -1645,11 +1883,16 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
}
}
-bool filelist_force_reset(struct FileList *filelist)
+bool filelist_needs_force_reset(FileList *filelist)
{
return (filelist->flags & FL_FORCE_RESET) != 0;
}
+void filelist_tag_force_reset(FileList *filelist)
+{
+ filelist->flags |= FL_FORCE_RESET;
+}
+
bool filelist_is_ready(struct FileList *filelist)
{
return (filelist->flags & FL_IS_READY) != 0;
@@ -1660,6 +1903,11 @@ bool filelist_pending(struct FileList *filelist)
return (filelist->flags & FL_IS_PENDING) != 0;
}
+bool filelist_needs_reset_on_main_changes(const FileList *filelist)
+{
+ return (filelist->tags & FILELIST_TAGS_USES_MAIN_DATA) != 0;
+}
+
/**
* Limited version of full update done by space_file's file_refresh(),
* to be used by operators and such.
@@ -1668,7 +1916,7 @@ bool filelist_pending(struct FileList *filelist)
*/
int filelist_files_ensure(FileList *filelist)
{
- if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) {
+ if (!filelist_needs_force_reset(filelist) || !filelist_needs_reading(filelist)) {
filelist_sort(filelist);
filelist_filter(filelist);
}
@@ -1701,6 +1949,17 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
if (entry->redirection_path) {
ret->redirection_path = BLI_strdup(entry->redirection_path);
}
+ ret->id = entry->local_data.id;
+ ret->asset_data = entry->imported_asset_data ? entry->imported_asset_data : NULL;
+ if (ret->id && (ret->asset_data == NULL)) {
+ ret->asset_data = ret->id->asset_data;
+ }
+ /* For some file types the preview is already available. */
+ if (entry->local_data.preview_image &&
+ BKE_previewimg_is_finished(entry->local_data.preview_image, ICON_SIZE_PREVIEW)) {
+ ImBuf *ibuf = BKE_previewimg_to_imbuf(entry->local_data.preview_image, ICON_SIZE_PREVIEW);
+ ret->preview_icon_id = BKE_icon_imbuf_create(ibuf);
+ }
BLI_addtail(&cache->cached_entries, ret);
return ret;
}
@@ -1770,7 +2029,7 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
{
int fidx = -1;
- if (filelist->filelist.nbr_entries_filtered < 0) {
+ if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
return fidx;
}
@@ -1788,9 +2047,17 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
return -1;
}
+/**
+ * Get the ID a file represents (if any). For #FILE_MAIN, #FILE_MAIN_ASSET.
+ */
+ID *filelist_file_get_id(const FileDirEntry *file)
+{
+ return file->id;
+}
+
FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4])
{
- if (filelist->filelist.nbr_entries_filtered < 0) {
+ if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
return NULL;
}
@@ -2147,15 +2414,17 @@ bool filelist_cache_previews_update(FileList *filelist)
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
- if (preview->img) {
+ if (preview->icon_id) {
/* 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;
+ if (entry && !entry->preview_icon_id) {
+ /* Move ownership over icon. */
+ entry->preview_icon_id = preview->icon_id;
+ preview->icon_id = 0;
changed = true;
}
else {
- IMB_freeImBuf(preview->img);
+ BKE_icon_delete(preview->icon_id);
}
}
else if (entry) {
@@ -2313,9 +2582,9 @@ int ED_file_extension_icon(const char *path)
}
}
-int filelist_empty(struct FileList *filelist)
+int filelist_needs_reading(struct FileList *filelist)
{
- return (filelist->filelist.nbr_entries == 0);
+ return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET);
}
uint filelist_entry_select_set(const FileList *filelist,
@@ -2564,8 +2833,8 @@ static int filelist_readjob_list_dir(const char *root,
static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
{
FileListInternEntry *entry;
- LinkNode *ln, *names;
- int i, nnames, idcode = 0, nbr_entries = 0;
+ LinkNode *ln, *names = NULL, *datablock_infos = NULL;
+ int i, nitems, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX_LIBEXTRA], *group;
bool ok;
@@ -2587,11 +2856,11 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
* and freed in filelist_entry_free. */
if (group) {
idcode = groupname_to_code(group);
- names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames);
+ datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems);
}
else {
names = BLO_blendhandle_get_linkable_groups(libfiledata);
- nnames = BLI_linklist_count(names);
+ nitems = BLI_linklist_count(names);
}
BLO_blendhandle_close(libfiledata);
@@ -2604,12 +2873,18 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
nbr_entries++;
}
- for (i = 0, ln = names; i < nnames; i++, ln = ln->next) {
- const char *blockname = ln->link;
+ for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) {
+ struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL;
+ const char *blockname = info ? info->name : ln->link;
entry = MEM_callocN(sizeof(*entry), __func__);
entry->relpath = BLI_strdup(blockname);
entry->typeflag |= FILE_TYPE_BLENDERLIB;
+ if (info && info->asset_data) {
+ entry->typeflag |= FILE_TYPE_ASSET;
+ /* Moves ownership! */
+ entry->imported_asset_data = info->asset_data;
+ }
if (!(group && idcode)) {
entry->typeflag |= FILE_TYPE_DIR;
entry->blentype = groupname_to_code(blockname);
@@ -2621,7 +2896,7 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
nbr_entries++;
}
- BLI_linklist_freeN(names);
+ BLI_linklist_freeN(datablock_infos ? datablock_infos : names);
return nbr_entries;
}
@@ -2820,7 +3095,10 @@ static void filelist_readjob_do(const bool do_lib,
// BLI_assert(filelist->filtered == NULL);
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == 0));
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty directory from now. */
+ filelist->filelist.nbr_entries = 0;
todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
td_dir = BLI_stack_push_r(todo_dirs);
@@ -2932,7 +3210,8 @@ static void filelist_readjob_do(const bool do_lib,
BLI_stack_free(todo_dirs);
}
-static void filelist_readjob_dir(FileList *filelist,
+static void filelist_readjob_dir(Main *UNUSED(current_main),
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2942,7 +3221,8 @@ static void filelist_readjob_dir(FileList *filelist,
filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
}
-static void filelist_readjob_lib(FileList *filelist,
+static void filelist_readjob_lib(Main *UNUSED(current_main),
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2952,7 +3232,8 @@ static void filelist_readjob_lib(FileList *filelist,
filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
}
-static void filelist_readjob_main(FileList *filelist,
+static void filelist_readjob_main(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2960,12 +3241,67 @@ static void filelist_readjob_main(FileList *filelist,
ThreadMutex *lock)
{
/* TODO! */
- filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock);
+}
+
+/**
+ * \warning Acts on main, so NOT thread-safe!
+ */
+static void filelist_readjob_main_assets(Main *current_main,
+ FileList *filelist,
+ const char *UNUSED(main_name),
+ short *UNUSED(stop),
+ short *do_update,
+ float *UNUSED(progress),
+ ThreadMutex *UNUSED(lock))
+{
+ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty directory from now. */
+ filelist->filelist.nbr_entries = 0;
+
+ FileListInternEntry *entry;
+ ListBase tmp_entries = {0};
+ ID *id_iter;
+ int nbr_entries = 0;
+
+ FOREACH_MAIN_ID_BEGIN (current_main, id_iter) {
+ if (!id_iter->asset_data) {
+ continue;
+ }
+
+ const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name));
+
+ entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(id_code_name);
+ entry->name = BLI_strdup(id_iter->name + 2);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_ASSET;
+ entry->blentype = GS(id_iter->name);
+ *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32(
+ (uint32_t *)filelist->filelist_intern.curr_uuid, 1);
+ entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data,
+ id_iter);
+ entry->local_data.id = id_iter;
+ nbr_entries++;
+ BLI_addtail(&tmp_entries, entry);
+ }
+ FOREACH_MAIN_ID_END;
+
+ if (nbr_entries) {
+ *do_update = true;
+
+ BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries);
+ filelist->filelist.nbr_entries += nbr_entries;
+ filelist->filelist.nbr_entries_filtered = filelist->filelist.entry_idx_start =
+ filelist->filelist.entry_idx_end = -1;
+ }
}
typedef struct FileListReadJob {
ThreadMutex lock;
char main_name[FILE_MAX];
+ Main *current_main;
struct FileList *filelist;
/** XXX We may use a simpler struct here... just a linked list and root path? */
struct FileList *tmp_filelist;
@@ -2985,7 +3321,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
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.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
flrj->tmp_filelist->filelist_intern.filtered = NULL;
BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
@@ -2996,11 +3332,17 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist->libfiledata = NULL;
memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
flrj->tmp_filelist->selection_state = NULL;
+ flrj->tmp_filelist->asset_library = NULL;
BLI_mutex_unlock(&flrj->lock);
- flrj->tmp_filelist->read_jobf(
- flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock);
+ flrj->tmp_filelist->read_jobf(flrj->current_main,
+ flrj->tmp_filelist,
+ flrj->main_name,
+ stop,
+ do_update,
+ progress,
+ &flrj->lock);
}
static void filelist_readjob_update(void *flrjv)
@@ -3015,7 +3357,7 @@ static void filelist_readjob_update(void *flrjv)
BLI_mutex_lock(&flrj->lock);
- if (flrj->tmp_filelist->filelist.nbr_entries) {
+ if (flrj->tmp_filelist->filelist.nbr_entries > 0) {
/* 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);
@@ -3033,7 +3375,7 @@ static void filelist_readjob_update(void *flrjv)
/* 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;
+ flrj->filelist->filelist.nbr_entries = MAX2(nbr_entries, 0) + new_nbr_entries;
}
static void filelist_readjob_endjob(void *flrjv)
@@ -3074,30 +3416,51 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
wmJob *wm_job;
FileListReadJob *flrj;
+ if (!filelist_is_dir(filelist, filelist->filelist.root)) {
+ return;
+ }
+
/* prepare job data */
flrj = MEM_callocN(sizeof(*flrj), __func__);
flrj->filelist = filelist;
+ flrj->current_main = bmain;
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
filelist->flags |= FL_IS_PENDING;
+ /* Init even for single threaded execution. Called functions use it. */
BLI_mutex_init(&flrj->lock);
- /* setup job */
- wm_job = WM_jobs_get(CTX_wm_manager(C),
- CTX_wm_window(C),
- CTX_data_scene(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);
+ if (filelist->tags & FILELIST_TAGS_NO_THREADS) {
+ short dummy_stop = false;
+ short dummy_do_update = false;
+ float dummy_progress = 0.0f;
+
+ /* Single threaded execution. Just directly call the callbacks. */
+ filelist_readjob_startjob(flrj, &dummy_stop, &dummy_do_update, &dummy_progress);
+ filelist_readjob_endjob(flrj);
+ filelist_readjob_free(flrj);
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+ return;
+ }
+ else {
+ /* setup job */
+ wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ CTX_data_scene(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 filelist_readjob_stop(wmWindowManager *wm, Scene *owner_scene)