diff options
Diffstat (limited to 'source/blender/editors/space_file')
-rw-r--r-- | source/blender/editors/space_file/file_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_ops.c | 9 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.c | 655 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.h | 19 | ||||
-rw-r--r-- | source/blender/editors/space_file/filesel.c | 188 | ||||
-rw-r--r-- | source/blender/editors/space_file/space_file.c | 139 |
6 files changed, 784 insertions, 227 deletions
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index b459c02d9e5..a0e02681e0e 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -90,6 +90,7 @@ void file_sfile_to_operator(struct Main *bmain, struct wmOperator *op, struct Sp void file_operator_to_sfile(struct Main *bmain, struct SpaceFile *sfile, struct wmOperator *op); /* filesel.c */ +void fileselect_refresh_params(struct SpaceFile *sfile); void fileselect_file_set(SpaceFile *sfile, const int index); bool file_attribute_column_type_enabled(const FileSelectParams *params, FileAttributeColumnType column); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index b98348307f3..8af84f65ced 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -930,6 +930,7 @@ void FILE_OT_select_all(wmOperatorType *ot) /* Note we could get rid of this one, but it's used by some addon so... * Does not hurt keeping it around for now. */ +/* TODO disallow bookmark editing in assets mode? */ static int bookmark_select_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1836,10 +1837,6 @@ static int file_previous_exec(bContext *C, wmOperator *UNUSED(op)) FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params) { - if (!sfile->folders_next) { - sfile->folders_next = folderlist_new(); - } - folderlist_pushdir(sfile->folders_next, params->dir); folderlist_popdir(sfile->folders_prev, params->dir); folderlist_pushdir(sfile->folders_next, params->dir); @@ -1874,10 +1871,6 @@ static int file_next_exec(bContext *C, wmOperator *UNUSED(unused)) SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (params) { - if (!sfile->folders_next) { - sfile->folders_next = folderlist_new(); - } - folderlist_pushdir(sfile->folders_prev, params->dir); folderlist_popdir(sfile->folders_next, params->dir); 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) diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 4b8cc052382..59bd5bb50d7 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -30,6 +30,7 @@ extern "C" { struct BlendHandle; struct FileList; struct FileSelection; +struct FileSelectAssetLibraryUID; struct wmWindowManager; struct FileDirEntry; @@ -46,14 +47,16 @@ typedef enum FileCheckType { CHECK_ALL = 3, } FileCheckType; -struct ListBase *folderlist_new(void); void folderlist_free(struct ListBase *folderlist); -struct ListBase *folderlist_duplicate(ListBase *folderlist); void folderlist_popdir(struct ListBase *folderlist, char *dir); void folderlist_pushdir(struct ListBase *folderlist, const char *dir); const char *folderlist_peeklastdir(struct ListBase *folderlist); int folderlist_clear_next(struct SpaceFile *sfile); +void folder_history_list_ensure_for_active_browse_mode(struct SpaceFile *sfile); +void folder_history_list_free(struct SpaceFile *sfile); +struct ListBase folder_history_list_duplicate(struct ListBase *listbase); + void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort); void filelist_sort(struct FileList *filelist); @@ -63,17 +66,22 @@ void filelist_setfilter_options(struct 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); void filelist_filter(struct FileList *filelist); +void filelist_setlibrary(struct FileList *filelist, + const struct FileSelectAssetLibraryUID *asset_library); void filelist_init_icons(void); void filelist_free_icons(void); struct ImBuf *filelist_getimage(struct FileList *filelist, const int index); +struct ImBuf *filelist_file_getimage(const FileDirEntry *file); 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_settype(struct FileList *filelist, 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); @@ -83,15 +91,18 @@ bool filelist_is_dir(struct FileList *filelist, const char *path); void filelist_setdir(struct FileList *filelist, char *r_dir); int filelist_files_ensure(struct FileList *filelist); -int filelist_empty(struct FileList *filelist); +int filelist_needs_reading(struct FileList *filelist); FileDirEntry *filelist_file(struct FileList *filelist, int index); int filelist_file_findpath(struct FileList *filelist, const char *file); +struct ID *filelist_file_get_id(const struct FileDirEntry *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_needs_force_reset(struct FileList *filelist); +void filelist_tag_force_reset(struct FileList *filelist); bool filelist_pending(struct FileList *filelist); +bool filelist_needs_reset_on_main_changes(const struct FileList *filelist); bool filelist_is_ready(struct FileList *filelist); unsigned int filelist_entry_select_set(const struct FileList *filelist, diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 6e933e53a8f..2c66bd39e0a 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -56,7 +56,9 @@ #include "BKE_appdir.h" #include "BKE_context.h" +#include "BKE_idtype.h" #include "BKE_main.h" +#include "BKE_preferences.h" #include "BLF_api.h" @@ -77,14 +79,66 @@ #define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X) -FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) +static void fileselect_initialize_params_common(SpaceFile *sfile, FileSelectParams *params) { - if (!sfile) { - /* Sometimes called in poll before space type was checked. */ - return NULL; + const char *blendfile_path = BKE_main_blendfile_path_from_global(); + + /* operator has no setting for this */ + params->active_file = -1; + + if (!params->dir[0]) { + if (blendfile_path[0] != '\0') { + BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir)); + } + else { + const char *doc_path = BKE_appdir_folder_default(); + if (doc_path) { + BLI_strncpy(params->dir, doc_path, sizeof(params->dir)); + } + } } - return sfile->params; + folder_history_list_ensure_for_active_browse_mode(sfile); + folderlist_pushdir(sfile->folders_prev, params->dir); + + /* Switching thumbnails needs to recalc layout T28809. */ + if (sfile->layout) { + sfile->layout->dirty = true; + } +} + +static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) +{ + BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); + BLI_assert(sfile->op == NULL); + + FileAssetSelectParams *asset_params = sfile->asset_params; + + if (!asset_params) { + asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params), + "FileAssetSelectParams"); + asset_params->base_params.details_flags = U_default.file_space_data.details_flags; + asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; + } + + FileSelectParams *base_params = &asset_params->base_params; + base_params->file[0] = '\0'; + base_params->filter_glob[0] = '\0'; + /* TODO this way of using filters to form categories is notably slower than specifying a + * "group" to read. That's because all types are read and filtering is applied afterwards. Would + * be nice if we could lazy-read individual groups. */ + base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER; + base_params->flag &= ~FILE_DIRSEL_ONLY; + base_params->filter |= FILE_TYPE_BLENDERLIB; + base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR; + base_params->display = FILE_IMGDISPLAY; + base_params->sort = FILE_SORT_ALPHA; + base_params->recursion_level = 1; + /* 'SMALL' size by default. More reasonable since this is typically used as regular editor, + * space is more of an issue here. */ + base_params->thumbnail_size = 96; + + fileselect_initialize_params_common(sfile, base_params); } /** @@ -92,6 +146,8 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) * the previously used settings to be used here rather than overriding them */ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile) { + BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_FILES); + FileSelectParams *params; wmOperator *op = sfile->op; @@ -297,42 +353,102 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile) params->filter_glob[0] = '\0'; } - /* operator has no setting for this */ - params->active_file = -1; + fileselect_initialize_params_common(sfile, params); - /* initialize the list with previous folders */ - if (!sfile->folders_prev) { - sfile->folders_prev = folderlist_new(); - } + return params; +} - if (!params->dir[0]) { - if (blendfile_path[0] != '\0') { - BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir)); - } - else { - const char *doc_path = BKE_appdir_folder_default(); - if (doc_path) { - BLI_strncpy(params->dir, doc_path, sizeof(params->dir)); +/** + * If needed, create and return the file select parameters for the active browse mode. + */ +FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile) +{ + switch ((eFileBrowse_Mode)sfile->browse_mode) { + case FILE_BROWSE_MODE_FILES: + if (!sfile->params) { + fileselect_ensure_updated_file_params(sfile); } - } + return sfile->params; + case FILE_BROWSE_MODE_ASSETS: + if (!sfile->asset_params) { + fileselect_ensure_updated_asset_params(sfile); + } + return &sfile->asset_params->base_params; } - folderlist_pushdir(sfile->folders_prev, params->dir); + BLI_assert(!"Invalid browse mode set in file space."); + return NULL; +} - /* Switching thumbnails needs to recalc layout T28809. */ - if (sfile->layout) { - sfile->layout->dirty = true; +/** + * Get the file select parameters for the active browse mode. + */ +FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) +{ + if (!sfile) { + /* Sometimes called in poll before space type was checked. */ + return NULL; } - return params; + switch ((eFileBrowse_Mode)sfile->browse_mode) { + case FILE_BROWSE_MODE_FILES: + return sfile->params; + case FILE_BROWSE_MODE_ASSETS: + return (FileSelectParams *)sfile->asset_params; + } + + BLI_assert(!"Invalid browse mode set in file space."); + return NULL; } -FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile) +FileSelectParams *ED_fileselect_get_file_params(const SpaceFile *sfile) { - if (!sfile->params) { - fileselect_ensure_updated_file_params(sfile); + return (sfile->browse_mode == FILE_BROWSE_MODE_FILES) ? sfile->params : NULL; +} + +FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile) +{ + return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) ? sfile->asset_params : NULL; +} + +static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) +{ + FileSelectAssetLibraryUID *library = &asset_params->asset_library; + FileSelectParams *base_params = &asset_params->base_params; + bUserAssetLibrary *user_library = NULL; + + /* Ensure valid repo, or fall-back to local one. */ + if (library->type == FILE_ASSET_LIBRARY_CUSTOM) { + user_library = BKE_preferences_asset_library_find_from_name( + &U, library->custom_library_identifier); + if (!user_library) { + library->type = FILE_ASSET_LIBRARY_LOCAL; + } + } + + switch (library->type) { + case FILE_ASSET_LIBRARY_LOCAL: + base_params->dir[0] = '\0'; + break; + case FILE_ASSET_LIBRARY_CUSTOM: + BLI_assert(user_library); + BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); + break; } - return sfile->params; + base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; +} + +void fileselect_refresh_params(SpaceFile *sfile) +{ + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + if (asset_params) { + fileselect_refresh_asset_params(asset_params); + } +} + +bool ED_fileselect_is_asset_browser(const SpaceFile *sfile) +{ + return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); } /* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA @@ -371,6 +487,8 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile) wmOperator *op = sfile->op; UserDef_FileSpaceData *sfile_udata = &U.file_space_data; + BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_FILES); + FileSelectParams *params = fileselect_ensure_updated_file_params(sfile); if (!op) { return; @@ -438,15 +556,6 @@ void ED_fileselect_params_to_userdef(SpaceFile *sfile, } } -void ED_fileselect_reset_params(SpaceFile *sfile) -{ - FileSelectParams *params = ED_fileselect_get_active_params(sfile); - params->type = FILE_UNIX; - params->flag = 0; - params->title[0] = '\0'; - params->active_file = -1; -} - /** * Sets FileSelectParams->file (name of selected file) */ @@ -1046,8 +1155,7 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil sfile->op = NULL; } - folderlist_free(sfile->folders_prev); - folderlist_free(sfile->folders_next); + folder_history_list_free(sfile); if (sfile->files) { ED_fileselect_clear(wm, owner_scene, sfile); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index c72ca58abba..f1d72387791 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -35,6 +35,8 @@ #include "BKE_screen.h" #include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_message.h" @@ -149,22 +151,10 @@ static void file_free(SpaceLink *sl) sfile->files = NULL; } - if (sfile->folders_prev) { - folderlist_free(sfile->folders_prev); - MEM_freeN(sfile->folders_prev); - sfile->folders_prev = NULL; - } - - if (sfile->folders_next) { - folderlist_free(sfile->folders_next); - MEM_freeN(sfile->folders_next); - sfile->folders_next = NULL; - } + folder_history_list_free(sfile); - if (sfile->params) { - MEM_freeN(sfile->params); - sfile->params = NULL; - } + MEM_SAFE_FREE(sfile->params); + MEM_SAFE_FREE(sfile->asset_params); if (sfile->layout) { MEM_freeN(sfile->layout); @@ -205,19 +195,20 @@ static SpaceLink *file_duplicate(SpaceLink *sl) sfilen->previews_timer = NULL; sfilen->smoothscroll_timer = NULL; + FileSelectParams *active_params_old = ED_fileselect_get_active_params(sfileo); + if (active_params_old) { + sfilen->files = filelist_new(active_params_old->type); + filelist_setdir(sfilen->files, active_params_old->dir); + } + if (sfileo->params) { - sfilen->files = filelist_new(sfileo->params->type); sfilen->params = MEM_dupallocN(sfileo->params); - filelist_setdir(sfilen->files, sfilen->params->dir); } - - if (sfileo->folders_prev) { - sfilen->folders_prev = folderlist_duplicate(sfileo->folders_prev); + if (sfileo->asset_params) { + sfilen->asset_params = MEM_dupallocN(sfileo->asset_params); } - if (sfileo->folders_next) { - sfilen->folders_next = folderlist_duplicate(sfileo->folders_next); - } + sfilen->folder_histories = folder_history_list_duplicate(&sfileo->folder_histories); if (sfileo->layout) { sfilen->layout = MEM_dupallocN(sfileo->layout); @@ -265,24 +256,42 @@ static void file_ensure_valid_region_state(bContext *C, } } +/** + * Tag the space to recreate the file-list. + */ +static void file_tag_reset_list(ScrArea *area, SpaceFile *sfile) +{ + filelist_tag_force_reset(sfile->files); + ED_area_tag_refresh(area); +} + static void file_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_ensure_active_params(sfile); + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); struct FSMenu *fsmenu = ED_fsmenu_get(); - if (!sfile->folders_prev) { - sfile->folders_prev = folderlist_new(); + fileselect_refresh_params(sfile); + folder_history_list_ensure_for_active_browse_mode(sfile); + + if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) && + filelist_needs_reset_on_main_changes(sfile->files)) { + filelist_tag_force_reset(sfile->files); } + sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES; + if (!sfile->files) { sfile->files = filelist_new(params->type); params->highlight_file = -1; /* added this so it opens nicer (ton) */ } + filelist_settype(sfile->files, params->type); filelist_setdir(sfile->files, params->dir); filelist_setrecursion(sfile->files, params->recursion_level); filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT); + filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL); filelist_setfilter_options( sfile->files, (params->flag & FILE_FILTER) != 0, @@ -290,6 +299,7 @@ static void file_refresh(const bContext *C, ScrArea *area) true, /* Just always hide parent, prefer to not add an extra user option for this. */ params->filter, params->filter_id, + (params->flag & FILE_ASSETS_ONLY) != 0, params->filter_glob, params->filter_search); @@ -300,12 +310,12 @@ static void file_refresh(const bContext *C, ScrArea *area) 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_force_reset(sfile->files)) { + if (filelist_needs_force_reset(sfile->files)) { filelist_readjob_stop(wm, CTX_data_scene(C)); filelist_clear(sfile->files); } - if (filelist_empty(sfile->files)) { + if (filelist_needs_reading(sfile->files)) { if (!filelist_pending(sfile->files)) { filelist_readjob_start(sfile->files, C); } @@ -365,6 +375,14 @@ static void file_listener(wmWindow *UNUSED(win), break; } break; + case NC_ASSET: { + if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { + /* Full refresh of the file list if local asset data was changed. Refreshing this view is + * cheap and users expect this to be updated immediately. */ + file_tag_reset_list(area, sfile); + } + break; + } } } @@ -442,6 +460,22 @@ static void file_main_region_message_subscribe(const struct bContext *UNUSED(C), } } +static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile) +{ + /* Needed, because filelist is not initialized on loading */ + if (!sfile->files || filelist_needs_reading(sfile->files)) { + return true; + } + + /* File reading tagged the space because main data changed that may require a filelist reset. */ + if (filelist_needs_reset_on_main_changes(sfile->files) && + (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES)) { + return true; + } + + return false; +} + static void file_main_region_draw(const bContext *C, ARegion *region) { /* draw entirely, view changes should be handled here */ @@ -450,8 +484,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; - /* Needed, because filelist is not initialized on loading */ - if (!sfile->files || filelist_empty(sfile->files)) { + if (file_main_region_needs_refresh_before_draw(sfile)) { file_refresh(C, NULL); } @@ -681,6 +714,52 @@ static void file_dropboxes(void) WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy); } +const char *file_context_dir[] = {"active_file", "active_id", NULL}; + +static int /*eContextResult*/ file_context(const bContext *C, + const char *member, + bContextDataResult *result) +{ + bScreen *screen = CTX_wm_screen(C); + SpaceFile *sfile = CTX_wm_space_file(C); + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + + BLI_assert(!ED_area_is_global(CTX_wm_area(C))); + + if (CTX_data_dir(member)) { + CTX_data_dir_set(result, file_context_dir); + return CTX_RESULT_OK; + } + else if (CTX_data_equals(member, "active_file")) { + FileDirEntry *file = filelist_file(sfile->files, params->active_file); + CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); + return CTX_RESULT_OK; + } + else if (CTX_data_equals(member, "active_id")) { + const FileDirEntry *file = filelist_file(sfile->files, params->active_file); + + ID *id = filelist_file_get_id(file); + if (id) { + CTX_data_id_pointer_set(result, id); + } + return CTX_RESULT_OK; + } + return CTX_RESULT_MEMBER_NOT_FOUND; +} + +static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id)) +{ + SpaceFile *sfile = (SpaceFile *)sl; + + /* If the file shows main data (IDs), tag it for reset. */ + if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { + /* Full refresh of the file list if main data was changed, don't even attempt remap pointers. + * We could give file list types a id-remap callback, but it's probably not worth it. + * Refreshing local file lists is relatively cheap. */ + file_tag_reset_list(area, sfile); + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_file(void) { @@ -700,6 +779,8 @@ void ED_spacetype_file(void) st->operatortypes = file_operatortypes; st->keymap = file_keymap; st->dropboxes = file_dropboxes; + st->context = file_context; + st->id_remap = file_id_remap; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype file region"); |