This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/asset/intern/asset_ops.cc
Brecht Van Lommel 4c3f57ffa7 Cleanup: compiler warnings with clang
Includes use of memcpy to avoid warnings about deprecated members.
2021-12-18 18:36:34 +01:00

964 lines
28 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup edasset
*/
#include "BKE_asset_library.hh"
#include "BKE_bpath.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_preferences.h"
#include "BKE_report.h"
#include "BLI_fileops.h"
#include "BLI_fnmatch.h"
#include "BLI_path_util.h"
#include "BLI_set.hh"
#include "ED_asset.h"
#include "ED_asset_catalog.hh"
#include "ED_screen.h"
#include "ED_util.h"
/* XXX needs access to the file list, should all be done via the asset system in future. */
#include "ED_fileselect.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "DNA_space_types.h"
#include "BLO_writefile.h"
using namespace blender;
/* -------------------------------------------------------------------- */
using PointerRNAVec = blender::Vector<PointerRNA>;
/**
* Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or
* multiple ones ("selected_ids" context member).
*/
static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C)
{
PointerRNAVec ids;
PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
if (idptr.data) {
/* Single ID. */
ids.append(idptr);
}
else {
ListBase list;
CTX_data_selected_ids(C, &list);
LISTBASE_FOREACH (CollectionPointerLink *, link, &list) {
ids.append(link->ptr);
}
BLI_freelistN(&list);
}
return ids;
}
/**
* Information about what's contained in a #PointerRNAVec, returned by
* #asset_operation_get_id_vec_stats_from_context().
*/
struct IDVecStats {
bool has_asset = false;
bool has_supported_type = false;
bool is_single = false;
};
/**
* Helper to report stats about the IDs in context. Operator polls use this, also to report a
* helpful disabled hint to the user.
*/
static IDVecStats asset_operation_get_id_vec_stats_from_context(const bContext *C)
{
PointerRNAVec pointers = asset_operation_get_ids_from_context(C);
IDVecStats stats;
stats.is_single = pointers.size() == 1;
for (PointerRNA &ptr : pointers) {
BLI_assert(RNA_struct_is_ID(ptr.type));
ID *id = static_cast<ID *>(ptr.data);
if (ED_asset_type_is_supported(id)) {
stats.has_supported_type = true;
}
if (ID_IS_ASSET(id)) {
stats.has_asset = true;
}
}
return stats;
}
static const char *asset_operation_unsupported_type_msg(const bool is_single)
{
const char *msg_single =
"Data-block does not support asset operations - must be "
"a " ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING;
const char *msg_multiple =
"No data-block selected that supports asset operations - select at least "
"one " ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING;
return is_single ? msg_single : msg_multiple;
}
/* -------------------------------------------------------------------- */
class AssetMarkHelper {
public:
void operator()(const bContext &C, PointerRNAVec &ids);
void reportResults(ReportList &reports) const;
bool wasSuccessful() const;
private:
struct Stats {
int tot_created = 0;
int tot_already_asset = 0;
ID *last_id = nullptr;
};
Stats stats;
};
void AssetMarkHelper::operator()(const bContext &C, PointerRNAVec &ids)
{
for (PointerRNA &ptr : ids) {
BLI_assert(RNA_struct_is_ID(ptr.type));
ID *id = static_cast<ID *>(ptr.data);
if (id->asset_data) {
stats.tot_already_asset++;
continue;
}
if (ED_asset_mark_id(id)) {
ED_asset_generate_preview(&C, id);
stats.last_id = id;
stats.tot_created++;
}
}
}
bool AssetMarkHelper::wasSuccessful() const
{
return stats.tot_created > 0;
}
void AssetMarkHelper::reportResults(ReportList &reports) const
{
/* User feedback on failure. */
if (!wasSuccessful()) {
if (stats.tot_already_asset > 0) {
BKE_report(&reports,
RPT_ERROR,
"Selected data-blocks are already assets (or do not support use as assets)");
}
else {
BKE_report(&reports,
RPT_ERROR,
"No data-blocks to create assets for found (or do not support use as assets)");
}
}
/* User feedback on success. */
else if (stats.tot_created == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(&reports, RPT_INFO, "Data-block '%s' is now an asset", stats.last_id->name + 2);
}
else {
BKE_reportf(&reports, RPT_INFO, "%i data-blocks are now assets", stats.tot_created);
}
}
static int asset_mark_exec(bContext *C, wmOperator *op)
{
PointerRNAVec ids = asset_operation_get_ids_from_context(C);
AssetMarkHelper mark_helper;
mark_helper(*C, ids);
mark_helper.reportResults(*op->reports);
if (!mark_helper.wasSuccessful()) {
return OPERATOR_CANCELLED;
}
WM_main_add_notifier(NC_ID | NA_EDITED, nullptr);
WM_main_add_notifier(NC_ASSET | NA_ADDED, nullptr);
return OPERATOR_FINISHED;
}
static bool asset_mark_poll(bContext *C)
{
IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C);
if (!ctx_stats.has_supported_type) {
CTX_wm_operator_poll_msg_set(C, asset_operation_unsupported_type_msg(ctx_stats.is_single));
return false;
}
return true;
}
static void ASSET_OT_mark(wmOperatorType *ot)
{
ot->name = "Mark as Asset";
ot->description =
"Enable easier reuse of selected data-blocks through the Asset Browser, with the help of "
"customizable metadata (like previews, descriptions and tags)";
ot->idname = "ASSET_OT_mark";
ot->exec = asset_mark_exec;
ot->poll = asset_mark_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* -------------------------------------------------------------------- */
class AssetClearHelper {
const bool set_fake_user_;
public:
AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user)
{
}
void operator()(PointerRNAVec &ids);
void reportResults(const bContext *C, ReportList &reports) const;
bool wasSuccessful() const;
private:
struct Stats {
int tot_cleared = 0;
ID *last_id = nullptr;
};
Stats stats;
};
void AssetClearHelper::operator()(PointerRNAVec &ids)
{
for (PointerRNA &ptr : ids) {
BLI_assert(RNA_struct_is_ID(ptr.type));
ID *id = static_cast<ID *>(ptr.data);
if (!id->asset_data) {
continue;
}
if (!ED_asset_clear_id(id)) {
continue;
}
if (set_fake_user_) {
id_fake_user_set(id);
}
stats.tot_cleared++;
stats.last_id = id;
}
}
void AssetClearHelper::reportResults(const bContext *C, ReportList &reports) const
{
if (!wasSuccessful()) {
bool is_valid;
/* Dedicated error message for when there is an active asset detected, but it's not an ID local
* to this file. Helps users better understanding what's going on. */
if (AssetHandle active_asset = CTX_wm_asset_handle(C, &is_valid);
is_valid && !ED_asset_handle_get_local_id(&active_asset)) {
BKE_report(&reports,
RPT_ERROR,
"No asset data-blocks from the current file selected (assets must be stored in "
"the current file to be able to edit or clear them)");
}
else {
BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused");
}
}
else if (stats.tot_cleared == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(
&reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats.last_id->name + 2);
}
else {
BKE_reportf(&reports, RPT_INFO, "%i data-blocks are no assets anymore", stats.tot_cleared);
}
}
bool AssetClearHelper::wasSuccessful() const
{
return stats.tot_cleared > 0;
}
static int asset_clear_exec(bContext *C, wmOperator *op)
{
PointerRNAVec ids = asset_operation_get_ids_from_context(C);
const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user");
AssetClearHelper clear_helper(set_fake_user);
clear_helper(ids);
clear_helper.reportResults(C, *op->reports);
if (!clear_helper.wasSuccessful()) {
return OPERATOR_CANCELLED;
}
WM_main_add_notifier(NC_ID | NA_EDITED, nullptr);
WM_main_add_notifier(NC_ASSET | NA_REMOVED, nullptr);
return OPERATOR_FINISHED;
}
static bool asset_clear_poll(bContext *C)
{
IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C);
if (!ctx_stats.has_asset) {
const char *msg_single = "Data-block is not marked as asset";
const char *msg_multiple = "No data-block selected that is marked as asset";
CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple);
return false;
}
if (!ctx_stats.has_supported_type) {
CTX_wm_operator_poll_msg_set(C, asset_operation_unsupported_type_msg(ctx_stats.is_single));
return false;
}
return true;
}
static char *asset_clear_get_description(struct bContext *UNUSED(C),
struct wmOperatorType *UNUSED(op),
struct PointerRNA *values)
{
const bool set_fake_user = RNA_boolean_get(values, "set_fake_user");
if (!set_fake_user) {
return nullptr;
}
return BLI_strdup(
"Delete all asset metadata, turning the selected asset data-blocks back into normal "
"data-blocks, and set Fake User to ensure the data-blocks will still be saved");
}
static void ASSET_OT_clear(wmOperatorType *ot)
{
ot->name = "Clear Asset";
ot->description =
"Delete all asset metadata and turn the selected asset data-blocks back into normal "
"data-blocks";
ot->get_description = asset_clear_get_description;
ot->idname = "ASSET_OT_clear";
ot->exec = asset_clear_exec;
ot->poll = asset_clear_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"set_fake_user",
false,
"Set Fake User",
"Ensure the data-block is saved, even when it is no longer marked as asset");
}
/* -------------------------------------------------------------------- */
static bool asset_library_refresh_poll(bContext *C)
{
if (ED_operator_asset_browsing_active(C)) {
return true;
}
/* While not inside an Asset Browser, check if there's a asset list stored for the active asset
* library (stored in the workspace, obtained via context). */
const AssetLibraryReference *library = CTX_wm_asset_library_ref(C);
if (!library) {
return false;
}
return ED_assetlist_storage_has_list_for_library(library);
}
static int asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
{
/* Execution mode #1: Inside the Asset Browser. */
if (ED_operator_asset_browsing_active(C)) {
SpaceFile *sfile = CTX_wm_space_file(C);
ED_fileselect_clear(CTX_wm_manager(C), sfile);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, nullptr);
}
else {
/* Execution mode #2: Outside the Asset Browser, use the asset list. */
const AssetLibraryReference *library = CTX_wm_asset_library_ref(C);
ED_assetlist_clear(library, C);
}
return OPERATOR_FINISHED;
}
/**
* This operator currently covers both cases, the File/Asset Browser file list and the asset list
* used for the asset-view template. Once the asset list design is used by the Asset Browser, this
* can be simplified to just that case.
*/
static void ASSET_OT_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 = "ASSET_OT_library_refresh";
/* api callbacks */
ot->exec = asset_library_refresh_exec;
ot->poll = asset_library_refresh_poll;
}
/* -------------------------------------------------------------------- */
static bool asset_catalog_operator_poll(bContext *C)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
return sfile && ED_fileselect_active_asset_library_get(sfile);
}
static int asset_catalog_new_exec(bContext *C, wmOperator *op)
{
SpaceFile *sfile = CTX_wm_space_file(C);
struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr);
blender::bke::AssetCatalog *new_catalog = ED_asset_catalog_add(
asset_library, "Catalog", parent_path);
if (sfile) {
ED_fileselect_activate_asset_catalog(sfile, new_catalog->catalog_id);
}
MEM_freeN(parent_path);
WM_event_add_notifier_ex(
CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr);
return OPERATOR_FINISHED;
}
static void ASSET_OT_catalog_new(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Asset Catalog";
ot->description = "Create a new catalog to put assets in";
ot->idname = "ASSET_OT_catalog_new";
/* api callbacks */
ot->exec = asset_catalog_new_exec;
ot->poll = asset_catalog_operator_poll;
RNA_def_string(ot->srna,
"parent_path",
nullptr,
0,
"Parent Path",
"Optional path defining the location to put the new catalog under");
}
static int asset_catalog_delete_exec(bContext *C, wmOperator *op)
{
SpaceFile *sfile = CTX_wm_space_file(C);
struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr);
bke::CatalogID catalog_id;
if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) {
return OPERATOR_CANCELLED;
}
ED_asset_catalog_remove(asset_library, catalog_id);
MEM_freeN(catalog_id_str);
WM_event_add_notifier_ex(
CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr);
return OPERATOR_FINISHED;
}
static void ASSET_OT_catalog_delete(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Asset Catalog";
ot->description =
"Remove an asset catalog from the asset library (contained assets will not be affected and "
"show up as unassigned)";
ot->idname = "ASSET_OT_catalog_delete";
/* api callbacks */
ot->exec = asset_catalog_delete_exec;
ot->poll = asset_catalog_operator_poll;
RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete");
}
static bke::AssetCatalogService *get_catalog_service(bContext *C)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile) {
return nullptr;
}
AssetLibrary *asset_lib = ED_fileselect_active_asset_library_get(sfile);
return BKE_asset_library_get_catalog_service(asset_lib);
}
static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/)
{
bke::AssetCatalogService *catalog_service = get_catalog_service(C);
if (!catalog_service) {
return OPERATOR_CANCELLED;
}
catalog_service->undo();
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
return OPERATOR_FINISHED;
}
static bool asset_catalog_undo_poll(bContext *C)
{
const bke::AssetCatalogService *catalog_service = get_catalog_service(C);
return catalog_service && catalog_service->is_undo_possbile();
}
static void ASSET_OT_catalog_undo(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Undo Catalog Edits";
ot->description = "Undo the last edit to the asset catalogs";
ot->idname = "ASSET_OT_catalog_undo";
/* api callbacks */
ot->exec = asset_catalog_undo_exec;
ot->poll = asset_catalog_undo_poll;
}
static int asset_catalog_redo_exec(bContext *C, wmOperator * /*op*/)
{
bke::AssetCatalogService *catalog_service = get_catalog_service(C);
if (!catalog_service) {
return OPERATOR_CANCELLED;
}
catalog_service->redo();
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr);
return OPERATOR_FINISHED;
}
static bool asset_catalog_redo_poll(bContext *C)
{
const bke::AssetCatalogService *catalog_service = get_catalog_service(C);
return catalog_service && catalog_service->is_redo_possbile();
}
static void ASSET_OT_catalog_redo(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Redo Catalog Edits";
ot->description = "Redo the last undone edit to the asset catalogs";
ot->idname = "ASSET_OT_catalog_redo";
/* api callbacks */
ot->exec = asset_catalog_redo_exec;
ot->poll = asset_catalog_redo_poll;
}
static int asset_catalog_undo_push_exec(bContext *C, wmOperator * /*op*/)
{
bke::AssetCatalogService *catalog_service = get_catalog_service(C);
if (!catalog_service) {
return OPERATOR_CANCELLED;
}
catalog_service->undo_push();
return OPERATOR_FINISHED;
}
static bool asset_catalog_undo_push_poll(bContext *C)
{
return get_catalog_service(C) != nullptr;
}
static void ASSET_OT_catalog_undo_push(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Store undo snapshot for asset catalog edits";
ot->description = "Store the current state of the asset catalogs in the undo buffer";
ot->idname = "ASSET_OT_catalog_undo_push";
/* api callbacks */
ot->exec = asset_catalog_undo_push_exec;
ot->poll = asset_catalog_undo_push_poll;
/* Generally artists don't need to find & use this operator, it's meant for scripts only. */
ot->flag = OPTYPE_INTERNAL;
}
/* -------------------------------------------------------------------- */
static bool asset_catalogs_save_poll(bContext *C)
{
if (!asset_catalog_operator_poll(C)) {
return false;
}
const Main *bmain = CTX_data_main(C);
if (!bmain->filepath[0]) {
CTX_wm_operator_poll_msg_set(C, "Cannot save asset catalogs before the Blender file is saved");
return false;
}
if (!BKE_asset_library_has_any_unsaved_catalogs()) {
CTX_wm_operator_poll_msg_set(C, "No changes to be saved");
return false;
}
return true;
}
static int asset_catalogs_save_exec(bContext *C, wmOperator * /*op*/)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
::AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
ED_asset_catalogs_save_from_main_path(asset_library, CTX_data_main(C));
WM_event_add_notifier_ex(
CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr);
return OPERATOR_FINISHED;
}
static void ASSET_OT_catalogs_save(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Save Asset Catalogs";
ot->description =
"Make any edits to any catalogs permanent by writing the current set up to the asset "
"library";
ot->idname = "ASSET_OT_catalogs_save";
/* api callbacks */
ot->exec = asset_catalogs_save_exec;
ot->poll = asset_catalogs_save_poll;
}
/* -------------------------------------------------------------------- */
static bool could_be_asset_bundle(const Main *bmain);
static const bUserAssetLibrary *selected_asset_library(struct wmOperator *op);
static bool is_contained_in_selected_asset_library(struct wmOperator *op, const char *filepath);
static bool set_filepath_for_asset_lib(const Main *bmain, struct wmOperator *op);
static bool has_external_files(Main *bmain, struct ReportList *reports);
static bool asset_bundle_install_poll(bContext *C)
{
/* This operator only works when the asset browser is set to Current File. */
const SpaceFile *sfile = CTX_wm_space_file(C);
if (sfile == nullptr) {
return false;
}
if (!ED_fileselect_is_local_asset_library(sfile)) {
return false;
}
const Main *bmain = CTX_data_main(C);
if (!could_be_asset_bundle(bmain)) {
return false;
}
/* Check whether this file is already located inside any asset library. */
const struct bUserAssetLibrary *asset_lib = BKE_preferences_asset_library_containing_path(
&U, bmain->filepath);
if (asset_lib) {
return false;
}
return true;
}
static int asset_bundle_install_invoke(struct bContext *C,
struct wmOperator *op,
const struct wmEvent * /*event*/)
{
Main *bmain = CTX_data_main(C);
if (has_external_files(bmain, op->reports)) {
return OPERATOR_CANCELLED;
}
WM_event_add_fileselect(C, op);
/* Make the "Save As" dialog box default to "${ASSET_LIB_ROOT}/${CURRENT_FILE}.blend". */
if (!set_filepath_for_asset_lib(bmain, op)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_RUNNING_MODAL;
}
static int asset_bundle_install_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
if (has_external_files(bmain, op->reports)) {
return OPERATOR_CANCELLED;
}
/* Check file path, copied from #wm_file_write(). */
char filepath[PATH_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
const size_t len = strlen(filepath);
if (len == 0) {
BKE_report(op->reports, RPT_ERROR, "Path is empty, cannot save");
return OPERATOR_CANCELLED;
}
if (len >= FILE_MAX) {
BKE_report(op->reports, RPT_ERROR, "Path too long, cannot save");
return OPERATOR_CANCELLED;
}
/* Check that the destination is actually contained in the selected asset library. */
if (!is_contained_in_selected_asset_library(op, filepath)) {
BKE_reportf(op->reports, RPT_ERROR, "Selected path is outside of the selected asset library");
return OPERATOR_CANCELLED;
}
WM_cursor_wait(true);
bke::AssetCatalogService *cat_service = get_catalog_service(C);
/* Store undo step, such that on a failed save the 'prepare_to_merge_on_write' call can be
* un-done. */
cat_service->undo_push();
cat_service->prepare_to_merge_on_write();
const int operator_result = WM_operator_name_call(
C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, op->ptr);
WM_cursor_wait(false);
if (operator_result != OPERATOR_FINISHED) {
cat_service->undo();
return operator_result;
}
const bUserAssetLibrary *lib = selected_asset_library(op);
BLI_assert_msg(lib, "If the asset library is not known, how did we get here?");
BKE_reportf(op->reports,
RPT_INFO,
R"(Saved "%s" to asset library "%s")",
BLI_path_basename(bmain->filepath),
lib->name);
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(false);
if (!items) {
*r_free = false;
}
*r_free = true;
return items;
}
static void ASSET_OT_bundle_install(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy to Asset Library";
ot->description =
"Copy the current .blend file into an Asset Library. Only works on standalone .blend files "
"(i.e. when no other files are referenced)";
ot->idname = "ASSET_OT_bundle_install";
/* api callbacks */
ot->exec = asset_bundle_install_exec;
ot->invoke = asset_bundle_install_invoke;
ot->poll = asset_bundle_install_poll;
ot->prop = RNA_def_property(ot->srna, "asset_library_ref", PROP_ENUM, PROP_NONE);
RNA_def_property_flag(ot->prop, PROP_HIDDEN);
RNA_def_enum_funcs(ot->prop, rna_asset_library_reference_itemf);
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_BLENDER,
FILE_BLENDER,
FILE_SAVE,
WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
}
/* Cheap check to see if this is an "asset bundle" just by checking main file name.
* A proper check will be done in the exec function, to ensure that no external files will be
* referenced. */
static bool could_be_asset_bundle(const Main *bmain)
{
return fnmatch("*_bundle.blend", bmain->filepath, FNM_CASEFOLD) == 0;
}
static const bUserAssetLibrary *selected_asset_library(struct wmOperator *op)
{
const int enum_value = RNA_enum_get(op->ptr, "asset_library_ref");
const AssetLibraryReference lib_ref = ED_asset_library_reference_from_enum_value(enum_value);
const bUserAssetLibrary *lib = BKE_preferences_asset_library_find_from_index(
&U, lib_ref.custom_library_index);
return lib;
}
static bool is_contained_in_selected_asset_library(struct wmOperator *op, const char *filepath)
{
const bUserAssetLibrary *lib = selected_asset_library(op);
if (!lib) {
return false;
}
return BLI_path_contains(lib->path, filepath);
}
/**
* Set the "filepath" RNA property based on selected "asset_library_ref".
* \return true if ok, false if error.
*/
static bool set_filepath_for_asset_lib(const Main *bmain, struct wmOperator *op)
{
/* Find the directory path of the selected asset library. */
const bUserAssetLibrary *lib = selected_asset_library(op);
if (lib == nullptr) {
return false;
}
/* Concatenate the filename of the current blend file. */
const char *blend_filename = BLI_path_basename(bmain->filepath);
if (blend_filename == nullptr || blend_filename[0] == '\0') {
return false;
}
char file_path[PATH_MAX];
BLI_join_dirfile(file_path, sizeof(file_path), lib->path, blend_filename);
RNA_string_set(op->ptr, "filepath", file_path);
return true;
}
struct FileCheckCallbackInfo {
struct ReportList *reports;
Set<std::string> external_files;
};
static bool external_file_check_callback(BPathForeachPathData *bpath_data,
char * /*path_dst*/,
const char *path_src)
{
FileCheckCallbackInfo *callback_info = static_cast<FileCheckCallbackInfo *>(
bpath_data->user_data);
callback_info->external_files.add(std::string(path_src));
return false;
}
/**
* Do a check on any external files (.blend, textures, etc.) being used.
* The ASSET_OT_bundle_install operator only works on standalone .blend files
* (catalog definition files are fine, though).
*
* \return true when there are external files, false otherwise.
*/
static bool has_external_files(Main *bmain, struct ReportList *reports)
{
struct FileCheckCallbackInfo callback_info = {reports, Set<std::string>()};
eBPathForeachFlag flag = static_cast<eBPathForeachFlag>(
BKE_BPATH_FOREACH_PATH_SKIP_PACKED /* Packed files are fine. */
| BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE /* Only report multi-files once, it's enough. */
| BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES); /* Only care about actually used files. */
BPathForeachPathData bpath_data = {
/* bmain */ bmain,
/* callback_function */ &external_file_check_callback,
/* flag */ flag,
/* user_data */ &callback_info,
/* absolute_base_path */ nullptr,
};
BKE_bpath_foreach_path_main(&bpath_data);
if (callback_info.external_files.is_empty()) {
/* No external dependencies. */
return false;
}
if (callback_info.external_files.size() == 1) {
/* Only one external dependency, report it directly. */
BKE_reportf(callback_info.reports,
RPT_ERROR,
"Unable to copy bundle due to external dependency: \"%s\"",
callback_info.external_files.begin()->c_str());
return true;
}
/* Multiple external dependencies, report the aggregate and put details on console. */
BKE_reportf(
callback_info.reports,
RPT_ERROR,
"Unable to copy bundle due to %zu external dependencies; more details on the console",
(size_t)callback_info.external_files.size());
printf("Unable to copy bundle due to %zu external dependencies:\n",
(size_t)callback_info.external_files.size());
for (const std::string &path : callback_info.external_files) {
printf(" \"%s\"\n", path.c_str());
}
return true;
}
/* -------------------------------------------------------------------- */
void ED_operatortypes_asset()
{
WM_operatortype_append(ASSET_OT_mark);
WM_operatortype_append(ASSET_OT_clear);
WM_operatortype_append(ASSET_OT_catalog_new);
WM_operatortype_append(ASSET_OT_catalog_delete);
WM_operatortype_append(ASSET_OT_catalogs_save);
WM_operatortype_append(ASSET_OT_catalog_undo);
WM_operatortype_append(ASSET_OT_catalog_redo);
WM_operatortype_append(ASSET_OT_catalog_undo_push);
WM_operatortype_append(ASSET_OT_bundle_install);
WM_operatortype_append(ASSET_OT_library_refresh);
}