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:
authorSybren A. Stüvel <sybren@blender.org>2021-11-23 14:29:44 +0300
committerSybren A. Stüvel <sybren@blender.org>2021-11-23 14:29:44 +0300
commitcd2849c89b65131cc381a1bf9ab75527afd51a4f (patch)
treebac399cca89d942f7c1fd384ff003975887e03c0 /source/blender/editors/asset
parentb02ac2d8be38d198edfece7347919d67f7cc7275 (diff)
Asset Browser: add operator for installing asset bundles
Add an operator "Copy to Asset Library" for installing asset bundles into already-existing asset libraries. The operator is shown when: - the "Current File" library is selected, - the blend file name matches `*_bundle.blend`, and - the file is not already located in an asset library. The user can select a target asset library, then gets a "Save As" dialogue box to select where in that library the file should be saved. This allows for renaming, saving in a subdirectory, etc. The Asset Catalogs from the asset bundle are merged into the target asset library. The operator will refuse to run when external files are referenced. This is not done in its poll function, as it's quite an extensive operator (it loops over all ID datablocks). Reviewed by: Severin Differential Revision: https://developer.blender.org/D13312
Diffstat (limited to 'source/blender/editors/asset')
-rw-r--r--source/blender/editors/asset/ED_asset_library.h3
-rw-r--r--source/blender/editors/asset/intern/asset_library_reference_enum.cc33
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc246
3 files changed, 267 insertions, 15 deletions
diff --git a/source/blender/editors/asset/ED_asset_library.h b/source/blender/editors/asset/ED_asset_library.h
index 905d097d223..cb055584364 100644
--- a/source/blender/editors/asset/ED_asset_library.h
+++ b/source/blender/editors/asset/ED_asset_library.h
@@ -28,7 +28,8 @@ extern "C" {
int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library);
AssetLibraryReference ED_asset_library_reference_from_enum_value(int value);
-const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(void);
+const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
+ bool include_local_library);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc
index c57d121a18f..4c343e1e0aa 100644
--- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc
+++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc
@@ -99,25 +99,30 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
*
* Since this is meant for UI display, skips non-displayable libraries, that is, libraries with an
* empty name or path.
+ *
+ * \param include_local_library whether to include the "Current File" library or not.
*/
-const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf()
+const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
+ const bool include_local_library)
{
- const EnumPropertyItem predefined_items[] = {
- /* For the future. */
- // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
- {ASSET_LIBRARY_LOCAL,
- "LOCAL",
- ICON_BLENDER,
- "Current File",
- "Show the assets currently available in this Blender session"},
- {0, nullptr, 0, nullptr, nullptr},
- };
-
EnumPropertyItem *item = nullptr;
int totitem = 0;
- /* Add predefined items. */
- RNA_enum_items_add(&item, &totitem, predefined_items);
+ if (include_local_library) {
+ const EnumPropertyItem predefined_items[] = {
+ /* For the future. */
+ // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
+ {ASSET_LIBRARY_LOCAL,
+ "LOCAL",
+ ICON_BLENDER,
+ "Current File",
+ "Show the assets currently available in this Blender session"},
+ {0, nullptr, 0, nullptr, nullptr},
+ };
+
+ /* Add predefined items. */
+ RNA_enum_items_add(&item, &totitem, predefined_items);
+ }
/* Add separator if needed. */
if (!BLI_listbase_is_empty(&U.asset_libraries)) {
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index d2fd8ab88a4..dccea2c01ea 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -21,16 +21,23 @@
#include "BKE_asset.h"
#include "BKE_asset_catalog.hh"
#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_string_ref.hh"
#include "BLI_vector.hh"
#include "ED_asset.h"
#include "ED_asset_catalog.hh"
+#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"
@@ -40,6 +47,10 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "DNA_space_types.h"
+
+#include "BLO_writefile.h"
+
using namespace blender;
/* -------------------------------------------------------------------- */
@@ -650,6 +661,240 @@ static void ASSET_OT_catalogs_save(struct wmOperatorType *ot)
/* -------------------------------------------------------------------- */
+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 (!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->name);
+ 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,
+ "Saved \"%s\" to asset library \"%s\"",
+ BLI_path_basename(bmain->name),
+ 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->name, 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->name);
+ if (blend_filename == NULL || 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;
+ bool external_file_found;
+};
+
+static bool external_file_check_callback(void *callback_info_ptr,
+ char * /*path_dst*/,
+ const char *path_src)
+{
+ FileCheckCallbackInfo *callback_info = static_cast<FileCheckCallbackInfo *>(callback_info_ptr);
+ BKE_reportf(callback_info->reports,
+ RPT_ERROR,
+ "Unable to install asset bundle, has external dependency \"%s\"",
+ path_src);
+ callback_info->external_file_found = true;
+ return false;
+}
+
+/**
+ * Do a check on any external files (.blend, textures, etc.) being used.
+ * The "Install asset bundle" 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, false};
+
+ BKE_bpath_traverse_main(
+ bmain,
+ &external_file_check_callback,
+ BKE_BPATH_TRAVERSE_SKIP_PACKED /* Packed files are fine. */
+ | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE /* Only report multifiles once, it's enough. */,
+ &callback_info);
+ return callback_info.external_file_found;
+}
+
+/* -------------------------------------------------------------------- */
+
void ED_operatortypes_asset(void)
{
WM_operatortype_append(ASSET_OT_mark);
@@ -661,6 +906,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);
}