diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_asset_catalog.hh | 14 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/asset_catalog.cc | 26 | ||||
-rw-r--r-- | source/blender/blenloader/BLO_writefile.h | 8 | ||||
-rw-r--r-- | source/blender/editors/asset/ED_asset_library.h | 3 | ||||
-rw-r--r-- | source/blender/editors/asset/intern/asset_library_reference_enum.cc | 33 | ||||
-rw-r--r-- | source/blender/editors/asset/intern/asset_ops.cc | 246 | ||||
-rw-r--r-- | source/blender/editors/include/ED_fileselect.h | 1 | ||||
-rw-r--r-- | source/blender/editors/space_file/file_panels.c | 16 | ||||
-rw-r--r-- | source/blender/editors/space_file/filesel.c | 9 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_asset.c | 2 | ||||
-rw-r--r-- | source/blender/windowmanager/WM_api.h | 2 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_event_system.c | 10 |
12 files changed, 353 insertions, 17 deletions
diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh index 3478eebbeb4..d071f782bd6 100644 --- a/source/blender/blenkernel/BKE_asset_catalog.hh +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -98,6 +98,15 @@ class AssetCatalogService { bool write_to_disk(const CatalogFilePath &blend_file_path); /** + * Ensure that the next call to #on_blend_save_post() will choose a new location for the CDF + * suitable for the location of the blend file (regardless of where the current catalogs come + * from), and that catalogs will be merged with already-existing ones in that location. + * + * Use this for a "Save as..." that has to write the catalogs to the new blend file location, + * instead of updating the previously read CDF. */ + void prepare_to_merge_on_write(); + + /** * Merge on-disk changes into the in-memory asset catalogs. * This should be called before writing the asset catalogs to disk. * @@ -238,6 +247,11 @@ class AssetCatalogService { */ void create_missing_catalogs(); + /** + * For every catalog, mark it as "dirty". + */ + void tag_all_catalogs_as_unsaved_changes(); + /* For access by subclasses, as those will not be marked as friend by #AssetCatalogCollection. */ AssetCatalogDefinitionFile *get_catalog_definition_file(); OwningAssetCatalogMap &get_catalogs(); diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc index 03043f3b784..9ef66d23aea 100644 --- a/source/blender/blenkernel/intern/asset_catalog.cc +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -98,6 +98,14 @@ bool AssetCatalogService::has_unsaved_changes() const return catalog_collection_->has_unsaved_changes_; } +void AssetCatalogService::tag_all_catalogs_as_unsaved_changes() +{ + for (auto &catalog : catalog_collection_->catalogs_.values()) { + catalog->flags.has_unsaved_changes = true; + } + catalog_collection_->has_unsaved_changes_ = true; +} + bool AssetCatalogService::is_empty() const { BLI_assert(catalog_collection_); @@ -486,6 +494,24 @@ bool AssetCatalogService::write_to_disk_ex(const CatalogFilePath &blend_file_pat return catalog_collection_->catalog_definition_file_->write_to_disk(); } +void AssetCatalogService::prepare_to_merge_on_write() +{ + /* TODO(Sybren): expand to support multiple CDFs. */ + + if (!catalog_collection_->catalog_definition_file_) { + /* There is no CDF connected, so it's a no-op. */ + return; + } + + /* Remove any association with the CDF, so that a new location will be chosen + * when the blend file is saved. */ + catalog_collection_->catalog_definition_file_.reset(); + + /* Mark all in-memory catalogs as "dirty", to force them to be kept around on + * the next "load-merge-write" cycle. */ + tag_all_catalogs_as_unsaved_changes(); +} + CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( const CatalogFilePath &blend_file_path) { diff --git a/source/blender/blenloader/BLO_writefile.h b/source/blender/blenloader/BLO_writefile.h index 746c663926d..bac0119a368 100644 --- a/source/blender/blenloader/BLO_writefile.h +++ b/source/blender/blenloader/BLO_writefile.h @@ -24,6 +24,10 @@ * \brief external writefile function prototypes. */ +#ifdef __cplusplus +extern "C" { +#endif + struct BlendThumbnail; struct Main; struct MemFile; @@ -72,3 +76,7 @@ extern bool BLO_write_file_mem(struct Main *mainvar, int write_flags); /** \} */ + +#ifdef __cplusplus +} +#endif 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 1a2d3f5837a..a97af1b1cf4 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -98,25 +98,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 f7c567c89f6..5e4fab6a14f 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -19,12 +19,19 @@ */ #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 "ED_asset.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 +40,10 @@ #include "WM_api.h" +#include "DNA_space_types.h" + +#include "BLO_writefile.h" + using namespace blender; /* -------------------------------------------------------------------- */ @@ -643,6 +654,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); @@ -654,6 +899,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); } diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 68b6e44371c..c1936b2fde5 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -109,6 +109,7 @@ struct FileSelectParams *ED_fileselect_ensure_active_params(struct SpaceFile *sf struct FileSelectParams *ED_fileselect_get_active_params(const struct SpaceFile *sfile); struct FileSelectParams *ED_fileselect_get_file_params(const struct SpaceFile *sfile); struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceFile *sfile); +bool ED_fileselect_is_local_asset_library(const struct SpaceFile *sfile); void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 0e468718a04..540d4729ed6 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -41,6 +41,7 @@ #include "ED_fileselect.h" #include "UI_interface.h" +#include "UI_interface_icons.h" #include "UI_resources.h" #include "WM_api.h" @@ -246,7 +247,20 @@ static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *pane RNA_pointer_create(&screen->id, &RNA_FileAssetSelectParams, params, ¶ms_ptr); uiItemR(row, ¶ms_ptr, "asset_library_ref", 0, "", ICON_NONE); - if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) { + if (params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) { + bContext *mutable_ctx = CTX_copy(C); + if (WM_operator_name_poll(mutable_ctx, "asset.bundle_install")) { + uiItemS(col); + uiItemMenuEnumO(col, + mutable_ctx, + "asset.bundle_install", + "asset_library_ref", + "Copy Bundle to Asset Library...", + ICON_IMPORT); + } + CTX_free(mutable_ctx); + } + else { uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh"); } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 11757975a62..c59398e0016 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -411,6 +411,15 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile) return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) ? sfile->asset_params : NULL; } +bool ED_fileselect_is_local_asset_library(const SpaceFile *sfile) +{ + const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + if (asset_params == NULL) { + return false; + } + return asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL; +} + static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) { AssetLibraryReference *library = &asset_params->asset_library_ref; diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 5d83da170b5..0d25a0ba079 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -307,7 +307,7 @@ const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C), PropertyRNA *UNUSED(prop), bool *r_free) { - const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(); + const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(true); if (!items) { *r_free = false; } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 8d25ece3753..c41c328c006 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -470,6 +470,8 @@ int WM_operator_repeat(struct bContext *C, struct wmOperator *op); int WM_operator_repeat_last(struct bContext *C, struct wmOperator *op); bool WM_operator_repeat_check(const struct bContext *C, struct wmOperator *op); bool WM_operator_is_repeat(const struct bContext *C, const struct wmOperator *op); + +bool WM_operator_name_poll(struct bContext *C, const char *opstring); int WM_operator_name_call_ptr(struct bContext *C, struct wmOperatorType *ot, wmOperatorCallContext context, diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f51c8c48c48..474d900a53d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -1614,6 +1614,16 @@ int WM_operator_name_call(bContext *C, return 0; } +bool WM_operator_name_poll(bContext *C, const char *opstring) +{ + wmOperatorType *ot = WM_operatortype_find(opstring, 0); + if (!ot) { + return false; + } + + return WM_operator_poll(C, ot); +} + int WM_operator_name_call_with_properties(struct bContext *C, const char *opstring, wmOperatorCallContext context, |