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:
authorJeroen Bakker <jeroen@blender.org>2021-11-09 12:34:41 +0300
committerJeroen Bakker <jeroen@blender.org>2021-11-09 12:34:41 +0300
commitb65df10346f5fe47c881b183901e2d7eff775848 (patch)
tree95120de32f77d7193edc3687f0b97a0692288e8b /source/blender/editors/space_file
parent8bf8db8ca2dd534ce4aaa32a0921b599f36098c4 (diff)
parenta7540f4b3611a0d06f197e6f27148319927188f7 (diff)
Merge branch 'master' into tmp-vulkan
Diffstat (limited to 'source/blender/editors/space_file')
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc655
-rw-r--r--source/blender/editors/space_file/file_draw.c154
-rw-r--r--source/blender/editors/space_file/file_intern.h60
-rw-r--r--source/blender/editors/space_file/file_ops.c331
-rw-r--r--source/blender/editors/space_file/file_panels.c46
-rw-r--r--source/blender/editors/space_file/filelist.c1317
-rw-r--r--source/blender/editors/space_file/filelist.h37
-rw-r--r--source/blender/editors/space_file/filesel.c228
-rw-r--r--source/blender/editors/space_file/fsmenu.c23
-rw-r--r--source/blender/editors/space_file/space_file.c179
11 files changed, 2277 insertions, 755 deletions
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index 993a52b9084..4b508f16c1e 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -34,6 +34,7 @@ set(INC
)
set(SRC
+ asset_catalog_tree_view.cc
file_draw.c
file_ops.c
file_panels.c
@@ -49,6 +50,7 @@ set(SRC
)
set(LIB
+ bf_blenkernel
)
if(WITH_HEADLESS)
diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc
new file mode 100644
index 00000000000..b3b81c5e07f
--- /dev/null
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -0,0 +1,655 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2007 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spfile
+ */
+
+#include "DNA_space_types.h"
+
+#include "BKE_asset.h"
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.hh"
+
+#include "BLI_string_ref.hh"
+
+#include "BLT_translation.h"
+
+#include "ED_asset.h"
+#include "ED_fileselect.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_interface.hh"
+#include "UI_resources.h"
+#include "UI_tree_view.hh"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "file_intern.h"
+#include "filelist.h"
+
+using namespace blender;
+using namespace blender::bke;
+
+namespace blender::ed::asset_browser {
+
+class AssetCatalogTreeView : public ui::AbstractTreeView {
+ ::AssetLibrary *asset_library_;
+ /** The asset catalog tree this tree-view represents. */
+ bke::AssetCatalogTree *catalog_tree_;
+ FileAssetSelectParams *params_;
+ SpaceFile &space_file_;
+
+ friend class AssetCatalogTreeViewItem;
+ friend class AssetCatalogDropController;
+
+ public:
+ AssetCatalogTreeView(::AssetLibrary *library,
+ FileAssetSelectParams *params,
+ SpaceFile &space_file);
+
+ void build_tree() override;
+
+ private:
+ ui::BasicTreeViewItem &build_catalog_items_recursive(ui::TreeViewItemContainer &view_parent_item,
+ AssetCatalogTreeItem &catalog);
+
+ void add_all_item();
+ void add_unassigned_item();
+ bool is_active_catalog(CatalogID catalog_id) const;
+};
+
+/* ---------------------------------------------------------------------- */
+
+class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
+ /** The catalog tree item this tree view item represents. */
+ AssetCatalogTreeItem &catalog_item_;
+
+ public:
+ AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item);
+
+ void on_activate() override;
+
+ void build_row(uiLayout &row) override;
+ void build_context_menu(bContext &C, uiLayout &column) const override;
+
+ bool can_rename() const override;
+ bool rename(StringRefNull new_name) override;
+
+ /** Add drag support for catalog items. */
+ std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override;
+ /** Add dropping support for catalog items. */
+ std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+};
+
+class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController {
+ AssetCatalogTreeItem &catalog_item_;
+
+ public:
+ explicit AssetCatalogDragController(AssetCatalogTreeItem &catalog_item);
+
+ int get_drag_type() const override;
+ void *create_drag_data() const override;
+};
+
+class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController {
+ AssetCatalogTreeItem &catalog_item_;
+
+ public:
+ AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item);
+
+ bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
+ std::string drop_tooltip(const wmDrag &drag) const override;
+ bool on_drop(const wmDrag &drag) override;
+
+ ::AssetLibrary &get_asset_library() const;
+
+ static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint);
+ static bool drop_assets_into_catalog(const AssetCatalogTreeView &tree_view,
+ const wmDrag &drag,
+ CatalogID catalog_id,
+ StringRefNull simple_name = "");
+
+ private:
+ bool drop_asset_catalog_into_catalog(const wmDrag &drag);
+ std::string drop_tooltip_asset_list(const wmDrag &drag) const;
+ std::string drop_tooltip_asset_catalog(const wmDrag &drag) const;
+};
+
+/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level
+ * catalog. */
+class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
+ using BasicTreeViewItem::BasicTreeViewItem;
+
+ void build_row(uiLayout &row) override;
+};
+
+class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
+ using BasicTreeViewItem::BasicTreeViewItem;
+
+ struct DropController : public ui::AbstractTreeViewItemDropController {
+ DropController(AssetCatalogTreeView &tree_view);
+
+ bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
+ std::string drop_tooltip(const wmDrag &drag) const override;
+ bool on_drop(const wmDrag &drag) override;
+ };
+
+ std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+};
+
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library,
+ FileAssetSelectParams *params,
+ SpaceFile &space_file)
+ : asset_library_(library),
+ catalog_tree_(BKE_asset_library_get_catalog_tree(library)),
+ params_(params),
+ space_file_(space_file)
+{
+}
+
+void AssetCatalogTreeView::build_tree()
+{
+ add_all_item();
+
+ if (catalog_tree_) {
+ catalog_tree_->foreach_root_item([this](AssetCatalogTreeItem &item) {
+ ui::BasicTreeViewItem &child_view_item = build_catalog_items_recursive(*this, item);
+
+ /* Open root-level items by default. */
+ child_view_item.set_collapsed(false);
+ });
+ }
+
+ add_unassigned_item();
+}
+
+ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive(
+ ui::TreeViewItemContainer &view_parent_item, AssetCatalogTreeItem &catalog)
+{
+ 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()); });
+
+ catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) {
+ build_catalog_items_recursive(view_item, child);
+ });
+ return view_item;
+}
+
+void AssetCatalogTreeView::add_all_item()
+{
+ FileAssetSelectParams *params = params_;
+
+ AssetCatalogTreeViewAllItem &item = add_tree_item<AssetCatalogTreeViewAllItem>(IFACE_("All"),
+ ICON_HOME);
+ item.on_activate([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(
+ [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS; });
+}
+
+void AssetCatalogTreeView::add_unassigned_item()
+{
+ FileAssetSelectParams *params = params_;
+
+ AssetCatalogTreeViewUnassignedItem &item = add_tree_item<AssetCatalogTreeViewUnassignedItem>(
+ IFACE_("Unassigned"), ICON_FILE_HIDDEN);
+
+ item.on_activate([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(
+ [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG; });
+}
+
+bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const
+{
+ return (params_->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) &&
+ (params_->catalog_id == catalog_id);
+}
+
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogTreeViewItem::AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item)
+ : BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item)
+{
+}
+
+void AssetCatalogTreeViewItem::on_activate()
+{
+ const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
+ get_tree_view());
+ tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
+ tree_view.params_->catalog_id = catalog_item_.get_catalog_id();
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
+}
+
+void AssetCatalogTreeViewItem::build_row(uiLayout &row)
+{
+ const std::string label_override = catalog_item_.has_unsaved_changes() ? (label_ + "*") : label_;
+ add_label(row, label_override);
+
+ if (!is_hovered()) {
+ return;
+ }
+
+ uiButTreeRow *tree_row_but = tree_row_button();
+ PointerRNA *props;
+
+ props = UI_but_extra_operator_icon_add(
+ (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str());
+}
+
+void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) const
+{
+ PointerRNA props;
+
+ uiItemFullO(&column,
+ "ASSET_OT_catalog_new",
+ "New Catalog",
+ ICON_NONE,
+ nullptr,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &props);
+ RNA_string_set(&props, "parent_path", catalog_item_.catalog_path().c_str());
+
+ char catalog_id_str_buffer[UUID_STRING_LEN] = "";
+ BLI_uuid_format(catalog_id_str_buffer, catalog_item_.get_catalog_id());
+ uiItemFullO(&column,
+ "ASSET_OT_catalog_delete",
+ "Delete Catalog",
+ ICON_NONE,
+ nullptr,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &props);
+ RNA_string_set(&props, "catalog_id", catalog_id_str_buffer);
+ uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename");
+
+ /* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done
+ * in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on
+ * catalog to the menu draw callback (via context probably).*/
+ MenuType *mt = WM_menutype_find("ASSETBROWSER_MT_catalog_context_menu", true);
+ if (!mt) {
+ return;
+ }
+ UI_menutype_draw(&C, mt, &column);
+}
+
+bool AssetCatalogTreeViewItem::can_rename() const
+{
+ return true;
+}
+
+bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
+{
+ /* Important to keep state. */
+ BasicTreeViewItem::rename(new_name);
+
+ const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
+ get_tree_view());
+ ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name);
+ return true;
+}
+
+std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem::
+ create_drop_controller() const
+{
+ return std::make_unique<AssetCatalogDropController>(
+ static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
+}
+
+std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem::
+ create_drag_controller() const
+{
+ return std::make_unique<AssetCatalogDragController>(catalog_item_);
+}
+
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view,
+ AssetCatalogTreeItem &catalog_item)
+ : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item)
+{
+}
+
+bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const
+{
+ if (drag.type == WM_DRAG_ASSET_CATALOG) {
+ /* Always supported. */
+ return true;
+ }
+ if (drag.type == WM_DRAG_ASSET_LIST) {
+ return has_droppable_asset(drag, r_disabled_hint);
+ }
+ return false;
+}
+
+std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const
+{
+ if (drag.type == WM_DRAG_ASSET_CATALOG) {
+ return drop_tooltip_asset_catalog(drag);
+ }
+ return drop_tooltip_asset_list(drag);
+}
+
+std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag &drag) const
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
+
+ const ::AssetLibrary *asset_library = tree_view<AssetCatalogTreeView>().asset_library_;
+ bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(asset_library);
+ wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag);
+ AssetCatalog *src_catalog = catalog_service->find_catalog(catalog_drag->drag_catalog_id);
+
+ return std::string(TIP_("Move Catalog")) + " '" + src_catalog->path.name() + "' " +
+ TIP_("into") + " '" + catalog_item_.get_name() + "'";
+}
+
+std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_LIST);
+
+ const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
+ const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
+
+ /* Don't try to be smart by dynamically adding the 's' for the plural. Just makes translation
+ * harder, so use full literals. */
+ std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") :
+ TIP_("Move asset to catalog");
+
+ return basic_tip + ": " + catalog_item_.get_name() + " (" + catalog_item_.catalog_path().str() +
+ ")";
+}
+
+bool AssetCatalogDropController::on_drop(const wmDrag &drag)
+{
+ if (drag.type == WM_DRAG_ASSET_CATALOG) {
+ return drop_asset_catalog_into_catalog(drag);
+ }
+ return drop_assets_into_catalog(tree_view<AssetCatalogTreeView>(),
+ drag,
+ catalog_item_.get_catalog_id(),
+ catalog_item_.get_simple_name());
+}
+
+bool AssetCatalogDropController::drop_asset_catalog_into_catalog(const wmDrag &drag)
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
+ wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag);
+ ED_asset_catalog_move(
+ &get_asset_library(), catalog_drag->drag_catalog_id, catalog_item_.get_catalog_id());
+
+ WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr);
+ return true;
+}
+
+bool AssetCatalogDropController::drop_assets_into_catalog(const AssetCatalogTreeView &tree_view,
+ const wmDrag &drag,
+ CatalogID catalog_id,
+ StringRefNull simple_name)
+{
+ BLI_assert(drag.type == WM_DRAG_ASSET_LIST);
+ const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
+ if (!asset_drags) {
+ return false;
+ }
+
+ LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) {
+ if (asset_item->is_external) {
+ /* Only internal assets can be modified! */
+ continue;
+ }
+ BKE_asset_metadata_catalog_id_set(
+ asset_item->asset_data.local_id->asset_data, catalog_id, simple_name.c_str());
+
+ /* Trigger re-run of filtering to update visible assets. */
+ filelist_tag_needs_filtering(tree_view.space_file_.files);
+ file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, nullptr);
+ }
+
+ return true;
+}
+
+bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag,
+ const char **r_disabled_hint)
+{
+ const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
+
+ *r_disabled_hint = nullptr;
+ /* There needs to be at least one asset from the current file. */
+ LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) {
+ if (!asset_item->is_external) {
+ return true;
+ }
+ }
+
+ *r_disabled_hint = "Only assets from this current file can be moved between catalogs";
+ return false;
+}
+
+::AssetLibrary &AssetCatalogDropController::get_asset_library() const
+{
+ return *tree_view<AssetCatalogTreeView>().asset_library_;
+}
+
+/* ---------------------------------------------------------------------- */
+
+AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeItem &catalog_item)
+ : catalog_item_(catalog_item)
+{
+}
+
+int AssetCatalogDragController::get_drag_type() const
+{
+ return WM_DRAG_ASSET_CATALOG;
+}
+
+void *AssetCatalogDragController::create_drag_data() const
+{
+ wmDragAssetCatalog *drag_catalog = (wmDragAssetCatalog *)MEM_callocN(sizeof(*drag_catalog),
+ __func__);
+ drag_catalog->drag_catalog_id = catalog_item_.get_catalog_id();
+ return drag_catalog;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
+{
+ ui::BasicTreeViewItem::build_row(row);
+
+ PointerRNA *props;
+
+ UI_but_extra_operator_icon_add(
+ (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK);
+
+ props = UI_but_extra_operator_icon_add(
+ (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ /* No parent path to use the root level. */
+ RNA_string_set(props, "parent_path", nullptr);
+}
+
+/* ---------------------------------------------------------------------- */
+
+std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem::
+ create_drop_controller() const
+{
+ return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>(
+ static_cast<AssetCatalogTreeView &>(get_tree_view()));
+}
+
+AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view)
+ : ui::AbstractTreeViewItemDropController(tree_view)
+{
+}
+
+bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop(
+ const wmDrag &drag, const char **r_disabled_hint) const
+{
+ if (drag.type != WM_DRAG_ASSET_LIST) {
+ return false;
+ }
+ return AssetCatalogDropController::has_droppable_asset(drag, r_disabled_hint);
+}
+
+std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip(
+ const wmDrag &drag) const
+{
+ const ListBase *asset_drags = WM_drag_asset_list_get(&drag);
+ const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags);
+
+ return is_multiple_assets ? TIP_("Move assets out of any catalog") :
+ TIP_("Move asset out of any catalog");
+}
+
+bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(const wmDrag &drag)
+{
+ /* Assign to nil catalog ID. */
+ return AssetCatalogDropController::drop_assets_into_catalog(
+ tree_view<AssetCatalogTreeView>(), drag, CatalogID{});
+}
+
+} // namespace blender::ed::asset_browser
+
+/* ---------------------------------------------------------------------- */
+
+namespace blender::ed::asset_browser {
+
+class AssetCatalogFilterSettings {
+ public:
+ eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility;
+ bUUID asset_catalog_id;
+
+ std::unique_ptr<AssetCatalogFilter> catalog_filter;
+};
+
+} // namespace blender::ed::asset_browser
+
+using namespace blender::ed::asset_browser;
+
+FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings()
+{
+ AssetCatalogFilterSettings *filter_settings = OBJECT_GUARDED_NEW(AssetCatalogFilterSettings);
+ return reinterpret_cast<FileAssetCatalogFilterSettingsHandle *>(filter_settings);
+}
+
+void file_delete_asset_catalog_filter_settings(
+ FileAssetCatalogFilterSettingsHandle **filter_settings_handle)
+{
+ AssetCatalogFilterSettings **filter_settings = reinterpret_cast<AssetCatalogFilterSettings **>(
+ filter_settings_handle);
+ OBJECT_GUARDED_SAFE_DELETE(*filter_settings, AssetCatalogFilterSettings);
+}
+
+/**
+ * \return True if the file list should update its filtered results (e.g. because filtering
+ * parameters changed).
+ */
+bool file_set_asset_catalog_filter_settings(
+ FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
+ eFileSel_Params_AssetCatalogVisibility catalog_visibility,
+ ::bUUID catalog_id)
+{
+ AssetCatalogFilterSettings *filter_settings = reinterpret_cast<AssetCatalogFilterSettings *>(
+ filter_settings_handle);
+ bool needs_update = false;
+
+ if (filter_settings->asset_catalog_visibility != catalog_visibility) {
+ filter_settings->asset_catalog_visibility = catalog_visibility;
+ needs_update = true;
+ }
+
+ if (filter_settings->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG &&
+ !BLI_uuid_equal(filter_settings->asset_catalog_id, catalog_id)) {
+ filter_settings->asset_catalog_id = catalog_id;
+ needs_update = true;
+ }
+
+ return needs_update;
+}
+
+void file_ensure_updated_catalog_filter_data(
+ FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
+ const ::AssetLibrary *asset_library)
+{
+ AssetCatalogFilterSettings *filter_settings = reinterpret_cast<AssetCatalogFilterSettings *>(
+ filter_settings_handle);
+ const AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(
+ asset_library);
+
+ if (filter_settings->asset_catalog_visibility != FILE_SHOW_ASSETS_ALL_CATALOGS) {
+ filter_settings->catalog_filter = std::make_unique<AssetCatalogFilter>(
+ catalog_service->create_catalog_filter(filter_settings->asset_catalog_id));
+ }
+}
+
+bool file_is_asset_visible_in_catalog_filter_settings(
+ const FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
+ const AssetMetaData *asset_data)
+{
+ const AssetCatalogFilterSettings *filter_settings =
+ reinterpret_cast<const AssetCatalogFilterSettings *>(filter_settings_handle);
+
+ switch (filter_settings->asset_catalog_visibility) {
+ case FILE_SHOW_ASSETS_WITHOUT_CATALOG:
+ return !filter_settings->catalog_filter->is_known(asset_data->catalog_id);
+ case FILE_SHOW_ASSETS_FROM_CATALOG:
+ return filter_settings->catalog_filter->contains(asset_data->catalog_id);
+ case FILE_SHOW_ASSETS_ALL_CATALOGS:
+ /* All asset files should be visible. */
+ return true;
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library,
+ uiLayout *layout,
+ SpaceFile *space_file,
+ FileAssetSelectParams *params)
+{
+ uiBlock *block = uiLayoutGetBlock(layout);
+
+ UI_block_layout_set_current(block, layout);
+
+ ui::AbstractTreeView *tree_view = UI_block_add_view(
+ *block,
+ "asset catalog tree view",
+ std::make_unique<ed::asset_browser::AssetCatalogTreeView>(
+ asset_library, params, *space_file));
+
+ ui::TreeViewBuilder builder(*block);
+ builder.build_tree_view(*tree_view);
+}
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 6e409d2f5a4..66aabe39e44 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -25,6 +25,8 @@
#include <math.h>
#include <string.h>
+#include "MEM_guardedalloc.h"
+
#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
@@ -80,6 +82,9 @@ void ED_file_path_button(bScreen *screen,
PointerRNA params_rna_ptr;
uiBut *but;
+ BLI_assert_msg(params != NULL,
+ "File select parameters not set. The caller is expected to check this.");
+
RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, &params_rna_ptr);
/* callbacks for operator check functions */
@@ -108,7 +113,7 @@ void ED_file_path_button(bScreen *screen,
UI_but_func_complete_set(but, autocomplete_directory, NULL);
UI_but_funcN_set(but, file_directory_enter_handle, NULL, but);
- /* TODO, directory editing is non-functional while a library is loaded
+ /* TODO: directory editing is non-functional while a library is loaded
* until this is properly supported just disable it. */
if (sfile && sfile->files && filelist_lib(sfile->files)) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
@@ -165,10 +170,10 @@ static void file_draw_icon(const SpaceFile *sfile,
const float a2 = dimmed ? 0.3f : 0.0f;
but = uiDefIconBut(
block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, a1, a2, NULL);
- UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
+ UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN);
if (drag) {
- /* TODO duplicated from file_draw_preview(). */
+ /* TODO: duplicated from file_draw_preview(). */
ID *id;
if ((id = filelist_file_get_id(file))) {
@@ -183,9 +188,9 @@ static void file_draw_icon(const SpaceFile *sfile,
BLI_assert(asset_params != NULL);
UI_but_drag_set_asset(but,
- file->name,
+ &(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
- file->blentype,
+ file->asset_data,
asset_params->import_type,
icon,
preview_image,
@@ -238,8 +243,9 @@ static void file_draw_string(int sx,
}
/**
- * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on
- * completion.
+ * \param r_sx, r_sy: The lower right corner of the last line drawn, plus the height of the last
+ * line. This is the cursor position on completion to allow drawing more text
+ * behind that.
*/
static void file_draw_string_multiline(int sx,
int sy,
@@ -461,6 +467,17 @@ static void file_draw_preview(const SpaceFile *sfile,
UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
}
+ const bool is_current_main_data = filelist_file_get_id(file) != NULL;
+ if (is_current_main_data) {
+ /* Smaller, fainter icon at the top-right indicating that the file represents data from the
+ * current file (from current #Main in fact). */
+ float icon_x, icon_y;
+ const uchar light[4] = {255, 255, 255, 255};
+ icon_x = xco + ex - UI_UNIT_X;
+ icon_y = yco + ey - UI_UNIT_Y;
+ UI_icon_draw_ex(icon_x, icon_y, ICON_FILE_BLEND, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
+ }
+
/* Contrasting outline around some preview types. */
if (show_outline) {
GPUVertFormat *format = immVertexFormat();
@@ -489,7 +506,8 @@ static void file_draw_preview(const SpaceFile *sfile,
UI_but_drag_set_id(but, id);
}
/* path is no more static, cannot give it directly to but... */
- else if (file->typeflag & FILE_TYPE_ASSET) {
+ else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS &&
+ (file->typeflag & FILE_TYPE_ASSET) != 0) {
char blend_path[FILE_MAX_LIBEXTRA];
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
@@ -497,9 +515,9 @@ static void file_draw_preview(const SpaceFile *sfile,
BLI_assert(asset_params != NULL);
UI_but_drag_set_asset(but,
- file->name,
+ &(AssetHandle){.file_data = file},
BLI_strdup(blend_path),
- file->blentype,
+ file->asset_data,
asset_params->import_type,
icon,
imb,
@@ -520,6 +538,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
char orgname[FILE_MAX + 12];
char filename[FILE_MAX + 12];
wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
ARegion *region = CTX_wm_region(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
@@ -539,17 +558,15 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
else {
/* If rename is successful, scroll to newly renamed entry. */
BLI_strncpy(params->renamefile, filename, sizeof(params->renamefile));
- params->rename_flag = FILE_PARAMS_RENAME_POSTSCROLL_PENDING;
-
- if (sfile->smoothscroll_timer != NULL) {
- WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
- }
- sfile->smoothscroll_timer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER1, 1.0 / 1000.0);
- sfile->scroll_offset = 0;
+ file_params_invoke_rename_postscroll(wm, win, sfile);
}
/* to make sure we show what is on disk */
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
+ }
+ else {
+ /* Renaming failed, reset the name for further renaming handling. */
+ BLI_strncpy(params->renamefile, oldname, sizeof(params->renamefile));
}
ED_region_tag_redraw(region);
@@ -724,40 +741,45 @@ static void draw_columnheader_columns(const FileSelectParams *params,
/**
* Updates the stat string stored in file->entry if necessary.
*/
-static const char *filelist_get_details_column_string(FileAttributeColumnType column,
- const FileDirEntry *file,
- const bool small_size,
- const bool update_stat_strings)
+static const char *filelist_get_details_column_string(
+ FileAttributeColumnType column,
+ /* Generated string will be cached in the file, so non-const. */
+ FileDirEntry *file,
+ const bool small_size,
+ const bool update_stat_strings)
{
switch (column) {
case COLUMN_DATETIME:
if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) {
- if ((file->entry->datetime_str[0] == '\0') || update_stat_strings) {
+ if ((file->draw_data.datetime_str[0] == '\0') || update_stat_strings) {
char date[FILELIST_DIRENTRY_DATE_LEN], time[FILELIST_DIRENTRY_TIME_LEN];
bool is_today, is_yesterday;
BLI_filelist_entry_datetime_to_string(
- NULL, file->entry->time, small_size, time, date, &is_today, &is_yesterday);
+ NULL, file->time, small_size, time, date, &is_today, &is_yesterday);
if (is_today || is_yesterday) {
BLI_strncpy(date, is_today ? N_("Today") : N_("Yesterday"), sizeof(date));
}
- BLI_snprintf(
- file->entry->datetime_str, sizeof(file->entry->datetime_str), "%s %s", date, time);
+ BLI_snprintf(file->draw_data.datetime_str,
+ sizeof(file->draw_data.datetime_str),
+ "%s %s",
+ date,
+ time);
}
- return file->entry->datetime_str;
+ return file->draw_data.datetime_str;
}
break;
case COLUMN_SIZE:
if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
!(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
- if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
+ if ((file->draw_data.size_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_size_to_string(
- NULL, file->entry->size, small_size, file->entry->size_str);
+ NULL, file->size, small_size, file->draw_data.size_str);
}
- return file->entry->size_str;
+ return file->draw_data.size_str;
}
break;
default:
@@ -769,7 +791,7 @@ static const char *filelist_get_details_column_string(FileAttributeColumnType co
static void draw_details_columns(const FileSelectParams *params,
const FileLayout *layout,
- const FileDirEntry *file,
+ FileDirEntry *file,
const int pos_x,
const int pos_y,
const uchar text_col[4])
@@ -810,6 +832,8 @@ static void draw_details_columns(const FileSelectParams *params,
void file_draw_list(const bContext *C, ARegion *region)
{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
FileLayout *layout = ED_fileselect_get_layout(sfile, region);
@@ -880,12 +904,12 @@ void file_draw_list(const bContext *C, ARegion *region)
// printf("%s: preview task: %d\n", __func__, previews_running);
if (previews_running && !sfile->previews_timer) {
sfile->previews_timer = WM_event_add_timer_notifier(
- CTX_wm_manager(C), CTX_wm_window(C), NC_SPACE | ND_SPACE_FILE_PREVIEW, 0.01);
+ wm, win, NC_SPACE | ND_SPACE_FILE_PREVIEW, 0.01);
}
if (!previews_running && sfile->previews_timer) {
/* Preview is not running, no need to keep generating update events! */
// printf("%s: Inactive preview task, sleeping!\n", __func__);
- WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), sfile->previews_timer);
+ WM_event_remove_timer_notifier(wm, win, sfile->previews_timer);
sfile->previews_timer = NULL;
}
}
@@ -996,8 +1020,19 @@ void file_draw_list(const bContext *C, ARegion *region)
UI_but_flag_enable(but, UI_BUT_NO_UTF8); /* allow non utf8 names */
UI_but_flag_disable(but, UI_BUT_UNDO);
if (false == UI_but_active_only(C, region, block, but)) {
- file_selflag = filelist_entry_select_set(
- sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL);
+ /* Note that this is the only place where we can also handle a cancelled renaming. */
+
+ file_params_rename_end(wm, win, sfile, file);
+
+ /* After the rename button is removed, we need to make sure the view is redrawn once more,
+ * in case selection changed. Usually UI code would trigger that redraw, but the rename
+ * operator may have been called from a different region.
+ * Tagging regions for redrawing while drawing is rightfully prevented. However, this
+ * active button removing basically introduces handling logic to drawing code. So a
+ * notifier should be an acceptable workaround. */
+ WM_event_add_notifier_ex(wm, win, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
+ file_selflag = filelist_entry_select_get(sfile->files, file, CHECK_ALL);
}
}
@@ -1032,7 +1067,9 @@ void file_draw_list(const bContext *C, ARegion *region)
layout->curr_size = params->thumbnail_size;
}
-static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region)
+static void file_draw_invalid_library_hint(const bContext *C,
+ const SpaceFile *sfile,
+ ARegion *region)
{
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
@@ -1040,9 +1077,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion
file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path));
uchar text_col[4];
- uchar text_alert_col[4];
UI_GetThemeColor4ubv(TH_TEXT, text_col);
- UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col);
const View2D *v2d = &region->v2d;
const int pad = sfile->layout->tile_border_x;
@@ -1053,23 +1088,42 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion
int sy = v2d->tot.ymax;
{
- const char *message = TIP_("Library not found");
- const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path);
- char *draw_string = alloca(draw_string_str_len);
- BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path);
- file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy);
+ const char *message = TIP_("Path to asset library does not exist:");
+ file_draw_string_multiline(sx, sy, message, width, line_height, text_col, NULL, &sy);
+
+ sy -= line_height;
+ file_draw_string(sx, sy, library_ui_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col);
}
- /* Next line, but separate it a bit further. */
- sy -= line_height;
+ /* Separate a bit further. */
+ sy -= line_height * 2.2f;
{
UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
const char *suggestion = TIP_(
- "Set up the library or edit libraries in the Preferences, File Paths section");
+ "Asset Libraries are local directories that can contain .blend files with assets inside.\n"
+ "Manage Asset Libraries from the File Paths section in Preferences");
file_draw_string_multiline(
- sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL);
+ sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy);
+
+ uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
+ uiBut *but = uiDefIconTextButO(block,
+ UI_BTYPE_BUT,
+ "SCREEN_OT_userpref_show",
+ WM_OP_INVOKE_DEFAULT,
+ ICON_PREFERENCES,
+ NULL,
+ sx + UI_UNIT_X,
+ sy - line_height - UI_UNIT_Y * 1.2f,
+ UI_UNIT_X * 8,
+ UI_UNIT_Y,
+ NULL);
+ PointerRNA *but_opptr = UI_but_operator_ptr_get(but);
+ RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS);
+
+ UI_block_end(C, block);
+ UI_block_draw(C, block);
}
}
@@ -1077,7 +1131,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion
* Draw a string hint if the file list is invalid.
* \return true if the list is invalid and a hint was drawn.
*/
-bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
+bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region)
{
FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
/* Only for asset browser. */
@@ -1085,12 +1139,12 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
return false;
}
/* Check if the library exists. */
- if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) ||
+ if ((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) ||
filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
return false;
}
- file_draw_invalid_library_hint(sfile, region);
+ file_draw_invalid_library_hint(C, sfile, region);
return true;
}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index a7c57459729..4be5d6d8008 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -23,12 +23,21 @@
#pragma once
+#include "DNA_space_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* internal exports only */
struct ARegion;
struct ARegionType;
+struct AssetLibrary;
struct FileSelectParams;
+struct FileAssetSelectParams;
struct SpaceFile;
+struct uiLayout;
struct View2D;
/* file_draw.c */
@@ -38,7 +47,7 @@ struct View2D;
void file_calc_previews(const bContext *C, ARegion *region);
void file_draw_list(const bContext *C, ARegion *region);
-bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region);
+bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region);
void file_draw_check_ex(bContext *C, struct ScrArea *area);
void file_draw_check(bContext *C);
@@ -62,7 +71,6 @@ void FILE_OT_bookmark_cleanup(struct wmOperatorType *ot);
void FILE_OT_bookmark_move(struct wmOperatorType *ot);
void FILE_OT_reset_recent(wmOperatorType *ot);
void FILE_OT_hidedot(struct wmOperatorType *ot);
-void FILE_OT_associate_blend(struct wmOperatorType *ot);
void FILE_OT_execute(struct wmOperatorType *ot);
void FILE_OT_mouse_execute(struct wmOperatorType *ot);
void FILE_OT_cancel(struct wmOperatorType *ot);
@@ -71,6 +79,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot);
void FILE_OT_previous(struct wmOperatorType *ot);
void FILE_OT_next(struct wmOperatorType *ot);
void FILE_OT_refresh(struct wmOperatorType *ot);
+void FILE_OT_asset_library_refresh(struct wmOperatorType *ot);
void FILE_OT_filenum(struct wmOperatorType *ot);
void FILE_OT_delete(struct wmOperatorType *ot);
void FILE_OT_rename(struct wmOperatorType *ot);
@@ -93,6 +102,9 @@ 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);
+/* space_file.c */
+extern const char *file_context_dir[]; /* doc access */
+
/* filesel.c */
void fileselect_refresh_params(struct SpaceFile *sfile);
void fileselect_file_set(SpaceFile *sfile, const int index);
@@ -109,10 +121,22 @@ FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
float file_string_width(const char *str);
float file_font_pointsize(void);
+void file_select_deselect_all(SpaceFile *sfile, uint flag);
int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matched_file);
int autocomplete_directory(struct bContext *C, char *str, void *arg_v);
int autocomplete_file(struct bContext *C, char *str, void *arg_v);
+void file_params_smoothscroll_timer_clear(struct wmWindowManager *wm,
+ struct wmWindow *win,
+ SpaceFile *sfile);
+void file_params_renamefile_clear(struct FileSelectParams *params);
+void file_params_invoke_rename_postscroll(struct wmWindowManager *wm,
+ struct wmWindow *win,
+ SpaceFile *sfile);
+void file_params_rename_end(struct wmWindowManager *wm,
+ struct wmWindow *win,
+ SpaceFile *sfile,
+ struct FileDirEntry *rename_file);
void file_params_renamefile_activate(struct SpaceFile *sfile, struct FileSelectParams *params);
typedef void *onReloadFnData;
@@ -133,8 +157,40 @@ void file_on_reload_callback_register(struct SpaceFile *sfile,
/* file_panels.c */
void file_tool_props_region_panels_register(struct ARegionType *art);
void file_execute_region_panels_register(struct ARegionType *art);
+void file_tools_region_panels_register(struct ARegionType *art);
/* file_utils.c */
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds);
void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
+
+/* asset_catalog_tree_view.cc */
+
+/* C-handle for #ed::asset_browser::AssetCatalogFilterSettings. */
+typedef struct FileAssetCatalogFilterSettingsHandle FileAssetCatalogFilterSettingsHandle;
+
+FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings(void);
+void file_delete_asset_catalog_filter_settings(
+ FileAssetCatalogFilterSettingsHandle **filter_settings_handle);
+/**
+ * \return True if the stored filter settings were modified.
+ */
+bool file_set_asset_catalog_filter_settings(
+ FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
+ eFileSel_Params_AssetCatalogVisibility catalog_visibility,
+ bUUID catalog_id);
+void file_ensure_updated_catalog_filter_data(
+ FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
+ const struct AssetLibrary *asset_library);
+bool file_is_asset_visible_in_catalog_filter_settings(
+ const FileAssetCatalogFilterSettingsHandle *filter_settings_handle,
+ const AssetMetaData *asset_data);
+
+void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library,
+ struct uiLayout *layout,
+ struct SpaceFile *space_file,
+ struct FileAssetSelectParams *params);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 0584e2ff938..844514759f3 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -104,15 +104,6 @@ static FileSelection find_file_mouse_rect(SpaceFile *sfile,
return sel;
}
-static void file_deselect_all(SpaceFile *sfile, uint flag)
-{
- FileSelection sel;
- sel.first = 0;
- sel.last = filelist_files_ensure(sfile->files) - 1;
-
- filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
-}
-
typedef enum FileSelect {
FILE_SELECT_NOTHING = 0,
FILE_SELECT_DIR = 1,
@@ -239,7 +230,7 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
}
/**
- * \warning: loops over all files so better use cautiously
+ * \warning Loops over all files so better use cautiously.
*/
static bool file_is_any_selected(struct FileList *files)
{
@@ -444,7 +435,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve
if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) {
int idx;
- file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
+ file_select_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
filelist_entries_select_index_range_set(
sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
@@ -472,7 +463,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve
params->highlight_file = -1;
params->sel_first = params->sel_last = -1;
fileselect_file_set(sfile, params->active_file);
- file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
+ file_select_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
}
@@ -491,7 +482,7 @@ static int file_box_select_exec(bContext *C, wmOperator *op)
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- file_deselect_all(sfile, FILE_SEL_SELECTED);
+ file_select_deselect_all(sfile, FILE_SEL_SELECTED);
}
ED_fileselect_layout_isect_rect(sfile->layout, &region->v2d, &rect, &rect);
@@ -522,6 +513,7 @@ void FILE_OT_select_box(wmOperatorType *ot)
ot->invoke = WM_gesture_box_invoke;
ot->exec = file_box_select_exec;
ot->modal = file_box_select_modal;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
ot->cancel = WM_gesture_box_cancel;
@@ -544,7 +536,7 @@ static rcti file_select_mval_to_select_rect(const int mval[2])
return rect;
}
-static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int file_select_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -554,26 +546,47 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const bool fill = RNA_boolean_get(op->ptr, "fill");
const bool do_diropen = RNA_boolean_get(op->ptr, "open");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
+ const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected");
+ /* Used so right mouse clicks can do both, activate and spawn the context menu. */
+ const bool pass_through = RNA_boolean_get(op->ptr, "pass_through");
+ bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
if (region->regiontype != RGN_TYPE_WINDOW) {
return OPERATOR_CANCELLED;
}
- rect = file_select_mval_to_select_rect(event->mval);
+ int mval[2];
+ mval[0] = RNA_int_get(op->ptr, "mouse_x");
+ mval[1] = RNA_int_get(op->ptr, "mouse_y");
+ rect = file_select_mval_to_select_rect(mval);
if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &region->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
+ if (extend || fill) {
+ wait_to_deselect_others = false;
+ }
+
+ int ret_val = OPERATOR_FINISHED;
+
const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (sfile && params) {
int idx = params->highlight_file;
int numfiles = filelist_files_ensure(sfile->files);
if ((idx >= 0) && (idx < numfiles)) {
+ const bool is_selected = filelist_entry_select_index_get(sfile->files, idx, CHECK_ALL) &
+ FILE_SEL_SELECTED;
+ if (only_activate_if_selected && is_selected) {
+ /* Don't deselect other items. */
+ }
+ else if (wait_to_deselect_others && is_selected) {
+ ret_val = OPERATOR_RUNNING_MODAL;
+ }
/* single select, deselect all selected first */
- if (!extend) {
- file_deselect_all(sfile, FILE_SEL_SELECTED);
+ else if (!extend) {
+ file_select_deselect_all(sfile, FILE_SEL_SELECTED);
}
}
}
@@ -588,7 +601,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (ret == FILE_SELECT_NOTHING) {
if (deselect_all) {
- file_deselect_all(sfile, FILE_SEL_SELECTED);
+ file_select_deselect_all(sfile, FILE_SEL_SELECTED);
}
}
else if (ret == FILE_SELECT_DIR) {
@@ -601,7 +614,10 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
- return OPERATOR_FINISHED;
+ if ((ret_val == OPERATOR_FINISHED) && pass_through) {
+ ret_val |= OPERATOR_PASS_THROUGH;
+ }
+ return ret_val;
}
void FILE_OT_select(wmOperatorType *ot)
@@ -614,10 +630,14 @@ void FILE_OT_select(wmOperatorType *ot)
ot->description = "Handle mouse clicks to select and activate items";
/* api callbacks */
- ot->invoke = file_select_invoke;
+ ot->invoke = WM_generic_select_invoke;
+ ot->exec = file_select_exec;
+ ot->modal = WM_generic_select_modal;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
+ WM_operator_properties_generic_select(ot);
prop = RNA_def_boolean(ot->srna,
"extend",
false,
@@ -635,6 +655,20 @@ void FILE_OT_select(wmOperatorType *ot)
"Deselect On Nothing",
"Deselect all when nothing under the cursor");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "only_activate_if_selected",
+ false,
+ "Only Activate if Selected",
+ "Do not change selection if the item under the cursor is already "
+ "selected, only activate it");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "pass_through",
+ false,
+ "Pass Through",
+ "Even on successful execution, pass the event on so other operators can "
+ "execute on it as well");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
/** \} */
@@ -721,7 +755,7 @@ static bool file_walk_select_selection_set(wmWindow *win,
}
else {
/* deselect all first */
- file_deselect_all(sfile, FILE_SEL_SELECTED);
+ file_select_deselect_all(sfile, FILE_SEL_SELECTED);
/* highlight file under mouse pos */
params->highlight_file = -1;
@@ -873,6 +907,7 @@ void FILE_OT_select_walk(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_walk_select_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
@@ -960,6 +995,7 @@ void FILE_OT_select_all(wmOperatorType *ot)
/* api callbacks */
ot->exec = file_select_all_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
/* properties */
@@ -1012,6 +1048,7 @@ void FILE_OT_view_selected(wmOperatorType *ot)
/* api callbacks */
ot->exec = file_view_selected_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
@@ -1023,7 +1060,6 @@ void FILE_OT_view_selected(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);
@@ -1056,7 +1092,8 @@ void FILE_OT_select_bookmark(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_select_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* properties */
prop = RNA_def_string(ot->srna, "dir", NULL, FILE_MAXDIR, "Directory", "");
@@ -1102,7 +1139,8 @@ void FILE_OT_bookmark_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_add_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
}
/** \} */
@@ -1156,7 +1194,8 @@ void FILE_OT_bookmark_delete(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_delete_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* properties */
prop = RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
@@ -1214,7 +1253,8 @@ void FILE_OT_bookmark_cleanup(wmOperatorType *ot)
/* api callbacks */
ot->exec = bookmark_cleanup_exec;
- ot->poll = ED_operator_file_active;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* properties */
}
@@ -1302,8 +1342,9 @@ void FILE_OT_bookmark_move(wmOperatorType *ot)
ot->description = "Move the active bookmark up/down in the list";
/* api callbacks */
- ot->poll = ED_operator_file_active;
ot->exec = bookmark_move_exec;
+ /* Bookmarks are for file browsing only (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
/* flags */
ot->flag = OPTYPE_REGISTER; /* No undo! */
@@ -1350,7 +1391,8 @@ void FILE_OT_reset_recent(wmOperatorType *ot)
/* api callbacks */
ot->exec = reset_recent_exec;
- ot->poll = ED_operator_file_active;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
}
/** \} */
@@ -1365,7 +1407,9 @@ int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my)
FileSelectParams *params;
int numfiles, origfile;
- if (sfile == NULL || sfile->files == NULL) {
+ /* In case blender starts where the mouse is over a File browser,
+ * this operator can be invoked when the `sfile` or `sfile->layout` isn't initialized yet. */
+ if (sfile == NULL || sfile->files == NULL || sfile->layout == NULL) {
return 0;
}
@@ -1405,7 +1449,7 @@ static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv
ARegion *region = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- if (!file_highlight_set(sfile, region, event->x, event->y)) {
+ if (!file_highlight_set(sfile, region, event->xy[0], event->xy[1])) {
return OPERATOR_PASS_THROUGH;
}
@@ -1423,6 +1467,7 @@ void FILE_OT_highlight(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_highlight_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
@@ -1474,6 +1519,7 @@ void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_column_sort_ui_context_invoke;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
ot->flag = OPTYPE_INTERNAL;
@@ -1487,7 +1533,7 @@ void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
static bool file_operator_poll(bContext *C)
{
- bool poll = ED_operator_file_active(C);
+ bool poll = ED_operator_file_browsing_active(C);
SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile || !sfile->op) {
@@ -1810,7 +1856,7 @@ void FILE_OT_execute(struct wmOperatorType *ot)
*
* Avoid using #file_operator_poll since this is also used for entering directories
* which is used even when the file manager doesn't have an operator. */
- ot->poll = ED_operator_file_active;
+ ot->poll = ED_operator_file_browsing_active;
}
/**
@@ -1865,7 +1911,7 @@ void FILE_OT_mouse_execute(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_execute_mouse_invoke;
- ot->poll = ED_operator_file_active;
+ ot->poll = ED_operator_file_browsing_active;
ot->flag = OPTYPE_INTERNAL;
}
@@ -1882,7 +1928,7 @@ static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
SpaceFile *sfile = CTX_wm_space_file(C);
struct FSMenu *fsmenu = ED_fsmenu_get();
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
/* refresh system directory menu */
fsmenu_refresh_system_category(fsmenu);
@@ -1904,7 +1950,36 @@ void FILE_OT_refresh(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_refresh_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Refresh Asset Library Operator
+ * \{ */
+
+static int file_asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ ED_fileselect_clear(wm, sfile);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void FILE_OT_asset_library_refresh(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Refresh Asset Library";
+ ot->description = "Reread assets and asset catalogs from the asset library on disk";
+ ot->idname = "FILE_OT_asset_library_refresh";
+
+ /* api callbacks */
+ ot->exec = file_asset_library_refresh_exec;
+ ot->poll = ED_operator_asset_browsing_active;
}
/** \} */
@@ -1944,7 +2019,8 @@ void FILE_OT_parent(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_parent_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -1979,7 +2055,8 @@ void FILE_OT_previous(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_previous_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -2015,7 +2092,8 @@ void FILE_OT_next(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_next_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -2059,13 +2137,15 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
}
}
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+
/* if we are not editing, we are done */
if (edit_idx == -1) {
/* Do not invalidate timer if filerename is still pending,
* we might still be building the filelist and yet have to find edited entry. */
if (params->rename_flag == 0) {
- WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
- sfile->smoothscroll_timer = NULL;
+ file_params_smoothscroll_timer_clear(wm, win, sfile);
}
return OPERATOR_PASS_THROUGH;
}
@@ -2073,8 +2153,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
/* we need the correct area for scrolling */
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (!region || region->regiontype != RGN_TYPE_WINDOW) {
- WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
- sfile->smoothscroll_timer = NULL;
+ file_params_smoothscroll_timer_clear(wm, win, sfile);
return OPERATOR_PASS_THROUGH;
}
@@ -2093,7 +2172,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
sfile->layout, (int)region->v2d.cur.xmin, (int)-region->v2d.cur.ymax);
const int last_visible_item = first_visible_item + numfiles_layout + 1;
- /* Note: the special case for vertical layout is because filename is at the bottom of items then,
+ /* NOTE: the special case for vertical layout is because filename is at the bottom of items then,
* so we artificially move current row back one step, to ensure we show bottom of
* active item rather than its top (important in case visible height is low). */
const int middle_offset = max_ii(
@@ -2131,13 +2210,11 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
(max_middle_offset - middle_offset < items_block_size));
if (is_ready && (is_centered || is_full_start || is_full_end)) {
- WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
- sfile->smoothscroll_timer = NULL;
+ file_params_smoothscroll_timer_clear(wm, win, sfile);
/* Post-scroll (after rename has been validated by user) is done,
* rename process is totally finished, cleanup. */
if ((params->rename_flag & FILE_PARAMS_RENAME_POSTSCROLL_ACTIVE) != 0) {
- params->renamefile[0] = '\0';
- params->rename_flag = 0;
+ file_params_renamefile_clear(params);
}
return OPERATOR_FINISHED;
}
@@ -2207,7 +2284,7 @@ void FILE_OT_smoothscroll(wmOperatorType *ot)
/* api callbacks */
ot->invoke = file_smoothscroll_invoke;
-
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
@@ -2251,7 +2328,8 @@ void FILE_OT_filepath_drop(wmOperatorType *ot)
ot->idname = "FILE_OT_filepath_drop";
ot->exec = filepath_drop_exec;
- ot->poll = WM_operator_winactive;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
RNA_def_string_file_path(ot->srna, "filepath", "Path", FILE_MAX, "", "");
}
@@ -2346,21 +2424,22 @@ static int file_directory_new_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ eFileSel_Params_RenameFlag rename_flag = params->rename_flag;
+
/* If we don't enter the directory directly, remember file to jump into editing. */
if (do_diropen == false) {
+ BLI_assert_msg(params->rename_id == NULL,
+ "File rename handling should immediately clear rename_id when done, "
+ "because otherwise it will keep taking precedence over renamefile.");
BLI_strncpy(params->renamefile, name, FILE_MAXFILE);
- params->rename_flag = FILE_PARAMS_RENAME_PENDING;
+ rename_flag = FILE_PARAMS_RENAME_PENDING;
}
- /* Set timer to smoothly view newly generated file. */
- if (sfile->smoothscroll_timer != NULL) {
- WM_event_remove_timer(wm, CTX_wm_window(C), sfile->smoothscroll_timer);
- }
- sfile->smoothscroll_timer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER1, 1.0 / 1000.0);
- sfile->scroll_offset = 0;
+ file_params_invoke_rename_postscroll(wm, CTX_wm_window(C), sfile);
+ params->rename_flag = rename_flag;
/* reload dir to make sure we're seeing what's in the directory */
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
if (do_diropen) {
BLI_strncpy(params->dir, path, sizeof(params->dir));
@@ -2384,7 +2463,8 @@ void FILE_OT_directory_new(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_operator_confirm_or_exec;
ot->exec = file_directory_new_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
prop = RNA_def_string_dir_path(
ot->srna, "directory", NULL, FILE_MAX, "Directory", "Name of new directory");
@@ -2400,7 +2480,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot)
/** \name Refresh File List Operator
* \{ */
-/* TODO This should go to BLI_path_utils. */
+/* TODO: This should go to BLI_path_utils. */
static void file_expand_directory(bContext *C)
{
Main *bmain = CTX_data_main(C);
@@ -2411,12 +2491,14 @@ static void file_expand_directory(bContext *C)
if (BLI_path_is_rel(params->dir)) {
/* Use of 'default' folder here is just to avoid an error message on '//' prefix. */
BLI_path_abs(params->dir,
- G.relbase_valid ? BKE_main_blendfile_path(bmain) : BKE_appdir_folder_default());
+ G.relbase_valid ? BKE_main_blendfile_path(bmain) :
+ BKE_appdir_folder_default_or_root());
}
else if (params->dir[0] == '~') {
char tmpstr[sizeof(params->dir) - 1];
BLI_strncpy(tmpstr, params->dir + 1, sizeof(tmpstr));
- BLI_join_dirfile(params->dir, sizeof(params->dir), BKE_appdir_folder_default(), tmpstr);
+ BLI_path_join(
+ params->dir, sizeof(params->dir), BKE_appdir_folder_default_or_root(), tmpstr, NULL);
}
else if (params->dir[0] == '\0')
@@ -2441,7 +2523,7 @@ static void file_expand_directory(bContext *C)
}
}
-/* TODO check we still need this, it's annoying to have OS-specific code here... :/ */
+/* TODO: check we still need this, it's annoying to have OS-specific code here... :/. */
#if defined(WIN32)
static bool can_create_dir(const char *dir)
{
@@ -2507,7 +2589,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN
/* don't do for now because it selects entire text instead of
* placing cursor at the end */
- /* UI_textbutton_activate_but(C, but); */
+ // UI_textbutton_activate_but(C, but);
}
#if defined(WIN32)
else if (!can_create_dir(params->dir)) {
@@ -2611,7 +2693,7 @@ static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
if (params) {
params->flag ^= FILE_HIDE_DOT;
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
@@ -2627,44 +2709,8 @@ void FILE_OT_hidedot(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_hidedot_exec;
- ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Associate File Type Operator (Windows only)
- * \{ */
-
-static int associate_blend_exec(bContext *UNUSED(C), wmOperator *op)
-{
-#ifdef WIN32
- WM_cursor_wait(true);
- if (BLI_windows_register_blend_extension(true)) {
- BKE_report(op->reports, RPT_INFO, "File association registered");
- WM_cursor_wait(false);
- return OPERATOR_FINISHED;
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "Unable to register file association");
- WM_cursor_wait(false);
- return OPERATOR_CANCELLED;
- }
-#else
- BKE_report(op->reports, RPT_WARNING, "Operator Not supported");
- return OPERATOR_CANCELLED;
-#endif
-}
-
-void FILE_OT_associate_blend(struct wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Register File Association";
- ot->description = "Use this installation for .blend files and to display thumbnails";
- ot->idname = "FILE_OT_associate_blend";
-
- /* api callbacks */
- ot->exec = associate_blend_exec;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */
}
/** \} */
@@ -2677,7 +2723,8 @@ static bool file_filenum_poll(bContext *C)
{
SpaceFile *sfile = CTX_wm_space_file(C);
- if (!ED_operator_file_active(C)) {
+ /* File browsing only operator (not asset browsing). */
+ if (!ED_operator_file_browsing_active(C)) {
return false;
}
@@ -2776,20 +2823,6 @@ static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool requ
}
}
-static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
-{
- ScrArea *area = CTX_wm_area(C);
- SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
- FileSelectParams *params = ED_fileselect_get_active_params(sfile);
-
- if (params) {
- file_rename_state_activate(sfile, params->active_file, true);
- ED_area_tag_redraw(area);
- }
-
- return OPERATOR_FINISHED;
-}
-
static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
{
ScrArea *area = CTX_wm_area(C);
@@ -2797,7 +2830,7 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
- file_rename_state_activate(sfile, params->highlight_file, false);
+ file_rename_state_activate(sfile, params->active_file, false);
ED_area_tag_redraw(area);
}
@@ -2812,9 +2845,9 @@ void FILE_OT_rename(struct wmOperatorType *ot)
ot->idname = "FILE_OT_rename";
/* api callbacks */
- ot->invoke = file_rename_invoke;
ot->exec = file_rename_exec;
- ot->poll = ED_operator_file_active;
+ /* File browsing only operator (not asset browsing). */
+ ot->poll = ED_operator_file_browsing_active;
}
/** \} */
@@ -2825,53 +2858,40 @@ void FILE_OT_rename(struct wmOperatorType *ot)
static bool file_delete_poll(bContext *C)
{
- bool poll = ED_operator_file_active(C);
+ if (!ED_operator_file_browsing_active(C)) {
+ return false;
+ }
+
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ if (!sfile || !params) {
+ return false;
+ }
- if (sfile && params) {
- char dir[FILE_MAX_LIBEXTRA];
- int numfiles = filelist_files_ensure(sfile->files);
- int i;
- int num_selected = 0;
+ char dir[FILE_MAX_LIBEXTRA];
+ if (filelist_islibrary(sfile->files, dir, NULL)) {
+ return false;
+ }
- if (filelist_islibrary(sfile->files, dir, NULL)) {
- poll = 0;
- }
- for (i = 0; i < numfiles; i++) {
- if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
- num_selected++;
- }
- }
- if (num_selected <= 0) {
- poll = 0;
+ int numfiles = filelist_files_ensure(sfile->files);
+ for (int i = 0; i < numfiles; i++) {
+ if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
+ /* Has a selected file -> the operator can run. */
+ return true;
}
}
- else {
- poll = 0;
- }
- return poll;
+ return false;
}
static bool file_delete_single(const FileSelectParams *params,
FileDirEntry *file,
const char **r_error_message)
{
- if (file->typeflag & FILE_TYPE_ASSET) {
- ID *id = filelist_file_get_id(file);
- if (!id) {
- *r_error_message = "File is not a local data-block asset.";
- return false;
- }
- ED_asset_clear_id(id);
- }
- else {
- char str[FILE_MAX];
- BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
- if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
- return false;
- }
+ char str[FILE_MAX];
+ BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
+ if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
+ return false;
}
return true;
@@ -2908,7 +2928,7 @@ static int file_delete_exec(bContext *C, wmOperator *op)
}
}
- ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
+ ED_fileselect_clear(wm, sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
return OPERATOR_FINISHED;
@@ -2964,6 +2984,7 @@ void FILE_OT_start_filter(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_start_filter_exec;
+ /* Operator works for file or asset browsing */
ot->poll = ED_operator_file_active;
}
diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c
index 7032d55b331..0e468718a04 100644
--- a/source/blender/editors/space_file/file_panels.c
+++ b/source/blender/editors/space_file/file_panels.c
@@ -47,6 +47,7 @@
#include "WM_types.h"
#include "file_intern.h"
+#include "filelist.h"
#include "fsmenu.h"
#include <string.h>
@@ -57,6 +58,12 @@ static bool file_panel_operator_poll(const bContext *C, PanelType *UNUSED(pt))
return (sfile && sfile->op);
}
+static bool file_panel_asset_browsing_poll(const bContext *C, PanelType *UNUSED(pt))
+{
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ return sfile && sfile->files && ED_fileselect_is_asset_browser(sfile);
+}
+
static void file_panel_operator_header(const bContext *C, Panel *panel)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -222,3 +229,42 @@ void file_execute_region_panels_register(ARegionType *art)
pt->draw = file_panel_execution_buttons_draw;
BLI_addtail(&art->paneltypes, pt);
}
+
+static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel)
+{
+ bScreen *screen = CTX_wm_screen(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ /* May be null if the library wasn't loaded yet. */
+ struct AssetLibrary *asset_library = filelist_asset_library(sfile->files);
+ FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
+ BLI_assert(params != NULL);
+
+ uiLayout *col = uiLayoutColumn(panel->layout, false);
+ uiLayout *row = uiLayoutRow(col, true);
+
+ PointerRNA params_ptr;
+ RNA_pointer_create(&screen->id, &RNA_FileAssetSelectParams, params, &params_ptr);
+
+ uiItemR(row, &params_ptr, "asset_library_ref", 0, "", ICON_NONE);
+ if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) {
+ uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh");
+ }
+
+ uiItemS(col);
+
+ file_create_asset_catalog_tree_view_in_layout(asset_library, col, sfile, params);
+}
+
+void file_tools_region_panels_register(ARegionType *art)
+{
+ PanelType *pt;
+
+ pt = MEM_callocN(sizeof(PanelType), "spacetype file asset catalog buttons");
+ strcpy(pt->idname, "FILE_PT_asset_catalog_buttons");
+ strcpy(pt->label, N_("Asset Catalogs"));
+ strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ pt->flag = PANEL_TYPE_NO_HEADER;
+ pt->poll = file_panel_asset_browsing_poll;
+ pt->draw = file_panel_asset_catalog_buttons_draw;
+ BLI_addtail(&art->paneltypes, pt);
+}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 40a7be0423e..a73fa2b9740 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -37,6 +37,8 @@
#endif
#include "MEM_guardedalloc.h"
+#include "BLF_api.h"
+
#include "BLI_blenlib.h"
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
@@ -48,12 +50,14 @@
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_uuid.h"
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
#include "BKE_asset.h"
+#include "BKE_asset_library.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_icons.h"
@@ -85,6 +89,7 @@
#include "atomic_ops.h"
+#include "file_intern.h"
#include "filelist.h"
#define FILEDIR_NBR_ENTRIES_UNSET -1
@@ -263,8 +268,7 @@ ListBase folder_history_list_duplicate(ListBase *listbase)
typedef struct FileListInternEntry {
struct FileListInternEntry *next, *prev;
- /** ASSET_UUID_LENGTH */
- char uuid[16];
+ FileUID uid;
/** eFileSel_File_Types */
int typeflag;
@@ -306,7 +310,7 @@ typedef struct FileListIntern {
ListBase entries;
FileListInternEntry **filtered;
- char curr_uuid[16]; /* Used to generate uuid during internal listing. */
+ FileUID curr_uid; /* Used to generate UID during internal listing. */
} FileListIntern;
#define FILELIST_ENTRYCACHESIZE_DEFAULT 1024 /* Keep it a power of two! */
@@ -315,7 +319,7 @@ typedef struct FileListEntryCache {
int flags;
- /* This one gathers all entries from both block and misc caches. Used for easy bulk-freing. */
+ /* This one gathers all entries from both block and misc caches. Used for easy bulk-freeing. */
ListBase cached_entries;
/* Block cache: all entries between start and end index.
@@ -324,17 +328,18 @@ typedef struct FileListEntryCache {
int block_start_index, block_end_index, block_center_index, block_cursor;
/* Misc cache: random indices, FIFO behavior.
- * Note: Not 100% sure we actually need that, time will say. */
+ * NOTE: Not 100% sure we actually need that, time will say. */
int misc_cursor;
int *misc_entries_indices;
GHash *misc_entries;
- /* Allows to quickly get a cached entry from its UUID. */
- GHash *uuids;
+ /* Allows to quickly get a cached entry from its UID. */
+ GHash *uids;
/* Previews handling. */
TaskPool *previews_pool;
ThreadQueue *previews_done;
+ size_t previews_todo_count;
} FileListEntryCache;
/* FileListCache.flags */
@@ -366,6 +371,8 @@ typedef struct FileListFilter {
char filter_glob[FILE_MAXFILE];
char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
short flags;
+
+ FileAssetCatalogFilterSettingsHandle *asset_catalog_filter;
} FileListFilter;
/* FileListFilter.flags */
@@ -377,12 +384,14 @@ enum {
FLF_ASSETS_ONLY = 1 << 4,
};
+struct FileListReadJob;
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;
+ AssetLibraryReference *asset_library_ref;
+ struct AssetLibrary *asset_library; /* Non-owning pointer. */
short flags;
@@ -413,11 +422,12 @@ typedef struct FileList {
bool (*check_dir_fn)(struct FileList *, char *, const bool);
/* Fill filelist (to be called by read job). */
- void (*read_job_fn)(
- Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+ void (*read_job_fn)(struct FileListReadJob *, short *, short *, float *);
/* Filter an entry of current filelist. */
bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *);
+ /* Executed before filtering individual items, to set up additional filter data. */
+ void (*prepare_filter_fn)(const struct FileList *, FileListFilter *);
short tags; /* FileListTags */
} FileList;
@@ -425,11 +435,14 @@ typedef struct FileList {
/* FileList.flags */
enum {
FL_FORCE_RESET = 1 << 0,
- FL_IS_READY = 1 << 1,
- FL_IS_PENDING = 1 << 2,
- FL_NEED_SORTING = 1 << 3,
- FL_NEED_FILTERING = 1 << 4,
- FL_SORT_INVERT = 1 << 5,
+ /* Don't do a full reset (unless #FL_FORCE_RESET is also set), only reset files representing main
+ * data (assets from the current file/#Main). */
+ FL_FORCE_RESET_MAIN_FILES = 1 << 1,
+ FL_IS_READY = 1 << 2,
+ FL_IS_PENDING = 1 << 3,
+ FL_NEED_SORTING = 1 << 4,
+ FL_NEED_FILTERING = 1 << 5,
+ FL_SORT_INVERT = 1 << 6,
};
/* FileList.tags */
@@ -457,40 +470,31 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(Main *current_main,
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_main(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
-static void filelist_readjob_lib(Main *current_main,
- FileList *filelist,
- const char *main_name,
+ float *progress);
+static void filelist_readjob_lib(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
-static void filelist_readjob_dir(Main *current_main,
- FileList *filelist,
- const char *main_name,
+ float *progress);
+static void filelist_readjob_dir(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
-static void filelist_readjob_main_assets(Main *current_main,
- FileList *filelist,
- const char *main_name,
+ float *progress);
+static void filelist_readjob_asset_library(struct FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress);
+static void filelist_readjob_main_assets(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
+ float *progress);
/* helper, could probably go in BKE actually? */
static int groupname_to_code(const char *group);
static uint64_t groupname_to_filter_id(const char *group);
-static void filelist_filter_clear(FileList *filelist);
static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size);
/* ********** Sort helpers ********** */
@@ -720,7 +724,7 @@ void filelist_sort(struct FileList *filelist)
sort_cb,
&(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0});
- filelist_filter_clear(filelist);
+ filelist_tag_needs_filtering(filelist);
filelist->flags &= ~FL_NEED_SORTING;
}
}
@@ -813,105 +817,201 @@ static bool is_filtered_hidden(const char *filename,
return false;
}
-static bool is_filtered_file(FileListInternEntry *file,
- const char *UNUSED(root),
- FileListFilter *filter)
+/**
+ * Apply the filter string as file path matching pattern.
+ * \return true when the file should be in the result set, false if it should be filtered out. */
+static bool is_filtered_file_relpath(const FileListInternEntry *file, const FileListFilter *filter)
{
- bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
+ if (filter->filter_search[0] == '\0') {
+ return true;
+ }
- if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
- /* We only check for types if some type are enabled in filtering. */
- if (filter->filter && (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;
- }
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
+ return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0;
+}
+
+/** \return true when the file should be in the result set, false if it should be filtered out. */
+static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter)
+{
+ if (is_filtered_hidden(file->relpath, filter, file)) {
+ return false;
+ }
+
+ if (FILENAME_IS_CURRPAR(file->relpath)) {
+ return false;
+ }
+
+ /* We only check for types if some type are enabled in filtering. */
+ if (filter->filter && (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))) {
+ return false;
}
}
else {
- if (!(file->typeflag & filter->filter)) {
- is_filtered = false;
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
+ return 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;
+ else {
+ if (!(file->typeflag & filter->filter)) {
+ return false;
}
}
}
+ return true;
+}
- return is_filtered;
+/** \return true when the file should be in the result set, false if it should be filtered out. */
+static bool is_filtered_file(FileListInternEntry *file,
+ const char *UNUSED(root),
+ FileListFilter *filter)
+{
+ return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter);
}
-static bool is_filtered_id_file(const FileListInternEntry *file,
- const char *id_group,
- const char *name,
- const FileListFilter *filter)
+static bool is_filtered_id_file_type(const FileListInternEntry *file,
+ const char *id_group,
+ const char *name,
+ const FileListFilter *filter)
{
- 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;
- }
- }
- else {
- if (!(filter->filter & FILE_TYPE_FOLDER)) {
- is_filtered = false;
- }
- }
+ if (!is_filtered_file_type(file, filter)) {
+ return false;
+ }
+
+ /* We only check for types if some type are enabled in filtering. */
+ if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
+ if (id_group) {
+ if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
+ return false;
}
- 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;
- }
- }
+
+ uint64_t filter_id = groupname_to_filter_id(id_group);
+ if (!(filter_id & filter->filter_id)) {
+ return 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 true;
+}
+
+/**
+ * Get the asset metadata of a file, if it represents an asset. This may either be of a local ID
+ * (ID in the current #Main) or read from an external asset library.
+ */
+static AssetMetaData *filelist_file_internal_get_asset_data(const FileListInternEntry *file)
+{
+ const ID *local_id = file->local_data.id;
+ return local_id ? local_id->asset_data : file->imported_asset_data;
+}
+
+static void prepare_filter_asset_library(const FileList *filelist, FileListFilter *filter)
+{
+ /* Not used yet for the asset view template. */
+ if (!filter->asset_catalog_filter) {
+ return;
+ }
+
+ file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library);
+}
+
+/**
+ * Copy a string from source to `dest`, but prefix and suffix it with a single space.
+ * Assumes `dest` has at least space enough for the two spaces.
+ */
+static void tag_copy_with_spaces(char *dest, const char *source, const size_t dest_size)
+{
+ BLI_assert(dest_size > 2);
+ const size_t source_length = BLI_strncpy_rlen(dest + 1, source, dest_size - 2);
+ dest[0] = ' ';
+ dest[source_length + 1] = ' ';
+ dest[source_length + 2] = '\0';
+}
+
+/**
+ * Return whether at least one tag matches the search filter.
+ * Tags are searched as "entire words", so instead of searching for "tag" in the
+ * filter string, this function searches for " tag ". Assumes the search filter
+ * starts and ends with a space.
+ *
+ * Here the tags on the asset are written in set notation:
+ *
+ * `asset_tag_matches_filter(" some tags ", {"some", "blue"})` -> true
+ * `asset_tag_matches_filter(" some tags ", {"som", "tag"})` -> false
+ * `asset_tag_matches_filter(" some tags ", {})` -> false
+ */
+static bool asset_tag_matches_filter(const char *filter_search, const AssetMetaData *asset_data)
+{
+ LISTBASE_FOREACH (const AssetTag *, asset_tag, &asset_data->tags) {
+ char tag_name[MAX_NAME + 2]; /* sizeof(AssetTag::name) + 2 */
+ tag_copy_with_spaces(tag_name, asset_tag->name, sizeof(tag_name));
+ if (BLI_strcasestr(filter_search, tag_name) != NULL) {
+ return true;
}
}
+ return false;
+}
+
+static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter)
+{
+ const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file);
+
+ /* Not used yet for the asset view template. */
+ if (filter->asset_catalog_filter && !file_is_asset_visible_in_catalog_filter_settings(
+ filter->asset_catalog_filter, asset_data)) {
+ return false;
+ }
+
+ if (filter->filter_search[0] == '\0') {
+ /* If there is no filter text, everything matches. */
+ return true;
+ }
- return is_filtered;
+ /* filter->filter_search contains "*the search text*". */
+ char filter_search[66]; /* sizeof(FileListFilter::filter_search) */
+ const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search);
+
+ /* When doing a name comparison, get rid of the leading/trailing asterisks. */
+ filter_search[string_length - 1] = '\0';
+ if (BLI_strcasestr(file->name, filter_search + 1) != NULL) {
+ return true;
+ }
+
+ /* Replace the asterisks with spaces, so that we can do matching on " sometag "; that way
+ * an artist searching for "redder" doesn't result in a match for the tag "red". */
+ filter_search[string_length - 1] = ' ';
+ filter_search[0] = ' ';
+
+ return asset_tag_matches_filter(filter_search, asset_data);
}
-static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+static bool is_filtered_lib_type(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);
+ return is_filtered_id_file_type(file, group, name, filter);
}
+ return is_filtered_file_type(file, filter);
+}
- return is_filtered;
+static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+{
+ return is_filtered_lib_type(file, root, filter) && is_filtered_file_relpath(file, filter);
+}
+
+static bool is_filtered_asset_library(FileListInternEntry *file,
+ const char *root,
+ FileListFilter *filter)
+{
+ return is_filtered_lib_type(file, root, filter) && is_filtered_asset(file, filter);
}
static bool is_filtered_main(FileListInternEntry *file,
@@ -926,10 +1026,11 @@ static bool is_filtered_main_assets(FileListInternEntry *file,
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);
+ return is_filtered_id_file_type(file, file->relpath, file->name, filter) &&
+ is_filtered_asset(file, filter);
}
-static void filelist_filter_clear(FileList *filelist)
+void filelist_tag_needs_filtering(FileList *filelist)
{
filelist->flags |= FL_NEED_FILTERING;
}
@@ -959,6 +1060,10 @@ void filelist_filter(FileList *filelist)
}
}
+ if (filelist->prepare_filter_fn) {
+ filelist->prepare_filter_fn(filelist, &filelist->filter_data);
+ }
+
filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__);
/* Filter remap & count how many files are left after filter in a single loop. */
@@ -1037,7 +1142,29 @@ void filelist_setfilter_options(FileList *filelist,
if (update) {
/* And now, free filtered data so that we know we have to filter again. */
- filelist_filter_clear(filelist);
+ filelist_tag_needs_filtering(filelist);
+ }
+}
+
+/**
+ * \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is
+ * #FILE_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise.
+ */
+void filelist_set_asset_catalog_filter_options(
+ FileList *filelist,
+ eFileSel_Params_AssetCatalogVisibility catalog_visibility,
+ const bUUID *catalog_id)
+{
+ if (!filelist->filter_data.asset_catalog_filter) {
+ /* There's no filter data yet. */
+ filelist->filter_data.asset_catalog_filter = file_create_asset_catalog_filter_settings();
+ }
+
+ const bool needs_update = file_set_asset_catalog_filter_settings(
+ filelist->filter_data.asset_catalog_filter, catalog_visibility, *catalog_id);
+
+ if (needs_update) {
+ filelist_tag_needs_filtering(filelist);
}
}
@@ -1045,13 +1172,13 @@ 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)
+static bool filelist_compare_asset_libraries(const AssetLibraryReference *library_a,
+ const AssetLibraryReference *library_b)
{
if (library_a->type != library_b->type) {
return false;
}
- if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ if (library_a->type == ASSET_LIBRARY_CUSTOM) {
/* Don't only check the index, also check that it's valid. */
bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index(
&U, library_a->custom_library_index);
@@ -1063,28 +1190,28 @@ static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *li
}
/**
- * \param asset_library: May be NULL to unset the library.
+ * \param asset_library_ref: May be NULL to unset the library.
*/
-void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library)
+void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library_ref)
{
/* Unset if needed. */
- if (!asset_library) {
- if (filelist->asset_library) {
- MEM_SAFE_FREE(filelist->asset_library);
+ if (!asset_library_ref) {
+ if (filelist->asset_library_ref) {
+ MEM_SAFE_FREE(filelist->asset_library_ref);
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;
+ if (!filelist->asset_library_ref) {
+ filelist->asset_library_ref = MEM_mallocN(sizeof(*filelist->asset_library_ref),
+ "filelist asset library");
+ *filelist->asset_library_ref = *asset_library_ref;
filelist->flags |= FL_FORCE_RESET;
}
- else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) {
- *filelist->asset_library = *asset_library;
+ else if (!filelist_compare_asset_libraries(filelist->asset_library_ref, asset_library_ref)) {
+ *filelist->asset_library_ref = *asset_library_ref;
filelist->flags |= FL_FORCE_RESET;
}
}
@@ -1154,7 +1281,7 @@ 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)
+ImBuf *filelist_geticon_image_ex(const FileDirEntry *file)
{
ImBuf *ibuf = NULL;
@@ -1308,6 +1435,11 @@ int ED_file_icon(const FileDirEntry *file)
filelist_geticon_ex(file, NULL, false, false);
}
+static bool filelist_intern_entry_is_main_file(const FileListInternEntry *intern_entry)
+{
+ return intern_entry->local_data.id != NULL;
+}
+
/* ********** Main ********** */
static void parent_dir_until_exists_or_default_root(char *dir)
@@ -1370,9 +1502,6 @@ static void filelist_entry_clear(FileDirEntry *entry)
if (entry->name && ((entry->flags & FILE_ENTRY_NAME_FREE) != 0)) {
MEM_freeN(entry->name);
}
- if (entry->description) {
- MEM_freeN(entry->description);
- }
if (entry->relpath) {
MEM_freeN(entry->relpath);
}
@@ -1383,40 +1512,6 @@ static void filelist_entry_clear(FileDirEntry *entry)
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 */
-
- if (!BLI_listbase_is_empty(&entry->variants)) {
- FileDirEntryVariant *var;
-
- for (var = entry->variants.first; var; var = var->next) {
- if (var->name) {
- MEM_freeN(var->name);
- }
- if (var->description) {
- MEM_freeN(var->description);
- }
-
- if (!BLI_listbase_is_empty(&var->revisions)) {
- FileDirEntryRevision *rev;
-
- for (rev = var->revisions.first; rev; rev = rev->next) {
- if (rev->comment) {
- MEM_freeN(rev->comment);
- }
- }
-
- BLI_freelistN(&var->revisions);
- }
- }
-
- /* TODO: tags! */
-
- BLI_freelistN(&entry->variants);
- }
- else if (entry->entry) {
- MEM_freeN(entry->entry);
- }
}
static void filelist_entry_free(FileDirEntry *entry)
@@ -1440,8 +1535,6 @@ static void filelist_direntryarr_free(FileDirEntryArr *array)
#endif
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;
}
static void filelist_intern_entry_free(FileListInternEntry *entry)
@@ -1475,6 +1568,26 @@ static void filelist_intern_free(FileListIntern *filelist_intern)
MEM_SAFE_FREE(filelist_intern->filtered);
}
+/**
+ * \return the number of main files removed.
+ */
+static int filelist_intern_free_main_files(FileListIntern *filelist_intern)
+{
+ int removed_counter = 0;
+ LISTBASE_FOREACH_MUTABLE (FileListInternEntry *, entry, &filelist_intern->entries) {
+ if (!filelist_intern_entry_is_main_file(entry)) {
+ continue;
+ }
+
+ BLI_remlink(&filelist_intern->entries, entry);
+ filelist_intern_entry_free(entry);
+ removed_counter++;
+ }
+
+ MEM_SAFE_FREE(filelist_intern->filtered);
+ return removed_counter;
+}
+
static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata)
{
FileListEntryCache *cache = BLI_task_pool_user_data(pool);
@@ -1489,7 +1602,9 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
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);
+ if (imbuf) {
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
+ }
done = true;
}
}
@@ -1529,6 +1644,7 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
/* 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);
}
// printf("%s: End (%d)...\n", __func__, threadid);
@@ -1555,6 +1671,7 @@ static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
if (!cache->previews_pool) {
cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW);
cache->previews_done = BLI_thread_queue_init();
+ cache->previews_todo_count = 0;
IMB_thumb_locks_acquire();
}
@@ -1588,6 +1705,7 @@ static void filelist_cache_previews_free(FileListEntryCache *cache)
BLI_task_pool_free(cache->previews_pool);
cache->previews_pool = NULL;
cache->previews_done = NULL;
+ cache->previews_todo_count = 0;
IMB_thumb_locks_release();
}
@@ -1634,7 +1752,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
preview->flags = entry->typeflag;
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);
+ // printf("%s: %d - %s\n", __func__, preview->index, preview->path);
filelist_cache_preview_ensure_running(cache);
@@ -1662,13 +1780,14 @@ static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
copy_vn_i(cache->misc_entries_indices, cache_size, -1);
cache->misc_cursor = 0;
- /* XXX This assumes uint is 32 bits and uuid is 128 bits (char[16]), be careful! */
- cache->uuids = BLI_ghash_new_ex(
- BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__, cache_size * 2);
+ cache->uids = BLI_ghash_new_ex(
+ BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__, cache_size * 2);
cache->size = cache_size;
cache->flags = FLC_IS_INIT;
+ cache->previews_todo_count = 0;
+
/* We cannot translate from non-main thread, so init translated strings once from here. */
IMB_thumb_ensure_translations();
}
@@ -1688,7 +1807,7 @@ static void filelist_cache_free(FileListEntryCache *cache)
BLI_ghash_free(cache->misc_entries, NULL, NULL);
MEM_freeN(cache->misc_entries_indices);
- BLI_ghash_free(cache->uuids, NULL, NULL);
+ BLI_ghash_free(cache->uids, NULL, NULL);
for (entry = cache->cached_entries.first; entry; entry = entry_next) {
entry_next = entry->next;
@@ -1721,7 +1840,7 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
}
copy_vn_i(cache->misc_entries_indices, new_size, -1);
- BLI_ghash_clear_ex(cache->uuids, NULL, NULL, new_size * 2);
+ BLI_ghash_clear_ex(cache->uids, NULL, NULL, new_size * 2);
cache->size = new_size;
@@ -1738,8 +1857,7 @@ FileList *filelist_new(short type)
filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT);
- p->selection_state = BLI_ghash_new(
- BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__);
+ p->selection_state = BLI_ghash_new(BLI_ghashutil_inthash_p, BLI_ghashutil_intcmp, __func__);
p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
filelist_settype(p, type);
@@ -1765,9 +1883,17 @@ void filelist_settype(FileList *filelist, short type)
filelist->read_job_fn = filelist_readjob_lib;
filelist->filter_fn = is_filtered_lib;
break;
+ case FILE_ASSET_LIBRARY:
+ filelist->check_dir_fn = filelist_checkdir_lib;
+ filelist->read_job_fn = filelist_readjob_asset_library;
+ filelist->prepare_filter_fn = prepare_filter_asset_library;
+ filelist->filter_fn = is_filtered_asset_library;
+ filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA;
+ break;
case FILE_MAIN_ASSET:
filelist->check_dir_fn = filelist_checkdir_main_assets;
filelist->read_job_fn = filelist_readjob_main_assets;
+ filelist->prepare_filter_fn = prepare_filter_asset_library;
filelist->filter_fn = is_filtered_main_assets;
filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS;
break;
@@ -1781,13 +1907,23 @@ void filelist_settype(FileList *filelist, short type)
filelist->flags |= FL_FORCE_RESET;
}
-void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
+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,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection)
{
if (!filelist) {
return;
}
- filelist_filter_clear(filelist);
+ filelist_tag_needs_filtering(filelist);
if (do_cache) {
filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
@@ -1798,13 +1934,65 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo
filelist_direntryarr_free(&filelist->filelist);
if (do_selection && filelist->selection_state) {
- BLI_ghash_clear(filelist->selection_state, MEM_freeN, NULL);
+ BLI_ghash_clear(filelist->selection_state, NULL, NULL);
+ }
+
+ if (do_asset_library) {
+ filelist_clear_asset_library(filelist);
}
}
-void filelist_clear(struct FileList *filelist)
+static void filelist_clear_main_files(FileList *filelist,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection)
{
- filelist_clear_ex(filelist, true, true);
+ if (!filelist || !(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) {
+ return;
+ }
+
+ filelist_tag_needs_filtering(filelist);
+
+ if (do_cache) {
+ filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size);
+ }
+
+ const int removed_files = filelist_intern_free_main_files(&filelist->filelist_intern);
+
+ filelist->filelist.nbr_entries -= removed_files;
+ filelist->filelist.nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET;
+ BLI_assert(filelist->filelist.nbr_entries > FILEDIR_NBR_ENTRIES_UNSET);
+
+ if (do_selection && filelist->selection_state) {
+ BLI_ghash_clear(filelist->selection_state, NULL, NULL);
+ }
+
+ if (do_asset_library) {
+ filelist_clear_asset_library(filelist);
+ }
+}
+
+void filelist_clear(FileList *filelist)
+{
+ filelist_clear_ex(filelist, true, true, true);
+}
+
+/**
+ * A "smarter" version of #filelist_clear() that calls partial clearing based on the filelist
+ * force-reset flags.
+ */
+void filelist_clear_from_reset_tag(FileList *filelist)
+{
+ /* Do a full clear if needed. */
+ if (filelist->flags & FL_FORCE_RESET) {
+ filelist_clear(filelist);
+ return;
+ }
+
+ if (filelist->flags & FL_FORCE_RESET_MAIN_FILES) {
+ filelist_clear_main_files(filelist, false, true, false);
+ return;
+ }
}
void filelist_free(struct FileList *filelist)
@@ -1815,21 +2003,26 @@ void filelist_free(struct FileList *filelist)
}
/* No need to clear cache & selection_state, we free them anyway. */
- filelist_clear_ex(filelist, false, false);
+ filelist_clear_ex(filelist, true, false, false);
filelist_cache_free(&filelist->filelist_cache);
if (filelist->selection_state) {
- BLI_ghash_free(filelist->selection_state, MEM_freeN, NULL);
+ BLI_ghash_free(filelist->selection_state, NULL, NULL);
filelist->selection_state = NULL;
}
- MEM_SAFE_FREE(filelist->asset_library);
+ MEM_SAFE_FREE(filelist->asset_library_ref);
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
}
+AssetLibrary *filelist_asset_library(FileList *filelist)
+{
+ return filelist->asset_library;
+}
+
void filelist_freelib(struct FileList *filelist)
{
if (filelist->libfiledata) {
@@ -1843,13 +2036,23 @@ BlendHandle *filelist_lib(struct FileList *filelist)
return filelist->libfiledata;
}
-static const char *fileentry_uiname(const char *root,
- const char *relpath,
- const eFileSel_File_Types typeflag,
- char *buff)
+static char *fileentry_uiname(const char *root,
+ const char *relpath,
+ const eFileSel_File_Types typeflag,
+ char *buff)
{
char *name = NULL;
+ if (typeflag & FILE_TYPE_FTFONT && !(typeflag & FILE_TYPE_BLENDERLIB)) {
+ char abspath[FILE_MAX_LIBEXTRA];
+ BLI_join_dirfile(abspath, sizeof(abspath), root, relpath);
+ name = BLF_display_name_from_file(abspath);
+ if (name) {
+ /* Allocated string, so no need to BLI_strdup.*/
+ return name;
+ }
+ }
+
if (typeflag & FILE_TYPE_BLENDERLIB) {
char abspath[FILE_MAX_LIBEXTRA];
char *group;
@@ -1871,7 +2074,7 @@ static const char *fileentry_uiname(const char *root,
}
BLI_assert(name);
- return name;
+ return BLI_strdup(name);
}
const char *filelist_dir(struct FileList *filelist)
@@ -1889,7 +2092,7 @@ 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;
+ const bool allow_invalid = filelist->asset_library_ref != NULL;
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
@@ -1913,7 +2116,7 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
bool filelist_needs_force_reset(FileList *filelist)
{
- return (filelist->flags & FL_FORCE_RESET) != 0;
+ return (filelist->flags & (FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES)) != 0;
}
void filelist_tag_force_reset(FileList *filelist)
@@ -1921,6 +2124,14 @@ void filelist_tag_force_reset(FileList *filelist)
filelist->flags |= FL_FORCE_RESET;
}
+void filelist_tag_force_reset_mainfiles(FileList *filelist)
+{
+ if (!(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) {
+ return;
+ }
+ filelist->flags |= FL_FORCE_RESET_MAIN_FILES;
+}
+
bool filelist_is_ready(struct FileList *filelist)
{
return (filelist->flags & FL_IS_READY) != 0;
@@ -1957,16 +2168,12 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
FileListInternEntry *entry = filelist->filelist_intern.filtered[index];
FileListEntryCache *cache = &filelist->filelist_cache;
FileDirEntry *ret;
- FileDirEntryRevision *rev;
ret = MEM_callocN(sizeof(*ret), __func__);
- rev = MEM_callocN(sizeof(*rev), __func__);
- rev->size = (uint64_t)entry->st.st_size;
+ ret->size = (uint64_t)entry->st.st_size;
+ ret->time = (int64_t)entry->st.st_mtime;
- rev->time = (int64_t)entry->st.st_mtime;
-
- ret->entry = rev;
ret->relpath = BLI_strdup(entry->relpath);
if (entry->free_name) {
ret->name = BLI_strdup(entry->name);
@@ -1975,8 +2182,7 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
else {
ret->name = entry->name;
}
- ret->description = BLI_strdupcat(filelist->filelist.root, entry->relpath);
- memcpy(ret->uuid, entry->uuid, sizeof(ret->uuid));
+ ret->uid = entry->uid;
ret->blentype = entry->blentype;
ret->typeflag = entry->typeflag;
ret->attributes = entry->attributes;
@@ -1992,7 +2198,9 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
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);
+ if (ibuf) {
+ ret->preview_icon_id = BKE_icon_imbuf_create(ibuf);
+ }
}
BLI_addtail(&cache->cached_entries, ret);
return ret;
@@ -2034,11 +2242,11 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const
ret = filelist_file_create_entry(filelist, index);
old_index = cache->misc_entries_indices[cache->misc_cursor];
if ((old = BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(old_index), NULL))) {
- BLI_ghash_remove(cache->uuids, old->uuid, NULL, NULL);
+ BLI_ghash_remove(cache->uids, POINTER_FROM_UINT(old->uid), NULL, NULL);
filelist_file_release_entry(filelist, old);
}
BLI_ghash_insert(cache->misc_entries, POINTER_FROM_INT(index), ret);
- BLI_ghash_insert(cache->uuids, ret->uuid, ret);
+ BLI_ghash_insert(cache->uids, POINTER_FROM_UINT(ret->uid), ret);
cache->misc_entries_indices[cache->misc_cursor] = index;
cache->misc_cursor = (cache->misc_cursor + 1) % cache_size;
@@ -2057,19 +2265,21 @@ FileDirEntry *filelist_file(struct FileList *filelist, int index)
return filelist_file_ex(filelist, index, true);
}
-int filelist_file_findpath(struct FileList *filelist, const char *filename)
+/**
+ * Find a file from a file name, or more precisely, its file-list relative path, inside the
+ * filtered items. \return The index of the found file or -1.
+ */
+int filelist_file_find_path(struct FileList *filelist, const char *filename)
{
- int fidx = -1;
-
if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
- return fidx;
+ return -1;
}
- /* XXX TODO Cache could probably use a ghash on paths too? Not really urgent though.
- * This is only used to find again renamed entry,
- * annoying but looks hairy to get rid of it currently. */
+ /* XXX TODO: Cache could probably use a ghash on paths too? Not really urgent though.
+ * This is only used to find again renamed entry,
+ * annoying but looks hairy to get rid of it currently. */
- for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+ for (int fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
if (STREQ(entry->relpath, filename)) {
return fidx;
@@ -2080,38 +2290,53 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
}
/**
- * Get the ID a file represents (if any). For #FILE_MAIN, #FILE_MAIN_ASSET.
+ * Find a file representing \a id.
+ * \return The index of the found file or -1.
*/
-ID *filelist_file_get_id(const FileDirEntry *file)
-{
- return file->id;
-}
-
-FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4])
+int filelist_file_find_id(const FileList *filelist, const ID *id)
{
if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
- return NULL;
+ return -1;
}
- if (filelist->filelist_cache.uuids) {
- FileDirEntry *entry = BLI_ghash_lookup(filelist->filelist_cache.uuids, uuid);
- if (entry) {
- return entry;
+ for (int fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
+ FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
+ if (entry->local_data.id == id) {
+ return fidx;
}
}
- {
- int fidx;
+ return -1;
+}
- for (fidx = 0; fidx < filelist->filelist.nbr_entries_filtered; fidx++) {
- FileListInternEntry *entry = filelist->filelist_intern.filtered[fidx];
- if (memcmp(entry->uuid, uuid, sizeof(entry->uuid)) == 0) {
- return filelist_file(filelist, fidx);
- }
- }
- }
+/**
+ * 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;
+}
- return NULL;
+#define FILE_UID_UNSET 0
+
+static FileUID filelist_uid_generate(FileList *filelist)
+{
+ /* Using an atomic operation to avoid having to lock thread...
+ * Note that we do not really need this here currently, since there is a single listing thread,
+ * but better remain consistent about threading! */
+ return atomic_add_and_fetch_uint32(&filelist->filelist_intern.curr_uid, 1);
+}
+
+bool filelist_uid_is_set(const FileUID uid)
+{
+ FileUID unset_uid;
+ filelist_uid_unset(&unset_uid);
+ return unset_uid != uid;
+}
+
+void filelist_uid_unset(FileUID *r_uid)
+{
+ *r_uid = FILE_UID_UNSET;
}
void filelist_file_cache_slidingwindow_set(FileList *filelist, size_t window_size)
@@ -2147,7 +2372,7 @@ static bool filelist_file_cache_block_create(FileList *filelist,
/* That entry might have already been requested and stored in misc cache... */
if ((entry = BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(idx), NULL)) == NULL) {
entry = filelist_file_create_entry(filelist, idx);
- BLI_ghash_insert(cache->uuids, entry->uuid, entry);
+ BLI_ghash_insert(cache->uids, POINTER_FROM_UINT(entry->uid), entry);
}
cache->block_entries[cursor] = entry;
}
@@ -2173,7 +2398,7 @@ static void filelist_file_cache_block_release(struct FileList *filelist,
__func__,
cursor /*, cache->block_entries[cursor], cache->block_entries[cursor]->relpath*/);
#endif
- BLI_ghash_remove(cache->uuids, entry->uuid, NULL, NULL);
+ BLI_ghash_remove(cache->uids, POINTER_FROM_UINT(entry->uid), NULL, NULL);
filelist_file_release_entry(filelist, entry);
#ifndef NDEBUG
cache->block_entries[cursor] = NULL;
@@ -2305,7 +2530,7 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
if (start_index < cache->block_start_index) {
/* Add (request) needed entries before already cached ones. */
- /* Note: We need some index black magic to wrap around (cycle)
+ /* NOTE: We need some index black magic to wrap around (cycle)
* inside our cache_size array... */
int size1 = cache->block_start_index - start_index;
int size2 = 0;
@@ -2337,7 +2562,7 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
// printf("\tstart-extended...\n");
if (end_index > cache->block_end_index) {
/* Add (request) needed entries after already cached ones. */
- /* Note: We need some index black magic to wrap around (cycle)
+ /* NOTE: We need some index black magic to wrap around (cycle)
* inside our cache_size array... */
int size1 = end_index - cache->block_end_index;
int size2 = 0;
@@ -2408,7 +2633,8 @@ void filelist_cache_previews_set(FileList *filelist, const bool use_previews)
if (use_previews && (filelist->flags & FL_IS_READY)) {
cache->flags |= FLC_PREVIEWS_ACTIVE;
- BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL));
+ BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL) &&
+ (cache->previews_todo_count == 0));
// printf("%s: Init Previews...\n", __func__);
@@ -2481,6 +2707,18 @@ bool filelist_cache_previews_running(FileList *filelist)
return (cache->previews_pool != NULL);
}
+bool filelist_cache_previews_done(FileList *filelist)
+{
+ FileListEntryCache *cache = &filelist->filelist_cache;
+ if ((cache->flags & FLC_PREVIEWS_ACTIVE) == 0) {
+ /* There are no previews. */
+ return false;
+ }
+
+ return (cache->previews_pool == NULL) || (cache->previews_done == NULL) ||
+ (cache->previews_todo_count == (size_t)BLI_thread_queue_len(cache->previews_done));
+}
+
/* would recognize .blend as well */
static bool file_is_blend_backup(const char *str)
{
@@ -2623,9 +2861,10 @@ int ED_file_extension_icon(const char *path)
}
}
-int filelist_needs_reading(struct FileList *filelist)
+int filelist_needs_reading(FileList *filelist)
{
- return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET);
+ return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET) ||
+ filelist_needs_force_reset(filelist);
}
uint filelist_entry_select_set(const FileList *filelist,
@@ -2635,7 +2874,7 @@ uint filelist_entry_select_set(const FileList *filelist,
FileCheckType check)
{
/* Default NULL pointer if not found is fine here! */
- void **es_p = BLI_ghash_lookup_p(filelist->selection_state, entry->uuid);
+ void **es_p = BLI_ghash_lookup_p(filelist->selection_state, POINTER_FROM_UINT(entry->uid));
uint entry_flag = es_p ? POINTER_AS_UINT(*es_p) : 0;
const uint org_entry_flag = entry_flag;
@@ -2663,13 +2902,12 @@ uint filelist_entry_select_set(const FileList *filelist,
*es_p = POINTER_FROM_UINT(entry_flag);
}
else {
- BLI_ghash_remove(filelist->selection_state, entry->uuid, MEM_freeN, NULL);
+ BLI_ghash_remove(filelist->selection_state, POINTER_FROM_UINT(entry->uid), NULL, NULL);
}
}
else if (entry_flag) {
- void *key = MEM_mallocN(sizeof(entry->uuid), __func__);
- memcpy(key, entry->uuid, sizeof(entry->uuid));
- BLI_ghash_insert(filelist->selection_state, key, POINTER_FROM_UINT(entry_flag));
+ BLI_ghash_insert(
+ filelist->selection_state, POINTER_FROM_UINT(entry->uid), POINTER_FROM_UINT(entry_flag));
}
}
@@ -2707,7 +2945,8 @@ uint filelist_entry_select_get(FileList *filelist, FileDirEntry *entry, FileChec
if (((check == CHECK_ALL)) || ((check == CHECK_DIRS) && (entry->typeflag & FILE_TYPE_DIR)) ||
((check == CHECK_FILES) && !(entry->typeflag & FILE_TYPE_DIR))) {
/* Default NULL pointer if not found is fine here! */
- return POINTER_AS_UINT(BLI_ghash_lookup(filelist->selection_state, entry->uuid));
+ return POINTER_AS_UINT(
+ BLI_ghash_lookup(filelist->selection_state, POINTER_FROM_UINT(entry->uid)));
}
return 0;
@@ -2732,7 +2971,7 @@ bool filelist_entry_is_selected(FileList *filelist, const int index)
/* BLI_ghash_lookup returns NULL if not found, which gets mapped to 0, which gets mapped to
* "not selected". */
const uint selection_state = POINTER_AS_UINT(
- BLI_ghash_lookup(filelist->selection_state, intern_entry->uuid));
+ BLI_ghash_lookup(filelist->selection_state, POINTER_FROM_UINT(intern_entry->uid)));
return selection_state != 0;
}
@@ -2884,76 +3123,129 @@ static int filelist_readjob_list_dir(const char *root,
return nbr_entries;
}
-static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
+typedef enum ListLibOptions {
+ /* Will read both the groups + actual ids from the library. Reduces the amount of times that
+ * a library needs to be opened. */
+ LIST_LIB_RECURSIVE = (1 << 0),
+
+ /* Will only list assets. */
+ LIST_LIB_ASSETS_ONLY = (1 << 1),
+
+ /* Add given root as result. */
+ LIST_LIB_ADD_PARENT = (1 << 2),
+} ListLibOptions;
+
+static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode,
+ const char *group_name)
+{
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(group_name);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR;
+ entry->blentype = idcode;
+ return entry;
+}
+
+static void filelist_readjob_list_lib_add_datablocks(ListBase *entries,
+ LinkNode *datablock_infos,
+ const bool prefix_relpath_with_group_name,
+ const int idcode,
+ 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;
+ BLI_addtail(entries, entry);
+ }
+}
+
+static int filelist_readjob_list_lib(const char *root,
+ ListBase *entries,
+ const ListLibOptions options)
{
- FileListInternEntry *entry;
- LinkNode *ln, *names = NULL, *datablock_infos = NULL;
- int i, nitems, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX_LIBEXTRA], *group;
- bool ok;
struct BlendHandle *libfiledata = NULL;
- /* name test */
- ok = BLO_library_path_explode(root, dir, &group, NULL);
- if (!ok) {
- return nbr_entries;
+ /* 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
+ * 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;
}
- /* there we go */
+ /* Open the library file. */
BlendFileReadReport bf_reports = {.reports = NULL};
libfiledata = BLO_blendhandle_from_file(dir, &bf_reports);
if (libfiledata == NULL) {
- return nbr_entries;
- }
-
- /* memory for strings is passed into filelist[i].entry->relpath
- * and freed in filelist_entry_free. */
- if (group) {
- idcode = groupname_to_code(group);
- datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems);
- }
- else {
- names = BLO_blendhandle_get_linkable_groups(libfiledata);
- nitems = BLI_linklist_count(names);
+ return 0;
}
- BLO_blendhandle_close(libfiledata);
-
- if (!skip_currpar) {
- entry = MEM_callocN(sizeof(*entry), __func__);
+ /* Add current parent when requested. */
+ int 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);
BLI_addtail(entries, entry);
- nbr_entries++;
+ parent_len = 1;
}
- 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);
- }
- else {
- entry->blentype = idcode;
+ 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(
+ libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len);
+ filelist_readjob_list_lib_add_datablocks(entries, datablock_infos, false, idcode, group);
+ BLI_linklist_freeN(datablock_infos);
+ }
+ else {
+ LinkNode *groups = BLO_blendhandle_get_linkable_groups(libfiledata);
+ group_len = BLI_linklist_count(groups);
+
+ for (LinkNode *ln = groups; ln; ln = ln->next) {
+ const char *group_name = ln->link;
+ const int idcode = groupname_to_code(group_name);
+ FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode,
+ group_name);
+ BLI_addtail(entries, group_entry);
+
+ if (options & LIST_LIB_RECURSIVE) {
+ int group_datablock_len;
+ LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info(
+ libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len);
+ filelist_readjob_list_lib_add_datablocks(
+ entries, group_datablock_infos, true, idcode, group_name);
+ BLI_linklist_freeN(group_datablock_infos);
+ datablock_len += group_datablock_len;
+ }
}
- BLI_addtail(entries, entry);
- nbr_entries++;
+
+ BLI_linklist_freeN(groups);
}
- BLI_linklist_freeN(datablock_infos ? datablock_infos : names);
+ BLO_blendhandle_close(libfiledata);
- return nbr_entries;
+ /* Return the number of items added to entries. */
+ int added_entries_len = group_len + datablock_len + parent_len;
+ return added_entries_len;
}
#if 0
@@ -2967,7 +3259,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
ListBase *lb;
int a, fake, idcode, ok, totlib, totbl;
- // filelist->type = FILE_MAIN; /* XXX TODO: add modes to filebrowser */
+ // filelist->type = FILE_MAIN; /* XXX TODO: add modes to file-browser */
BLI_assert(filelist->filelist.entries == NULL);
@@ -3064,7 +3356,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
ok = 1;
if (ok) {
if (!(filelist->filter_data.flags & FLF_HIDE_DOT) || id->name[2] != '.') {
- if (id->lib == NULL) {
+ if (!ID_IS_LINKED(id)) {
files->entry->relpath = BLI_strdup(id->name + 2);
}
else {
@@ -3073,7 +3365,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
files->entry->relpath = BLI_strdup(relname);
}
// files->type |= S_IFREG;
-# if 0 /* XXX TODO show the selection status of the objects */
+# if 0 /* XXX TODO: show the selection status of the objects. */
if (!filelist->has_func) { /* F4 DATA BROWSE */
if (idcode == ID_OB) {
if ( ((Object *)id)->flag & SELECT) {
@@ -3130,14 +3422,91 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
}
#endif
-static void filelist_readjob_do(const bool do_lib,
- FileList *filelist,
- const char *main_name,
- const short *stop,
- short *do_update,
- float *progress,
- ThreadMutex *lock)
+typedef struct FileListReadJob {
+ ThreadMutex lock;
+ char main_name[FILE_MAX];
+ Main *current_main;
+ struct FileList *filelist;
+ /** Set to request a partial read that only adds files representing #Main data (IDs). Used when
+ * #Main may have received changes of interest (e.g. asset removed or renamed). */
+ bool only_main_data;
+
+ /** Shallow copy of #filelist for thread-safe access.
+ *
+ * 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.
+ *
+ * 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;
+
+static void filelist_readjob_append_entries(FileListReadJob *job_params,
+ ListBase *from_entries,
+ int nbr_from_entries,
+ short *do_update)
+{
+ BLI_assert(BLI_listbase_count(from_entries) == nbr_from_entries);
+ if (nbr_from_entries <= 0) {
+ *do_update = false;
+ return;
+ }
+
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
+ BLI_mutex_lock(&job_params->lock);
+ BLI_movelisttolist(&filelist->filelist.entries, from_entries);
+ filelist->filelist.nbr_entries += nbr_from_entries;
+ BLI_mutex_unlock(&job_params->lock);
+
+ *do_update = true;
+}
+
+static bool filelist_readjob_should_recurse_into_entry(const int max_recursion,
+ const bool is_lib,
+ const int current_recursion_level,
+ FileListInternEntry *entry)
{
+ if (max_recursion == 0) {
+ /* Recursive loading is disabled. */
+ return false;
+ }
+ if (!is_lib && current_recursion_level > 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`. */
+ if (!is_lib && (current_recursion_level >= max_recursion) &&
+ ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) {
+ return false;
+ }
+ if (entry->typeflag & FILE_TYPE_BLENDERLIB) {
+ /* Libraries are already loaded recursively when recursive loaded is used. No need to add
+ * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */
+ return false;
+ }
+ if (!(entry->typeflag & FILE_TYPE_DIR)) {
+ /* Cannot recurse into regular file entries. */
+ return false;
+ }
+ if (FILENAME_IS_CURRPAR(entry->relpath)) {
+ /* Don't schedule go to parent entry, (`..`) */
+ return false;
+ }
+
+ return true;
+}
+
+static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
+ FileListReadJob *job_params,
+ const short *stop,
+ short *do_update,
+ float *progress)
+{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
ListBase entries = {0};
BLI_Stack *todo_dirs;
TodoDir *td_dir;
@@ -3147,13 +3516,6 @@ static void filelist_readjob_do(const bool do_lib,
const int max_recursion = filelist->max_recursion;
int nbr_done_dirs = 0, nbr_todo_dirs = 1;
- // BLI_assert(filelist->filtered == NULL);
- 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;
-
todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
td_dir = BLI_stack_push_r(todo_dirs);
td_dir->level = 1;
@@ -3161,13 +3523,12 @@ static void filelist_readjob_do(const bool do_lib,
BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
- BLI_path_normalize_dir(main_name, dir);
+ BLI_path_normalize_dir(job_params->main_name, dir);
td_dir->dir = BLI_strdup(dir);
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
FileListInternEntry *entry;
int nbr_entries = 0;
- bool is_lib = do_lib;
char *subdir;
char rel_subdir[FILE_MAX_LIBEXTRA];
@@ -3190,65 +3551,59 @@ static void filelist_readjob_do(const bool do_lib,
BLI_path_normalize_dir(root, rel_subdir);
BLI_path_rel(rel_subdir, root);
+ bool is_lib = false;
if (do_lib) {
- nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar);
+ ListLibOptions list_lib_options = 0;
+ if (!skip_currpar) {
+ list_lib_options |= LIST_LIB_ADD_PARENT;
+ }
+
+ /* Libraries are loaded recursively when max_recursion is set. It doesn't check if there is
+ * still a recursion level over. */
+ if (max_recursion > 0) {
+ list_lib_options |= LIST_LIB_RECURSIVE;
+ }
+ /* Only load assets when browsing an asset library. For normal file browsing we return all
+ * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user.*/
+ if (filelist->asset_library_ref) {
+ list_lib_options |= LIST_LIB_ASSETS_ONLY;
+ }
+ nbr_entries = filelist_readjob_list_lib(subdir, &entries, list_lib_options);
+ if (nbr_entries > 0) {
+ is_lib = true;
+ }
}
- if (!nbr_entries) {
- is_lib = false;
+
+ if (!is_lib) {
nbr_entries = filelist_readjob_list_dir(
- subdir, &entries, filter_glob, do_lib, main_name, skip_currpar);
+ subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
}
for (entry = entries.first; entry; entry = entry->next) {
- BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
-
- /* Generate our entry uuid. Abusing uuid as an uint32, shall be more than enough here,
- * things would crash way before we overflow that counter!
- * Using an atomic operation to avoid having to lock thread...
- * Note that we do not really need this here currently,
- * since there is a single listing thread, but better
- * remain consistent about threading! */
- *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32(
- (uint32_t *)filelist->filelist_intern.curr_uuid, 1);
+ entry->uid = filelist_uid_generate(filelist);
- /* Only thing we change in direntry here, so we need to free it first. */
+ /* When loading entries recursive, the rel_path should be relative from the root dir.
+ * we combine the relative path to the subdir with the relative path of the entry. */
+ BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
MEM_freeN(entry->relpath);
entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//'
* added by BLI_path_rel to rel_subdir. */
- entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir));
+ entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir);
entry->free_name = true;
- /* Here we decide whether current filedirentry is to be listed too, or not. */
- if (max_recursion && (is_lib || (recursion_level <= max_recursion))) {
- if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) {
- /* Skip... */
- }
- else if (!is_lib && (recursion_level >= max_recursion) &&
- ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) {
- /* Do not recurse in real directories in this case, only in .blend libs. */
- }
- else {
- /* We have a directory we want to list, add it to todo list! */
- BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
- BLI_path_normalize_dir(main_name, dir);
- td_dir = BLI_stack_push_r(todo_dirs);
- td_dir->level = recursion_level + 1;
- td_dir->dir = BLI_strdup(dir);
- nbr_todo_dirs++;
- }
+ if (filelist_readjob_should_recurse_into_entry(
+ max_recursion, is_lib, recursion_level, entry)) {
+ /* We have a directory we want to list, add it to todo list! */
+ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
+ BLI_path_normalize_dir(job_params->main_name, dir);
+ td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir->level = recursion_level + 1;
+ td_dir->dir = BLI_strdup(dir);
+ nbr_todo_dirs++;
}
}
- if (nbr_entries) {
- BLI_mutex_lock(lock);
-
- *do_update = true;
-
- BLI_movelisttolist(&filelist->filelist.entries, &entries);
- filelist->filelist.nbr_entries += nbr_entries;
-
- BLI_mutex_unlock(lock);
- }
+ filelist_readjob_append_entries(job_params, &entries, nbr_entries, do_update);
nbr_done_dirs++;
*progress = (float)nbr_done_dirs / (float)nbr_todo_dirs;
@@ -3265,63 +3620,97 @@ static void filelist_readjob_do(const bool do_lib,
BLI_stack_free(todo_dirs);
}
-static void filelist_readjob_dir(Main *UNUSED(current_main),
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_do(const bool do_lib,
+ FileListReadJob *job_params,
+ const short *stop,
+ short *do_update,
+ float *progress)
+{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
+
+ // BLI_assert(filelist->filtered == NULL);
+ 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;
+
+ filelist_readjob_recursive_dir_add_items(do_lib, job_params, stop, do_update, progress);
+}
+
+static void filelist_readjob_dir(FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock)
+ float *progress)
{
- filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_do(false, job_params, stop, do_update, progress);
}
-static void filelist_readjob_lib(Main *UNUSED(current_main),
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_lib(FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock)
+ float *progress)
{
- filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_do(true, job_params, stop, do_update, progress);
}
-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_asset_library_path(const FileListReadJob *job_params,
+ char r_library_root_path[FILE_MAX])
{
- /* TODO! */
- filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock);
+ if (job_params->filelist->type == FILE_MAIN_ASSET) {
+ /* For the "Current File" library (#FILE_MAIN_ASSET) we get the asset library root path based
+ * on main. */
+ BKE_asset_library_find_suitable_root_path_from_main(job_params->current_main,
+ r_library_root_path);
+ }
+ else {
+ BLI_strncpy(r_library_root_path, job_params->tmp_filelist->filelist.root, FILE_MAX);
+ }
}
/**
- * \warning Acts on main, so NOT thread-safe!
+ * Load asset library data, which currently means loading the asset catalogs for the library.
*/
-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))
+static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, short *do_update)
{
- BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+ FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
- /* A valid, but empty directory from now. */
- filelist->filelist.nbr_entries = 0;
+ *do_update = false;
+
+ if (job_params->filelist->asset_library_ref == NULL) {
+ return;
+ }
+ if (tmp_filelist->asset_library != NULL) {
+ /* Asset library already loaded. */
+ return;
+ }
+
+ char library_root_path[FILE_MAX];
+ filelist_asset_library_path(job_params, library_root_path);
+
+ /* Load asset catalogs, into the temp filelist for thread-safety.
+ * #filelist_readjob_endjob() will move it into the real filelist. */
+ tmp_filelist->asset_library = BKE_asset_library_load(library_root_path);
+ *do_update = true;
+}
+
+static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params,
+ short *UNUSED(stop),
+ short *do_update,
+ float *UNUSED(progress))
+{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
FileListInternEntry *entry;
ListBase tmp_entries = {0};
ID *id_iter;
int nbr_entries = 0;
- FOREACH_MAIN_ID_BEGIN (current_main, id_iter) {
+ /* Make sure no IDs are added/removed/reallocated in the main thread while this is running in
+ * parallel. */
+ BKE_main_lock(job_params->current_main);
+
+ FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) {
if (!id_iter->asset_data) {
continue;
}
@@ -3334,8 +3723,7 @@ static void filelist_readjob_main_assets(Main *current_main,
entry->free_name = false;
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->uid = filelist_uid_generate(filelist);
entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data,
id_iter);
entry->local_data.id = id_iter;
@@ -3344,25 +3732,93 @@ static void filelist_readjob_main_assets(Main *current_main,
}
FOREACH_MAIN_ID_END;
+ BKE_main_unlock(job_params->current_main);
+
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;
+ filelist->filelist.nbr_entries_filtered = -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;
-} FileListReadJob;
+/**
+ * Check if \a bmain is stored within the root path of \a filelist. This means either directly or
+ * in some nested directory. In other words, it checks if the \a filelist root path is contained in
+ * the path to \a bmain.
+ * This is irrespective of the recursion level displayed, it basically assumes unlimited recursion
+ * levels.
+ */
+static bool filelist_contains_main(const FileList *filelist, const Main *bmain)
+{
+ const char *main_path = BKE_main_blendfile_path(bmain);
+ return main_path[0] && BLI_path_contains(filelist->filelist.root, main_path);
+}
+
+static void filelist_readjob_asset_library(FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress)
+{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
+
+ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty file-list from now. */
+ filelist->filelist.nbr_entries = 0;
+
+ /* NOP if already read. */
+ filelist_readjob_load_asset_library_data(job_params, do_update);
+
+ if (filelist_contains_main(filelist, job_params->current_main)) {
+ filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
+ }
+ if (!job_params->only_main_data) {
+ filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress);
+ }
+}
+
+static void filelist_readjob_main(FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress)
+{
+ /* TODO! */
+ filelist_readjob_dir(job_params, stop, do_update, progress);
+}
+
+static void filelist_readjob_main_assets(FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress)
+{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
+ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ filelist_readjob_load_asset_library_data(job_params, do_update);
+
+ /* A valid, but empty file-list from now. */
+ filelist->filelist.nbr_entries = 0;
+
+ filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
+}
+/**
+ * Check if the read-job is requesting a partial reread of the file list only.
+ */
+static bool filelist_readjob_is_partial_read(const FileListReadJob *read_job)
+{
+ return read_job->only_main_data;
+}
+
+/**
+ * \note This may trigger partial filelist reading. If the #FL_FORCE_RESET_MAIN_FILES flag is set,
+ * some current entries are kept and we just call the readjob to update the main files (see
+ * #FileListReadJob.only_main_data).
+ */
static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress)
{
FileListReadJob *flrj = flrjv;
@@ -3381,26 +3837,29 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist->filelist_intern.filtered = NULL;
BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
- memset(flrj->tmp_filelist->filelist_intern.curr_uuid,
- 0,
- sizeof(flrj->tmp_filelist->filelist_intern.curr_uuid));
+ if (filelist_readjob_is_partial_read(flrj)) {
+ /* Don't unset the current UID on partial read, would give duplicates otherwise. */
+ }
+ else {
+ filelist_uid_unset(&flrj->tmp_filelist->filelist_intern.curr_uid);
+ }
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;
+ flrj->tmp_filelist->asset_library_ref = NULL;
+ flrj->tmp_filelist->filter_data.asset_catalog_filter = NULL;
BLI_mutex_unlock(&flrj->lock);
- flrj->tmp_filelist->read_job_fn(flrj->current_main,
- flrj->tmp_filelist,
- flrj->main_name,
- stop,
- do_update,
- progress,
- &flrj->lock);
+ flrj->tmp_filelist->read_job_fn(flrj, stop, do_update, progress);
}
+/**
+ * \note This may update for a partial filelist reading job. If the #FL_FORCE_RESET_MAIN_FILES flag
+ * is set, some current entries are kept and we just call the readjob to update the main
+ * files (see #FileListReadJob.only_main_data).
+ */
static void filelist_readjob_update(void *flrjv)
{
FileListReadJob *flrj = flrjv;
@@ -3420,11 +3879,21 @@ static void filelist_readjob_update(void *flrjv)
flrj->tmp_filelist->filelist.nbr_entries = 0;
}
+ if (flrj->tmp_filelist->asset_library) {
+ flrj->filelist->asset_library = flrj->tmp_filelist->asset_library;
+ }
+
+ /* Important for partial reads: Copy increased UID counter back to the real list. */
+ if (flrj->tmp_filelist->filelist_intern.curr_uid > fl_intern->curr_uid) {
+ fl_intern->curr_uid = flrj->tmp_filelist->filelist_intern.curr_uid;
+ }
+
BLI_mutex_unlock(&flrj->lock);
if (new_nbr_entries) {
- /* Do not clear selection cache, we can assume already 'selected' uuids are still valid! */
- filelist_clear_ex(flrj->filelist, true, false);
+ /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep
+ * the asset library data we just read. */
+ filelist_clear_ex(flrj->filelist, false, true, false);
flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING);
}
@@ -3466,7 +3935,7 @@ static void filelist_readjob_free(void *flrjv)
MEM_freeN(flrj);
}
-void filelist_readjob_start(FileList *filelist, const bContext *C)
+void filelist_readjob_start(FileList *filelist, const int space_notifier, const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmJob *wm_job;
@@ -3481,8 +3950,11 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
flrj->filelist = filelist;
flrj->current_main = bmain;
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
+ if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET)) {
+ flrj->only_main_data = true;
+ }
- filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
+ filelist->flags &= ~(FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES | FL_IS_READY);
filelist->flags |= FL_IS_PENDING;
/* Init even for single threaded execution. Called functions use it. */
@@ -3498,22 +3970,19 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
filelist_readjob_endjob(flrj);
filelist_readjob_free(flrj);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL);
+ WM_event_add_notifier(C, space_notifier | NA_JOB_FINISHED, NULL);
return;
}
/* setup job */
wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
- CTX_data_scene(C),
+ filelist,
"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 | NA_JOB_FINISHED);
+ WM_jobs_timer(wm_job, 0.01, space_notifier, space_notifier | NA_JOB_FINISHED);
WM_jobs_callbacks(
wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob);
@@ -3521,12 +3990,12 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
-void filelist_readjob_stop(wmWindowManager *wm, Scene *owner_scene)
+void filelist_readjob_stop(FileList *filelist, wmWindowManager *wm)
{
- WM_jobs_kill_type(wm, owner_scene, WM_JOB_TYPE_FILESEL_READDIR);
+ WM_jobs_kill_type(wm, filelist, WM_JOB_TYPE_FILESEL_READDIR);
}
-int filelist_readjob_running(wmWindowManager *wm, Scene *owner_scene)
+int filelist_readjob_running(FileList *filelist, wmWindowManager *wm)
{
- return WM_jobs_test(wm, owner_scene, WM_JOB_TYPE_FILESEL_READDIR);
+ return WM_jobs_test(wm, filelist, WM_JOB_TYPE_FILESEL_READDIR);
}
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 9eb70dd8437..0048a349dca 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -27,14 +27,17 @@
extern "C" {
#endif
+struct AssetLibraryReference;
struct BlendHandle;
struct FileList;
-struct FileSelectAssetLibraryUID;
struct FileSelection;
+struct bUUID;
struct wmWindowManager;
struct FileDirEntry;
+typedef uint32_t FileUID;
+
typedef enum FileSelType {
FILE_SEL_REMOVE = 0,
FILE_SEL_ADD = 1,
@@ -69,21 +72,31 @@ void filelist_setfilter_options(struct FileList *filelist,
const bool filter_assets_only,
const char *filter_glob,
const char *filter_search);
+void filelist_set_asset_catalog_filter_options(
+ struct FileList *filelist,
+ eFileSel_Params_AssetCatalogVisibility catalog_visibility,
+ const struct bUUID *catalog_id);
+void filelist_tag_needs_filtering(struct FileList *filelist);
void filelist_filter(struct FileList *filelist);
void filelist_setlibrary(struct FileList *filelist,
- const struct FileSelectAssetLibraryUID *asset_library);
+ const struct AssetLibraryReference *asset_library_ref);
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_ex(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_clear_ex(struct FileList *filelist,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection);
+void filelist_clear_from_reset_tag(struct FileList *filelist);
void filelist_free(struct FileList *filelist);
const char *filelist_dir(struct FileList *filelist);
@@ -95,14 +108,17 @@ int filelist_needs_reading(struct FileList *filelist);
FileDirEntry *filelist_file(struct FileList *filelist, int index);
FileDirEntry *filelist_file_ex(struct FileList *filelist, int index, bool use_request);
-int filelist_file_findpath(struct FileList *filelist, const char *file);
+int filelist_file_find_path(struct FileList *filelist, const char *file);
+int filelist_file_find_id(const struct FileList *filelist, const struct ID *id);
struct ID *filelist_file_get_id(const struct FileDirEntry *file);
-FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]);
+bool filelist_uid_is_set(const FileUID uid);
+void filelist_uid_unset(FileUID *r_uid);
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_needs_force_reset(struct FileList *filelist);
void filelist_tag_force_reset(struct FileList *filelist);
+void filelist_tag_force_reset_mainfiles(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);
@@ -136,17 +152,22 @@ void filelist_entry_parent_select_set(struct FileList *filelist,
void filelist_setrecursion(struct FileList *filelist, const int recursion_level);
+struct AssetLibrary *filelist_asset_library(struct FileList *filelist);
+
struct BlendHandle *filelist_lib(struct FileList *filelist);
bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group);
void filelist_freelib(struct FileList *filelist);
-void filelist_readjob_start(struct FileList *filelist, const struct bContext *C);
-void filelist_readjob_stop(struct wmWindowManager *wm, struct Scene *owner_scene);
-int filelist_readjob_running(struct wmWindowManager *wm, struct Scene *owner_scene);
+void filelist_readjob_start(struct FileList *filelist,
+ int space_notifier,
+ const struct bContext *C);
+void filelist_readjob_stop(struct FileList *filelist, struct wmWindowManager *wm);
+int filelist_readjob_running(struct FileList *filelist, struct wmWindowManager *wm);
bool filelist_cache_previews_update(struct FileList *filelist);
void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews);
bool filelist_cache_previews_running(struct FileList *filelist);
+bool filelist_cache_previews_done(struct FileList *filelist);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 8e3fc36aa71..11757975a62 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -118,24 +118,22 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
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;
- asset_params->asset_library.custom_library_index = -1;
- asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
+ asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL;
+ asset_params->asset_library_ref.custom_library_index = -1;
+ asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE;
}
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->filter_id = FILTER_ID_ALL;
base_params->display = FILE_IMGDISPLAY;
base_params->sort = FILE_SORT_ALPHA;
- base_params->recursion_level = 1;
+ /* Asset libraries include all sub-directories, so enable maximal recursion. */
+ base_params->recursion_level = FILE_SELECT_MAX_RECURSIONS;
/* '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;
@@ -378,7 +376,7 @@ FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile)
return &sfile->asset_params->base_params;
}
- BLI_assert(!"Invalid browse mode set in file space.");
+ BLI_assert_msg(0, "Invalid browse mode set in file space.");
return NULL;
}
@@ -399,7 +397,7 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
return (FileSelectParams *)sfile->asset_params;
}
- BLI_assert(!"Invalid browse mode set in file space.");
+ BLI_assert_msg(0, "Invalid browse mode set in file space.");
return NULL;
}
@@ -415,31 +413,32 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile)
static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
{
- FileSelectAssetLibraryUID *library = &asset_params->asset_library;
+ AssetLibraryReference *library = &asset_params->asset_library_ref;
FileSelectParams *base_params = &asset_params->base_params;
bUserAssetLibrary *user_library = NULL;
/* Ensure valid repository, or fall-back to local one. */
- if (library->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ if (library->type == ASSET_LIBRARY_CUSTOM) {
BLI_assert(library->custom_library_index >= 0);
user_library = BKE_preferences_asset_library_find_from_index(&U,
library->custom_library_index);
if (!user_library) {
- library->type = FILE_ASSET_LIBRARY_LOCAL;
+ library->type = ASSET_LIBRARY_LOCAL;
}
}
switch (library->type) {
- case FILE_ASSET_LIBRARY_LOCAL:
+ case ASSET_LIBRARY_LOCAL:
base_params->dir[0] = '\0';
break;
- case FILE_ASSET_LIBRARY_CUSTOM:
+ case ASSET_LIBRARY_CUSTOM:
BLI_assert(user_library);
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
break;
}
- base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+ base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET :
+ FILE_ASSET_LIBRARY;
}
void fileselect_refresh_params(SpaceFile *sfile)
@@ -450,11 +449,25 @@ void fileselect_refresh_params(SpaceFile *sfile)
}
}
+bool ED_fileselect_is_file_browser(const SpaceFile *sfile)
+{
+ return (sfile->browse_mode == FILE_BROWSE_MODE_FILES);
+}
+
bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
{
return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
}
+struct AssetLibrary *ED_fileselect_active_asset_library_get(const SpaceFile *sfile)
+{
+ if (!ED_fileselect_is_asset_browser(sfile) || !sfile->files) {
+ return NULL;
+ }
+
+ return filelist_asset_library(sfile->files);
+}
+
struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile)
{
if (!ED_fileselect_is_asset_browser(sfile)) {
@@ -470,6 +483,18 @@ struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile)
return filelist_file_get_id(file);
}
+void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, const bUUID catalog_id)
+{
+ if (!ED_fileselect_is_asset_browser(sfile)) {
+ return;
+ }
+
+ FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile);
+ params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG;
+ params->catalog_id = catalog_id;
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
+}
+
static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data)
{
ID *asset_id = (ID *)custom_data;
@@ -501,20 +526,57 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def
const FileDirEntry *file = filelist_file_ex(files, file_index, false);
if (filelist_file_get_id(file) != asset_id) {
- filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
continue;
}
params->active_file = file_index;
filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
-
- /* Keep looping to deselect the other files. */
+ break;
}
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL);
WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL);
}
+static void on_reload_select_by_relpath(SpaceFile *sfile, onReloadFnData custom_data)
+{
+ const char *relative_path = custom_data;
+ ED_fileselect_activate_by_relpath(sfile, relative_path);
+}
+
+void ED_fileselect_activate_by_relpath(SpaceFile *sfile, const char *relative_path)
+{
+ /* If there are filelist operations running now ("pending" true) or soon ("force reset" true),
+ * there is a fair chance that the to-be-activated file at relative_path will only be present
+ * after these operations have completed. Defer activation until then. */
+ struct FileList *files = sfile->files;
+ if (files == NULL || filelist_pending(files) || filelist_needs_force_reset(files)) {
+ /* Casting away the constness of `relative_path` is safe here, because eventually it just ends
+ * up in another call to this function, and then it's a const char* again. */
+ file_on_reload_callback_register(sfile, on_reload_select_by_relpath, (char *)relative_path);
+ return;
+ }
+
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ const int num_files_filtered = filelist_files_ensure(files);
+
+ for (int file_index = 0; file_index < num_files_filtered; ++file_index) {
+ const FileDirEntry *file = filelist_file(files, file_index);
+
+ if (STREQ(file->relpath, relative_path)) {
+ params->active_file = file_index;
+ filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
+ }
+ }
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+}
+
+void ED_fileselect_deselect_all(SpaceFile *sfile)
+{
+ file_select_deselect_all(sfile, FILE_SEL_SELECTED);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+}
+
/* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA
* may also be remembered, but only conditionally. */
#define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT)
@@ -858,20 +920,8 @@ FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
float file_string_width(const char *str)
{
const uiStyle *style = UI_style_get();
- float width;
-
UI_fontstyle_set(&style->widget);
- if (style->widget.kerning == 1) { /* for BLF_width */
- BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
- }
-
- width = BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
-
- if (style->widget.kerning == 1) {
- BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
- }
-
- return width;
+ return BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
}
float file_font_pointsize(void)
@@ -941,6 +991,8 @@ static void file_attribute_columns_init(const FileSelectParams *params, FileLayo
void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
{
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ /* Request a slightly more compact layout for asset browsing. */
+ const bool compact = ED_fileselect_is_asset_browser(sfile);
FileLayout *layout = NULL;
View2D *v2d = &region->v2d;
int numfiles;
@@ -960,12 +1012,13 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
layout->textheight = textheight;
if (params->display == FILE_IMGDISPLAY) {
+ const float pad_fac = compact ? 0.15f : 0.3f;
layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X;
layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y;
- layout->tile_border_x = 0.3f * UI_UNIT_X;
- layout->tile_border_y = 0.3f * UI_UNIT_X;
- layout->prv_border_x = 0.3f * UI_UNIT_X;
- layout->prv_border_y = 0.3f * UI_UNIT_Y;
+ layout->tile_border_x = pad_fac * UI_UNIT_X;
+ layout->tile_border_y = pad_fac * UI_UNIT_X;
+ layout->prv_border_x = pad_fac * UI_UNIT_X;
+ layout->prv_border_y = pad_fac * UI_UNIT_Y;
layout->tile_w = layout->prv_w + 2 * layout->prv_border_x;
layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight;
layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
@@ -1047,7 +1100,7 @@ FileLayout *ED_fileselect_get_layout(struct SpaceFile *sfile, ARegion *region)
* Support updating the directory even when this isn't the active space
* needed so RNA properties update function isn't context sensitive, see T70255.
*/
-void ED_file_change_dir_ex(bContext *C, bScreen *screen, ScrArea *area)
+void ED_file_change_dir_ex(bContext *C, ScrArea *area)
{
/* May happen when manipulating non-active spaces. */
if (UNLIKELY(area->spacetype != SPACE_FILE)) {
@@ -1057,10 +1110,7 @@ void ED_file_change_dir_ex(bContext *C, bScreen *screen, ScrArea *area)
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
wmWindowManager *wm = CTX_wm_manager(C);
- Scene *scene = WM_windows_scene_get_from_screen(wm, screen);
- if (LIKELY(scene != NULL)) {
- ED_fileselect_clear(wm, scene, sfile);
- }
+ ED_fileselect_clear(wm, sfile);
/* Clear search string, it is very rare to want to keep that filter while changing dir,
* and usually very annoying to keep it actually! */
@@ -1085,9 +1135,17 @@ void ED_file_change_dir_ex(bContext *C, bScreen *screen, ScrArea *area)
void ED_file_change_dir(bContext *C)
{
- bScreen *screen = CTX_wm_screen(C);
ScrArea *area = CTX_wm_area(C);
- ED_file_change_dir_ex(C, screen, area);
+ ED_file_change_dir_ex(C, area);
+}
+
+void file_select_deselect_all(SpaceFile *sfile, uint flag)
+{
+ FileSelection sel;
+ sel.first = 0;
+ sel.last = filelist_files_ensure(sfile->files) - 1;
+
+ filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
}
int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matched_file)
@@ -1183,11 +1241,11 @@ int autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
return match;
}
-void ED_fileselect_clear(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfile)
+void ED_fileselect_clear(wmWindowManager *wm, SpaceFile *sfile)
{
/* only NULL in rare cases - T29734. */
if (sfile->files) {
- filelist_readjob_stop(wm, owner_scene);
+ filelist_readjob_stop(sfile->files, wm);
filelist_freelib(sfile->files);
filelist_clear(sfile->files);
}
@@ -1197,7 +1255,7 @@ void ED_fileselect_clear(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfi
WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
-void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfile)
+void ED_fileselect_exit(wmWindowManager *wm, SpaceFile *sfile)
{
if (!sfile) {
return;
@@ -1224,13 +1282,72 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil
folder_history_list_free(sfile);
if (sfile->files) {
- ED_fileselect_clear(wm, owner_scene, sfile);
+ ED_fileselect_clear(wm, sfile);
filelist_free(sfile->files);
MEM_freeN(sfile->files);
sfile->files = NULL;
}
}
+void file_params_smoothscroll_timer_clear(wmWindowManager *wm, wmWindow *win, SpaceFile *sfile)
+{
+ WM_event_remove_timer(wm, win, sfile->smoothscroll_timer);
+ sfile->smoothscroll_timer = NULL;
+}
+
+/**
+ * Set the renaming-state to #FILE_PARAMS_RENAME_POSTSCROLL_PENDING and trigger the smooth-scroll
+ * timer. To be used right after a file was renamed.
+ * Note that the caller is responsible for setting the correct rename-file info
+ * (#FileSelectParams.renamefile or #FileSelectParams.rename_id).
+ */
+void file_params_invoke_rename_postscroll(wmWindowManager *wm, wmWindow *win, SpaceFile *sfile)
+{
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+
+ params->rename_flag = FILE_PARAMS_RENAME_POSTSCROLL_PENDING;
+
+ if (sfile->smoothscroll_timer != NULL) {
+ file_params_smoothscroll_timer_clear(wm, win, sfile);
+ }
+ sfile->smoothscroll_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 1000.0);
+ sfile->scroll_offset = 0;
+}
+
+/**
+ * To be executed whenever renaming ends (successfully or not).
+ */
+void file_params_rename_end(wmWindowManager *wm,
+ wmWindow *win,
+ SpaceFile *sfile,
+ FileDirEntry *rename_file)
+{
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+
+ filelist_entry_select_set(
+ sfile->files, rename_file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL);
+
+ /* Ensure smooth-scroll timer is active, even if not needed, because that way rename state is
+ * handled properly. */
+ file_params_invoke_rename_postscroll(wm, win, sfile);
+ /* Also always activate the rename file, even if renaming was canceled. */
+ file_params_renamefile_activate(sfile, params);
+}
+
+void file_params_renamefile_clear(FileSelectParams *params)
+{
+ params->renamefile[0] = '\0';
+ params->rename_id = NULL;
+ params->rename_flag = 0;
+}
+
+static int file_params_find_renamed(const FileSelectParams *params, struct FileList *filelist)
+{
+ /* Find the file either through the local ID/asset it represents or its relative path. */
+ return (params->rename_id != NULL) ? filelist_file_find_id(filelist, params->rename_id) :
+ filelist_file_find_path(filelist, params->renamefile);
+}
+
/**
* Helper used by both main update code, and smooth-scroll timer,
* to try to enable rename editing from #FileSelectParams.renamefile name.
@@ -1244,28 +1361,33 @@ void file_params_renamefile_activate(SpaceFile *sfile, FileSelectParams *params)
return;
}
- BLI_assert(params->renamefile[0] != '\0');
+ BLI_assert(params->renamefile[0] != '\0' || params->rename_id != NULL);
- const int idx = filelist_file_findpath(sfile->files, params->renamefile);
+ const int idx = file_params_find_renamed(params, sfile->files);
if (idx >= 0) {
FileDirEntry *file = filelist_file(sfile->files, idx);
BLI_assert(file != NULL);
+ params->active_file = idx;
+ filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
+
if ((params->rename_flag & FILE_PARAMS_RENAME_PENDING) != 0) {
filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
params->rename_flag = FILE_PARAMS_RENAME_ACTIVE;
}
else if ((params->rename_flag & FILE_PARAMS_RENAME_POSTSCROLL_PENDING) != 0) {
- filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
- params->renamefile[0] = '\0';
+ file_select_deselect_all(sfile, FILE_SEL_SELECTED);
+ filelist_entry_select_set(
+ sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED, CHECK_ALL);
+ params->active_file = idx;
+ file_params_renamefile_clear(params);
params->rename_flag = FILE_PARAMS_RENAME_POSTSCROLL_ACTIVE;
}
}
/* File listing is now async, only reset renaming if matching entry is not found
* when file listing is not done. */
else if (filelist_is_ready(sfile->files)) {
- params->renamefile[0] = '\0';
- params->rename_flag = 0;
+ file_params_renamefile_clear(params);
}
}
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 2d1151c8f4d..97f22ca7d89 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -769,21 +769,22 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
FS_INSERT_LAST);
const char *home = BLI_getenv("HOME");
-
+ if (home) {
# define FS_MACOS_PATH(path, name, icon) \
BLI_snprintf(line, sizeof(line), path, home); \
fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST);
- FS_MACOS_PATH("%s/", NULL, ICON_HOME)
- FS_MACOS_PATH("%s/Desktop/", N_("Desktop"), ICON_DESKTOP)
- FS_MACOS_PATH("%s/Documents/", N_("Documents"), ICON_DOCUMENTS)
- FS_MACOS_PATH("%s/Downloads/", N_("Downloads"), ICON_IMPORT)
- FS_MACOS_PATH("%s/Movies/", N_("Movies"), ICON_FILE_MOVIE)
- FS_MACOS_PATH("%s/Music/", N_("Music"), ICON_FILE_SOUND)
- FS_MACOS_PATH("%s/Pictures/", N_("Pictures"), ICON_FILE_IMAGE)
- FS_MACOS_PATH("%s/Library/Fonts/", N_("Fonts"), ICON_FILE_FONT)
+ FS_MACOS_PATH("%s/", NULL, ICON_HOME)
+ FS_MACOS_PATH("%s/Desktop/", N_("Desktop"), ICON_DESKTOP)
+ FS_MACOS_PATH("%s/Documents/", N_("Documents"), ICON_DOCUMENTS)
+ FS_MACOS_PATH("%s/Downloads/", N_("Downloads"), ICON_IMPORT)
+ FS_MACOS_PATH("%s/Movies/", N_("Movies"), ICON_FILE_MOVIE)
+ FS_MACOS_PATH("%s/Music/", N_("Music"), ICON_FILE_SOUND)
+ FS_MACOS_PATH("%s/Pictures/", N_("Pictures"), ICON_FILE_IMAGE)
+ FS_MACOS_PATH("%s/Library/Fonts/", N_("Fonts"), ICON_FILE_FONT)
# undef FS_MACOS_PATH
+ }
/* Get mounted volumes better method OSX 10.6 and higher, see:
* https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html
@@ -958,7 +959,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
found = 1;
}
if (endmntent(fp) == 0) {
- fprintf(stderr, "could not close the list of mounted filesystems\n");
+ fprintf(stderr, "could not close the list of mounted file-systems\n");
}
}
/* Check gvfs shares. */
@@ -969,7 +970,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/");
const uint dir_len = BLI_filelist_dir_contents(name, &dir);
for (uint i = 0; i < dir_len; i++) {
- if ((dir[i].type & S_IFDIR)) {
+ if (dir[i].type & S_IFDIR) {
const char *dirname = dir[i].relname;
if (dirname[0] != '.') {
/* Dir names contain a lot of unwanted text.
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 0418bb87768..b115c63a569 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -32,6 +32,7 @@
#include "BKE_appdir.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_main.h"
#include "BKE_screen.h"
#include "RNA_access.h"
@@ -42,6 +43,7 @@
#include "WM_message.h"
#include "WM_types.h"
+#include "ED_asset.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@@ -175,10 +177,7 @@ static void file_free(SpaceLink *sl)
MEM_SAFE_FREE(sfile->asset_params);
MEM_SAFE_FREE(sfile->runtime);
- if (sfile->layout) {
- MEM_freeN(sfile->layout);
- sfile->layout = NULL;
- }
+ MEM_SAFE_FREE(sfile->layout);
}
/* spacetype; init callback, area size changes, screen set, etc */
@@ -206,7 +205,7 @@ static void file_exit(wmWindowManager *wm, ScrArea *area)
sfile->previews_timer = NULL;
}
- ED_fileselect_exit(wm, NULL, sfile);
+ ED_fileselect_exit(wm, sfile);
}
static SpaceLink *file_duplicate(SpaceLink *sl)
@@ -305,15 +304,6 @@ 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);
@@ -328,7 +318,7 @@ static void file_refresh(const bContext *C, ScrArea *area)
if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) &&
filelist_needs_reset_on_main_changes(sfile->files)) {
- filelist_tag_force_reset(sfile->files);
+ filelist_tag_force_reset_mainfiles(sfile->files);
}
sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES;
@@ -336,11 +326,18 @@ static void file_refresh(const bContext *C, ScrArea *area)
sfile->files = filelist_new(params->type);
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
+
+ if (ED_fileselect_is_asset_browser(sfile)) {
+ /* Ask the asset code for appropriate ID filter flags for the supported assets, and mask others
+ * out. */
+ params->filter_id &= ED_asset_types_supported_as_filter_flags();
+ }
+
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_setlibrary(sfile->files, asset_params ? &asset_params->asset_library_ref : NULL);
filelist_setfilter_options(
sfile->files,
(params->flag & FILE_FILTER) != 0,
@@ -351,6 +348,10 @@ static void file_refresh(const bContext *C, ScrArea *area)
(params->flag & FILE_ASSETS_ONLY) != 0,
params->filter_glob,
params->filter_search);
+ if (asset_params) {
+ filelist_set_asset_catalog_filter_options(
+ sfile->files, asset_params->asset_catalog_visibility, &asset_params->catalog_id);
+ }
/* Update the active indices of bookmarks & co. */
sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
@@ -360,13 +361,13 @@ static void file_refresh(const bContext *C, ScrArea *area)
sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
if (filelist_needs_force_reset(sfile->files)) {
- filelist_readjob_stop(wm, CTX_data_scene(C));
- filelist_clear(sfile->files);
+ filelist_readjob_stop(sfile->files, wm);
+ filelist_clear_from_reset_tag(sfile->files);
}
if (filelist_needs_reading(sfile->files)) {
if (!filelist_pending(sfile->files)) {
- filelist_readjob_start(sfile->files, C);
+ filelist_readjob_start(sfile->files, NC_SPACE | ND_SPACE_FILE_LIST, C);
}
}
@@ -423,16 +424,15 @@ static void file_on_reload_callback_call(SpaceFile *sfile)
static void file_reset_filelist_showing_main_data(ScrArea *area, SpaceFile *sfile)
{
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);
+ filelist_tag_force_reset_mainfiles(sfile->files);
+ ED_area_tag_refresh(area);
}
}
-static void file_listener(const wmSpaceTypeListenerParams *params)
+static void file_listener(const wmSpaceTypeListenerParams *listener_params)
{
- ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ ScrArea *area = listener_params->area;
+ wmNotifier *wmn = listener_params->notifier;
SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
/* context changes */
@@ -469,10 +469,19 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
break;
case NC_ID: {
switch (wmn->action) {
- case NA_RENAME:
+ case NA_RENAME: {
+ const ID *active_file_id = ED_fileselect_active_asset_get(sfile);
+ /* If a renamed ID is active in the file browser, update scrolling to keep it in view. */
+ if (active_file_id && (wmn->reference == active_file_id)) {
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ params->rename_id = active_file_id;
+ file_params_invoke_rename_postscroll(G_MAIN->wm.first, listener_params->window, sfile);
+ }
+
/* Force list to update sorting (with a full reset for now). */
file_reset_filelist_showing_main_data(area, sfile);
break;
+ }
}
break;
}
@@ -508,10 +517,10 @@ static void file_main_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
}
-static void file_main_region_listener(const wmRegionListenerParams *params)
+static void file_main_region_listener(const wmRegionListenerParams *listener_params)
{
- ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ ARegion *region = listener_params->region;
+ wmNotifier *wmn = listener_params->notifier;
/* context changes */
switch (wmn->category) {
@@ -568,6 +577,16 @@ static void file_main_region_message_subscribe(const wmRegionMessageSubscribePar
/* All properties for this space type. */
WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__);
}
+
+ /* Experimental Asset Browser features option. */
+ {
+ PointerRNA ptr;
+ RNA_pointer_create(NULL, &RNA_PreferencesExperimental, &U.experimental, &ptr);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "use_extended_asset_browser");
+
+ /* All properties for this space type. */
+ WM_msg_subscribe_rna(mbus, &ptr, prop, &msg_sub_value_area_tag_refresh, __func__);
+ }
}
static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile)
@@ -637,10 +656,10 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
/* on first read, find active file */
if (params->highlight_file == -1) {
wmEvent *event = CTX_wm_window(C)->eventstate;
- file_highlight_set(sfile, region, event->x, event->y);
+ file_highlight_set(sfile, region, event->xy[0], event->xy[1]);
}
- if (!file_draw_hint_if_invalid(sfile, region)) {
+ if (!file_draw_hint_if_invalid(C, sfile, region)) {
file_draw_list(C, region);
}
@@ -669,13 +688,13 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_previous);
WM_operatortype_append(FILE_OT_next);
WM_operatortype_append(FILE_OT_refresh);
+ WM_operatortype_append(FILE_OT_asset_library_refresh);
WM_operatortype_append(FILE_OT_bookmark_add);
WM_operatortype_append(FILE_OT_bookmark_delete);
WM_operatortype_append(FILE_OT_bookmark_cleanup);
WM_operatortype_append(FILE_OT_bookmark_move);
WM_operatortype_append(FILE_OT_reset_recent);
WM_operatortype_append(FILE_OT_hidedot);
- WM_operatortype_append(FILE_OT_associate_blend);
WM_operatortype_append(FILE_OT_filenum);
WM_operatortype_append(FILE_OT_directory_new);
WM_operatortype_append(FILE_OT_delete);
@@ -716,14 +735,24 @@ static void file_tools_region_draw(const bContext *C, ARegion *region)
ED_region_panels(C, region);
}
-static void file_tools_region_listener(const wmRegionListenerParams *UNUSED(params))
+static void file_tools_region_listener(const wmRegionListenerParams *listener_params)
{
+ const wmNotifier *wmn = listener_params->notifier;
+ ARegion *region = listener_params->region;
+
+ switch (wmn->category) {
+ case NC_SCENE:
+ if (ELEM(wmn->data, ND_MODE)) {
+ ED_region_tag_redraw(region);
+ }
+ break;
+ }
}
-static void file_tool_props_region_listener(const wmRegionListenerParams *params)
+static void file_tool_props_region_listener(const wmRegionListenerParams *listener_params)
{
- const wmNotifier *wmn = params->notifier;
- ARegion *region = params->region;
+ const wmNotifier *wmn = listener_params->notifier;
+ ARegion *region = listener_params->region;
switch (wmn->category) {
case NC_ID:
@@ -732,6 +761,11 @@ static void file_tool_props_region_listener(const wmRegionListenerParams *params
ED_region_tag_redraw(region);
}
break;
+ case NC_SCENE:
+ if (ELEM(wmn->data, ND_MODE)) {
+ ED_region_tag_redraw(region);
+ }
+ break;
}
}
@@ -789,10 +823,10 @@ static void file_execution_region_draw(const bContext *C, ARegion *region)
ED_region_panels(C, region);
}
-static void file_ui_region_listener(const wmRegionListenerParams *params)
+static void file_ui_region_listener(const wmRegionListenerParams *listener_params)
{
- ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ ARegion *region = listener_params->region;
+ wmNotifier *wmn = listener_params->notifier;
/* context changes */
switch (wmn->category) {
@@ -806,10 +840,7 @@ static void file_ui_region_listener(const wmRegionListenerParams *params)
}
}
-static bool filepath_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -830,7 +861,7 @@ static void file_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL);
+ WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL, NULL);
}
static int file_space_subtype_get(ScrArea *area)
@@ -849,16 +880,17 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C),
EnumPropertyItem **item,
int *totitem)
{
- if (U.experimental.use_asset_browser) {
- RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
- }
- else {
- RNA_enum_items_add_value(
- item, totitem, rna_enum_space_file_browse_mode_items, FILE_BROWSE_MODE_FILES);
- }
+ RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
}
-static const char *file_context_dir[] = {"active_file", "id", NULL};
+const char *file_context_dir[] = {
+ "active_file",
+ "selected_files",
+ "asset_library_ref",
+ "selected_asset_files",
+ "id",
+ NULL,
+};
static int /*eContextResult*/ file_context(const bContext *C,
const char *member,
@@ -889,6 +921,48 @@ static int /*eContextResult*/ file_context(const bContext *C,
CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
return CTX_RESULT_OK;
}
+ if (CTX_data_equals(member, "selected_files")) {
+ const int num_files_filtered = filelist_files_ensure(sfile->files);
+
+ for (int file_index = 0; file_index < num_files_filtered; file_index++) {
+ if (filelist_entry_is_selected(sfile->files, file_index)) {
+ FileDirEntry *entry = filelist_file(sfile->files, file_index);
+ CTX_data_list_add(result, &screen->id, &RNA_FileSelectEntry, entry);
+ }
+ }
+
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ return CTX_RESULT_OK;
+ }
+
+ if (CTX_data_equals(member, "asset_library_ref")) {
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ if (!asset_params) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ CTX_data_pointer_set(
+ result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library_ref);
+ return CTX_RESULT_OK;
+ }
+ /** TODO temporary AssetHandle design: For now this returns the file entry. Would be better if it
+ * was `"selected_assets"` and returned the assets (e.g. as `AssetHandle`) directly. See comment
+ * for #AssetHandle for more info. */
+ if (CTX_data_equals(member, "selected_asset_files")) {
+ const int num_files_filtered = filelist_files_ensure(sfile->files);
+
+ for (int file_index = 0; file_index < num_files_filtered; file_index++) {
+ if (filelist_entry_is_selected(sfile->files, file_index)) {
+ FileDirEntry *entry = filelist_file(sfile->files, file_index);
+ if (entry->asset_data) {
+ CTX_data_list_add(result, &screen->id, &RNA_FileSelectEntry, entry);
+ }
+ }
+ }
+
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ return CTX_RESULT_OK;
+ }
if (CTX_data_equals(member, "id")) {
const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file == NULL) {
@@ -992,6 +1066,7 @@ void ED_spacetype_file(void)
art->init = file_tools_region_init;
art->draw = file_tools_region_draw;
BLI_addhead(&st->regiontypes, art);
+ file_tools_region_panels_register(art);
/* regions: tool properties */
art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");