diff options
Diffstat (limited to 'source/blender/editors/space_file')
-rw-r--r-- | source/blender/editors/space_file/CMakeLists.txt | 3 | ||||
-rw-r--r-- | source/blender/editors/space_file/asset_catalog_tree_view.cc | 11 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_indexer.cc | 96 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_indexer.h | 36 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.c | 364 | ||||
-rw-r--r-- | source/blender/editors/space_file/filelist.h | 2 | ||||
-rw-r--r-- | source/blender/editors/space_file/space_file.c | 12 |
7 files changed, 400 insertions, 124 deletions
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index 4b508f16c1e..1651269869e 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../include + ../asset ../../blenfont ../../blenkernel ../../blenlib @@ -36,6 +37,7 @@ set(INC set(SRC asset_catalog_tree_view.cc file_draw.c + file_indexer.cc file_ops.c file_panels.c file_utils.c @@ -44,6 +46,7 @@ set(SRC fsmenu.c space_file.c + file_indexer.h file_intern.h filelist.h fsmenu.h diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 654278cd990..5d8bfbc0b79 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -216,7 +216,8 @@ ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive( { ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item<AssetCatalogTreeViewItem>( &catalog); - view_item.is_active([this, &catalog]() { return is_active_catalog(catalog.get_catalog_id()); }); + view_item.set_is_active_fn( + [this, &catalog]() { return is_active_catalog(catalog.get_catalog_id()); }); catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) { build_catalog_items_recursive(view_item, child); @@ -230,11 +231,11 @@ void AssetCatalogTreeView::add_all_item() AssetCatalogTreeViewAllItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(IFACE_("All"), ICON_HOME); - item.on_activate([params](ui::BasicTreeViewItem & /*item*/) { + item.set_on_activate_fn([params](ui::BasicTreeViewItem & /*item*/) { params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS; WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); }); - item.is_active( + item.set_is_active_fn( [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS; }); } @@ -245,11 +246,11 @@ void AssetCatalogTreeView::add_unassigned_item() AssetCatalogTreeViewUnassignedItem &item = add_tree_item<AssetCatalogTreeViewUnassignedItem>( IFACE_("Unassigned"), ICON_FILE_HIDDEN); - item.on_activate([params](ui::BasicTreeViewItem & /*item*/) { + item.set_on_activate_fn([params](ui::BasicTreeViewItem & /*item*/) { params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); }); - item.is_active( + item.set_is_active_fn( [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG; }); } diff --git a/source/blender/editors/space_file/file_indexer.cc b/source/blender/editors/space_file/file_indexer.cc new file mode 100644 index 00000000000..95e0afd7a1e --- /dev/null +++ b/source/blender/editors/space_file/file_indexer.cc @@ -0,0 +1,96 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edfile + * + * This file implements the default file browser indexer and has some helper function to work with + * `FileIndexerEntries`. + */ +#include "file_indexer.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +namespace blender::ed::file::indexer { + +static eFileIndexerResult read_index(const char *UNUSED(file_name), + FileIndexerEntries *UNUSED(entries), + int *UNUSED(r_read_entries_len), + void *UNUSED(user_data)) +{ + return FILE_INDEXER_NEEDS_UPDATE; +} + +static void update_index(const char *UNUSED(file_name), + FileIndexerEntries *UNUSED(entries), + void *UNUSED(user_data)) +{ +} + +constexpr FileIndexerType default_indexer() +{ + FileIndexerType indexer = {nullptr}; + indexer.read_index = read_index; + indexer.update_index = update_index; + return indexer; +} + +static FileIndexerEntry *file_indexer_entry_create_from_datablock_info( + const BLODataBlockInfo *datablock_info, const int idcode) +{ + FileIndexerEntry *entry = static_cast<FileIndexerEntry *>( + MEM_mallocN(sizeof(FileIndexerEntry), __func__)); + entry->datablock_info = *datablock_info; + entry->idcode = idcode; + return entry; +} + +} // namespace blender::ed::file::indexer + +extern "C" { + +void ED_file_indexer_entries_extend_from_datablock_infos( + FileIndexerEntries *indexer_entries, + const LinkNode * /* BLODataBlockInfo */ datablock_infos, + const int idcode) +{ + for (const LinkNode *ln = datablock_infos; ln; ln = ln->next) { + const BLODataBlockInfo *datablock_info = static_cast<const BLODataBlockInfo *>(ln->link); + FileIndexerEntry *file_indexer_entry = + blender::ed::file::indexer::file_indexer_entry_create_from_datablock_info(datablock_info, + idcode); + BLI_linklist_prepend(&indexer_entries->entries, file_indexer_entry); + } +} + +static void ED_file_indexer_entry_free(void *indexer_entry) +{ + MEM_freeN(indexer_entry); +} + +void ED_file_indexer_entries_clear(FileIndexerEntries *indexer_entries) +{ + BLI_linklist_free(indexer_entries->entries, ED_file_indexer_entry_free); + indexer_entries->entries = nullptr; +} + +const FileIndexerType file_indexer_noop = blender::ed::file::indexer::default_indexer(); +} diff --git a/source/blender/editors/space_file/file_indexer.h b/source/blender/editors/space_file/file_indexer.h new file mode 100644 index 00000000000..ea42f10498b --- /dev/null +++ b/source/blender/editors/space_file/file_indexer.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edfile + */ +#pragma once + +#include "ED_file_indexer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Default indexer to use when listing files. The implementation is a no-operation indexing. When + * set it won't use indexing. It is added to increase the code clarity. + */ +extern const FileIndexerType file_indexer_noop; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 3e7a831691c..3e48c2a5d87 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -89,6 +89,7 @@ #include "atomic_ops.h" +#include "file_indexer.h" #include "file_intern.h" #include "filelist.h" @@ -352,9 +353,6 @@ typedef struct FileListEntryPreview { char path[FILE_MAX]; uint flags; int index; - /* 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; @@ -399,6 +397,11 @@ typedef struct FileList { FileListFilter filter_data; + /** + * File indexer to use. Attribute is always set. + */ + const struct FileIndexerType *indexer; + struct FileListIntern filelist_intern; struct FileListEntryCache filelist_cache; @@ -1129,6 +1132,19 @@ void filelist_setfilter_options(FileList *filelist, } /** + * Set the indexer to be used by the filelist. + * + * The given indexer allocation should be handled by the caller or defined statically. + */ +void filelist_setindexer(FileList *filelist, const FileIndexerType *indexer) +{ + BLI_assert(filelist); + BLI_assert(indexer); + + filelist->indexer = indexer; +} + +/** * \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is * #FILE_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise. */ @@ -1577,74 +1593,46 @@ 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); - 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); - if (imbuf) { - 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)); + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + BLI_assert(preview->flags & + (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER | + FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)); - if (preview->flags & FILE_TYPE_IMAGE) { - source = THB_SOURCE_IMAGE; - } - else if (preview->flags & - (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) { - source = THB_SOURCE_BLEND; - } - else if (preview->flags & FILE_TYPE_MOVIE) { - source = THB_SOURCE_MOVIE; - } - else if (preview->flags & FILE_TYPE_FTFONT) { - source = THB_SOURCE_FONT; - } - - IMB_thumb_path_lock(preview->path); - /* 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); - if (imbuf) { - preview->icon_id = BKE_icon_imbuf_create(imbuf); - } - - done = true; + 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 (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); - atomic_fetch_and_sub_z(&cache->previews_todo_count, 1); + 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); + if (imbuf) { + preview->icon_id = BKE_icon_imbuf_create(imbuf); } + BLI_thread_queue_push(cache->previews_done, preview); + atomic_fetch_and_sub_z(&cache->previews_todo_count, 1); + // printf("%s: End (%d)...\n", __func__, threadid); } static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void *taskdata) { FileListEntryPreviewTaskData *preview_taskdata = taskdata; - FileListEntryPreview *preview = preview_taskdata->preview; - - /* preview_taskdata->preview is atomically set to NULL once preview has been processed and sent - * to previews_done queue. */ - if (preview != NULL) { - if (preview->icon_id) { - BKE_icon_delete(preview->icon_id); - } - MEM_freeN(preview); - } MEM_freeN(preview_taskdata); } @@ -1710,7 +1698,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry return; } - if (entry->flags & FILE_ENTRY_INVALID_PREVIEW) { + if (entry->flags & (FILE_ENTRY_INVALID_PREVIEW | FILE_ENTRY_PREVIEW_LOADING)) { return; } @@ -1719,33 +1707,51 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry return; } - 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); - } - else { - BLI_join_dirfile( - preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); + PreviewImage *preview_in_memory = intern_entry->local_data.preview_image; + if (preview_in_memory && !BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)) { + /* Nothing to set yet. Wait for next call. */ + return; } + filelist_cache_preview_ensure_running(cache); + entry->flags |= FILE_ENTRY_PREVIEW_LOADING; + + FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); preview->index = index; preview->flags = entry->typeflag; - preview->in_memory_preview = intern_entry->local_data.preview_image; preview->icon_id = 0; - // printf("%s: %d - %s\n", __func__, preview->index, preview->path); - filelist_cache_preview_ensure_running(cache); + if (preview_in_memory) { + /* TODO(mano-wii): No need to use the thread API here. */ + BLI_assert(BKE_previewimg_is_finished(preview_in_memory, ICON_SIZE_PREVIEW)); + preview->path[0] = '\0'; + ImBuf *imbuf = BKE_previewimg_to_imbuf(preview_in_memory, ICON_SIZE_PREVIEW); + if (imbuf) { + preview->icon_id = BKE_icon_imbuf_create(imbuf); + } + BLI_thread_queue_push(cache->previews_done, preview); + atomic_fetch_and_sub_z(&cache->previews_todo_count, 1); + } + else { + if (entry->redirection_path) { + BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); + } + else { + BLI_join_dirfile( + preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); + } + // printf("%s: %d - %s\n", __func__, preview->index, preview->path); - FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), - __func__); - preview_taskdata->preview = preview; - BLI_task_pool_push(cache->previews_pool, - filelist_cache_preview_runf, - preview_taskdata, - true, - filelist_cache_preview_freef); + FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), + __func__); + preview_taskdata->preview = preview; + BLI_task_pool_push(cache->previews_pool, + filelist_cache_preview_runf, + preview_taskdata, + true, + filelist_cache_preview_freef); + } } static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size) @@ -1843,6 +1849,8 @@ FileList *filelist_new(short type) p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; filelist_settype(p, type); + p->indexer = &file_indexer_noop; + return p; } @@ -1896,6 +1904,7 @@ static void filelist_clear_asset_library(FileList *filelist) { /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ filelist->asset_library = NULL; + file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); } void filelist_clear_ex(struct FileList *filelist, @@ -1995,7 +2004,6 @@ void filelist_free(struct FileList *filelist) filelist->selection_state = NULL; } - file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); MEM_SAFE_FREE(filelist->asset_library_ref); memset(&filelist->filter_data, 0, sizeof(filelist->filter_data)); @@ -2659,24 +2667,27 @@ bool filelist_cache_previews_update(FileList *filelist) // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, 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->preview_icon_id) { + if (entry) { + if (preview->icon_id) { + /* The FILE_ENTRY_PREVIEW_LOADING flag should have prevented any other asynchronous + * process from trying to generate the same preview icon. */ + BLI_assert_msg(!entry->preview_icon_id, "Preview icon should not have been generated yet"); + /* Move ownership over icon. */ entry->preview_icon_id = preview->icon_id; preview->icon_id = 0; changed = true; } else { - BKE_icon_delete(preview->icon_id); + /* We want to avoid re-processing this entry continuously! + * Note that, since entries only live in cache, + * preview will be retried quite often anyway. */ + entry->flags |= FILE_ENTRY_INVALID_PREVIEW; } + entry->flags &= ~FILE_ENTRY_PREVIEW_LOADING; } - else if (entry) { - /* We want to avoid re-processing this entry continuously! - * Note that, since entries only live in cache, - * preview will be retried quite often anyway. */ - entry->flags |= FILE_ENTRY_INVALID_PREVIEW; + else { + BKE_icon_delete(preview->icon_id); } MEM_freeN(preview); @@ -3130,6 +3141,29 @@ static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idc return entry; } +static void filelist_readjob_list_lib_add_datablock(ListBase *entries, + const BLODataBlockInfo *datablock_info, + const bool prefix_relpath_with_group_name, + const int idcode, + const char *group_name) +{ + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + if (prefix_relpath_with_group_name) { + entry->relpath = BLI_sprintfN("%s/%s", group_name, datablock_info->name); + } + else { + entry->relpath = BLI_strdup(datablock_info->name); + } + entry->typeflag |= FILE_TYPE_BLENDERLIB; + if (datablock_info && datablock_info->asset_data) { + entry->typeflag |= FILE_TYPE_ASSET; + /* Moves ownership! */ + entry->imported_asset_data = datablock_info->asset_data; + } + entry->blentype = idcode; + BLI_addtail(entries, entry); +} + static void filelist_readjob_list_lib_add_datablocks(ListBase *entries, LinkNode *datablock_infos, const bool prefix_relpath_with_group_name, @@ -3137,29 +3171,71 @@ static void filelist_readjob_list_lib_add_datablocks(ListBase *entries, const char *group_name) { for (LinkNode *ln = datablock_infos; ln; ln = ln->next) { - struct BLODataBlockInfo *info = ln->link; - FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); - if (prefix_relpath_with_group_name) { - entry->relpath = BLI_sprintfN("%s/%s", group_name, info->name); - } - else { - entry->relpath = BLI_strdup(info->name); - } - entry->typeflag |= FILE_TYPE_BLENDERLIB; - if (info && info->asset_data) { - entry->typeflag |= FILE_TYPE_ASSET; - /* Moves ownership! */ - entry->imported_asset_data = info->asset_data; - } - entry->blentype = idcode; + struct BLODataBlockInfo *datablock_info = ln->link; + filelist_readjob_list_lib_add_datablock( + entries, datablock_info, prefix_relpath_with_group_name, idcode, group_name); + } +} + +static void filelist_readjob_list_lib_add_from_indexer_entries( + ListBase *entries, + const FileIndexerEntries *indexer_entries, + const bool prefix_relpath_with_group_name) +{ + for (const LinkNode *ln = indexer_entries->entries; ln; ln = ln->next) { + const FileIndexerEntry *indexer_entry = (const FileIndexerEntry *)ln->link; + const char *group_name = BKE_idtype_idcode_to_name(indexer_entry->idcode); + filelist_readjob_list_lib_add_datablock(entries, + &indexer_entry->datablock_info, + prefix_relpath_with_group_name, + indexer_entry->idcode, + group_name); + } +} + +static FileListInternEntry *filelist_readjob_list_lib_navigate_to_parent_entry_create(void) +{ + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + entry->relpath = BLI_strdup(FILENAME_PARENT); + entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); + return entry; +} + +/** + * Structure to keep the file indexer and its user data together. + */ +typedef struct FileIndexer { + const FileIndexerType *callbacks; + + /** + * User data. Contains the result of `callbacks.init_user_data`. + */ + void *user_data; +} FileIndexer; + +static int filelist_readjob_list_lib_populate_from_index(ListBase *entries, + const ListLibOptions options, + const int read_from_index, + const FileIndexerEntries *indexer_entries) +{ + int navigate_to_parent_len = 0; + if (options & LIST_LIB_ADD_PARENT) { + FileListInternEntry *entry = filelist_readjob_list_lib_navigate_to_parent_entry_create(); BLI_addtail(entries, entry); + navigate_to_parent_len = 1; } + + filelist_readjob_list_lib_add_from_indexer_entries(entries, indexer_entries, true); + return read_from_index + navigate_to_parent_len; } static int filelist_readjob_list_lib(const char *root, ListBase *entries, - const ListLibOptions options) + const ListLibOptions options, + FileIndexer *indexer_runtime) { + BLI_assert(indexer_runtime); + char dir[FILE_MAX_LIBEXTRA], *group; struct BlendHandle *libfiledata = NULL; @@ -3167,13 +3243,37 @@ static int filelist_readjob_list_lib(const char *root, /* Check if the given root is actually a library. All folders are passed to * `filelist_readjob_list_lib` and based on the number of found entries `filelist_readjob_do` * will do a dir listing only when this function does not return any entries. */ - /* TODO: We should consider introducing its own function to detect if it is a lib and + /* TODO(jbakker): We should consider introducing its own function to detect if it is a lib and * call it directly from `filelist_readjob_do` to increase readability. */ const bool is_lib = BLO_library_path_explode(root, dir, &group, NULL); if (!is_lib) { return 0; } + const bool group_came_from_path = group != NULL; + + /* Try read from indexer_runtime. */ + /* Indexing returns all entries in a blend file. We should ignore the index when listing a group + * inside a blend file, so the `entries` isn't filled with undesired entries. + * This happens when linking or appending data-blocks, where you can navigate into a group (fe + * Materials/Objects) where you only want to work with partial indexes. + * + * Adding support for partial reading/updating indexes would increase the complexity. + */ + const bool use_indexer = !group_came_from_path; + FileIndexerEntries indexer_entries = {NULL}; + if (use_indexer) { + int read_from_index = 0; + eFileIndexerResult indexer_result = indexer_runtime->callbacks->read_index( + dir, &indexer_entries, &read_from_index, indexer_runtime->user_data); + if (indexer_result == FILE_INDEXER_ENTRIES_LOADED) { + int entries_read = filelist_readjob_list_lib_populate_from_index( + entries, options, read_from_index, &indexer_entries); + ED_file_indexer_entries_clear(&indexer_entries); + return entries_read; + } + } + /* Open the library file. */ BlendFileReadReport bf_reports = {.reports = NULL}; libfiledata = BLO_blendhandle_from_file(dir, &bf_reports); @@ -3182,18 +3282,18 @@ static int filelist_readjob_list_lib(const char *root, } /* Add current parent when requested. */ - int parent_len = 0; + /* Is the navigate to previous level added to the list of entries. When added the return value + * should be increased to match the actual number of entries added. It is introduced to keep + * the code clean and readable and not counting in a single variable. */ + int navigate_to_parent_len = 0; if (options & LIST_LIB_ADD_PARENT) { - FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); - entry->relpath = BLI_strdup(FILENAME_PARENT); - entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); + FileListInternEntry *entry = filelist_readjob_list_lib_navigate_to_parent_entry_create(); BLI_addtail(entries, entry); - parent_len = 1; + navigate_to_parent_len = 1; } int group_len = 0; int datablock_len = 0; - const bool group_came_from_path = group != NULL; if (group_came_from_path) { const int idcode = groupname_to_code(group); LinkNode *datablock_infos = BLO_blendhandle_get_datablock_info( @@ -3218,6 +3318,10 @@ static int filelist_readjob_list_lib(const char *root, libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len); filelist_readjob_list_lib_add_datablocks( entries, group_datablock_infos, true, idcode, group_name); + if (use_indexer) { + ED_file_indexer_entries_extend_from_datablock_infos( + &indexer_entries, group_datablock_infos, idcode); + } BLI_linklist_freeN(group_datablock_infos); datablock_len += group_datablock_len; } @@ -3228,8 +3332,14 @@ static int filelist_readjob_list_lib(const char *root, BLO_blendhandle_close(libfiledata); + /* Update the index. */ + if (use_indexer) { + indexer_runtime->callbacks->update_index(dir, &indexer_entries, indexer_runtime->user_data); + ED_file_indexer_entries_clear(&indexer_entries); + } + /* Return the number of items added to entries. */ - int added_entries_len = group_len + datablock_len + parent_len; + int added_entries_len = group_len + datablock_len + navigate_to_parent_len; return added_entries_len; } @@ -3421,11 +3531,11 @@ typedef struct FileListReadJob { * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist * into #filelist in a thread-safe way. * - * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, and - * moved to #filelist once all categories are loaded. + * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, + * and moved to #filelist once all categories are loaded. * - * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set - * to NULL to avoid double-freeing them. */ + * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be + * set to NULL to avoid double-freeing them. */ struct FileList *tmp_filelist; } FileListReadJob; @@ -3462,8 +3572,8 @@ static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, /* No more levels of recursion left. */ return false; } - /* Show entries when recursion is set to `Blend file` even when `current_recursion_level` exceeds - * `max_recursion`. */ + /* Show entries when recursion is set to `Blend file` even when `current_recursion_level` + * exceeds `max_recursion`. */ if (!is_lib && (current_recursion_level >= max_recursion) && ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) { return false; @@ -3511,6 +3621,12 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, BLI_path_normalize_dir(job_params->main_name, dir); td_dir->dir = BLI_strdup(dir); + /* Init the file indexer. */ + FileIndexer indexer_runtime = {.callbacks = filelist->indexer}; + if (indexer_runtime.callbacks->init_user_data) { + indexer_runtime.user_data = indexer_runtime.callbacks->init_user_data(dir, sizeof(dir)); + } + while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { FileListInternEntry *entry; int nbr_entries = 0; @@ -3553,7 +3669,8 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, if (filelist->asset_library_ref) { list_lib_options |= LIST_LIB_ASSETS_ONLY; } - nbr_entries = filelist_readjob_list_lib(subdir, &entries, list_lib_options); + nbr_entries = filelist_readjob_list_lib( + subdir, &entries, list_lib_options, &indexer_runtime); if (nbr_entries > 0) { is_lib = true; } @@ -3595,6 +3712,15 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib, MEM_freeN(subdir); } + /* Finalize and free indexer. */ + if (indexer_runtime.callbacks->filelist_finished && BLI_stack_is_empty(todo_dirs)) { + indexer_runtime.callbacks->filelist_finished(indexer_runtime.user_data); + } + if (indexer_runtime.callbacks->free_user_data && indexer_runtime.user_data) { + indexer_runtime.callbacks->free_user_data(indexer_runtime.user_data); + indexer_runtime.user_data = NULL; + } + /* If we were interrupted by stop, stack may not be empty and we need to free * pending dir paths. */ while (!BLI_stack_is_empty(todo_dirs)) { diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 0048a349dca..e7cda106224 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -29,6 +29,7 @@ extern "C" { struct AssetLibraryReference; struct BlendHandle; +struct FileIndexerType; struct FileList; struct FileSelection; struct bUUID; @@ -72,6 +73,7 @@ void filelist_setfilter_options(struct FileList *filelist, const bool filter_assets_only, const char *filter_glob, const char *filter_search); +void filelist_setindexer(struct FileList *filelist, const struct FileIndexerType *indexer); void filelist_set_asset_catalog_filter_options( struct FileList *filelist, eFileSel_Params_AssetCatalogVisibility catalog_visibility, diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index ef503708335..c8e9742be53 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_linklist.h" #include "BLI_utildefines.h" #include "BKE_appdir.h" @@ -44,6 +45,7 @@ #include "WM_types.h" #include "ED_asset.h" +#include "ED_asset_indexer.h" #include "ED_fileselect.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -59,6 +61,10 @@ #include "filelist.h" #include "fsmenu.h" +/* Enable asset indexing. Currently disabled as ID properties aren't indexed yet and is needed for + * object snapping. See {D12990}. */ +//#define SPACE_FILE_ENABLE_ASSET_INDEXING + static ARegion *file_ui_region_ensure(ScrArea *area, ARegion *region_prev) { ARegion *region; @@ -353,6 +359,12 @@ static void file_refresh(const bContext *C, ScrArea *area) sfile->files, asset_params->asset_catalog_visibility, &asset_params->catalog_id); } +#ifdef SPACE_FILE_ENABLE_ASSET_INDEXING + if (ED_fileselect_is_asset_browser(sfile)) { + filelist_setindexer(sfile->files, &file_indexer_asset); + } +#endif + /* Update the active indices of bookmarks & co. */ sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir); sfile->system_bookmarknr = fsmenu_get_active_indices( |