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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/asset/intern/asset_ops.cc')
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc330
1 files changed, 317 insertions, 13 deletions
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index f7c567c89f6..e4edff19a21 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -19,12 +19,23 @@
*/
#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"
@@ -33,6 +44,10 @@
#include "WM_api.h"
+#include "DNA_space_types.h"
+
+#include "BLO_writefile.h"
+
using namespace blender;
/* -------------------------------------------------------------------- */
@@ -377,8 +392,14 @@ static void ASSET_OT_clear(wmOperatorType *ot)
/* -------------------------------------------------------------------- */
-static bool asset_list_refresh_poll(bContext *C)
+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;
@@ -387,23 +408,38 @@ static bool asset_list_refresh_poll(bContext *C)
return ED_assetlist_storage_has_list_for_library(library);
}
-static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
+static int asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
{
- const AssetLibraryReference *library = CTX_wm_asset_library_ref(C);
- ED_assetlist_clear(library, C);
+ /* 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;
}
-static void ASSET_OT_list_refresh(struct wmOperatorType *ot)
+/**
+ * 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 List";
- ot->description = "Trigger a reread of the assets";
- ot->idname = "ASSET_OT_list_refresh";
+ 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_list_refresh_exec;
- ot->poll = asset_list_refresh_poll;
+ ot->exec = asset_library_refresh_exec;
+ ot->poll = asset_library_refresh_poll;
}
/* -------------------------------------------------------------------- */
@@ -601,7 +637,7 @@ static bool asset_catalogs_save_poll(bContext *C)
}
const Main *bmain = CTX_data_main(C);
- if (!bmain->name[0]) {
+ if (!bmain->filepath[0]) {
CTX_wm_operator_poll_msg_set(C, "Cannot save asset catalogs before the Blender file is saved");
return false;
}
@@ -643,7 +679,274 @@ static void ASSET_OT_catalogs_save(struct wmOperatorType *ot)
/* -------------------------------------------------------------------- */
-void ED_operatortypes_asset(void)
+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 %ld external dependencies; more details on the console",
+ callback_info.external_files.size());
+ printf("Unable to copy bundle due to %ld external dependencies:\n",
+ 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);
@@ -654,6 +957,7 @@ void ED_operatortypes_asset(void)
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_list_refresh);
+ WM_operatortype_append(ASSET_OT_library_refresh);
}