/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup RNA */ #include #include "RNA_define.h" #include "RNA_enum_types.h" #include "DNA_asset_types.h" #include "DNA_defs.h" #include "DNA_space_types.h" #include "rna_internal.h" #ifdef RNA_RUNTIME # include "BKE_asset.h" # include "BKE_asset_library.h" # include "BKE_context.h" # include "BKE_idprop.h" # include "BLI_listbase.h" # include "BLI_uuid.h" # include "ED_asset.h" # include "ED_fileselect.h" # include "RNA_access.h" static bool rna_AssetMetaData_editable_from_owner_id(const ID *owner_id, const AssetMetaData *asset_data, const char **r_info) { if (owner_id && asset_data && (owner_id->asset_data == asset_data)) { return true; } if (r_info) { *r_info = "Asset metadata from external asset libraries can't be edited, only assets stored in the " "current file can"; } return false; } int rna_AssetMetaData_editable(PointerRNA *ptr, const char **r_info) { AssetMetaData *asset_data = ptr->data; return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, asset_data, r_info) ? PROP_EDITABLE : 0; } static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info) { AssetTag *asset_tag = ptr->data; ID *owner_id = ptr->owner_id; if (owner_id && owner_id->asset_data) { BLI_assert_msg(BLI_findindex(&owner_id->asset_data->tags, asset_tag) != -1, "The owner of the asset tag pointer is not the asset ID containing the tag"); UNUSED_VARS_NDEBUG(asset_tag); } return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, owner_id->asset_data, r_info) ? PROP_EDITABLE : 0; } static AssetTag *rna_AssetMetaData_tag_new( ID *id, AssetMetaData *asset_data, ReportList *reports, const char *name, bool skip_if_exists) { const char *disabled_info = NULL; if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) { BKE_report(reports, RPT_WARNING, disabled_info); return NULL; } AssetTag *tag = NULL; if (skip_if_exists) { struct AssetTagEnsureResult result = BKE_asset_metadata_tag_ensure(asset_data, name); if (!result.is_new) { BKE_reportf( reports, RPT_WARNING, "Tag '%s' already present for given asset", result.tag->name); /* Report, but still return valid item. */ } tag = result.tag; } else { tag = BKE_asset_metadata_tag_add(asset_data, name); } return tag; } static void rna_AssetMetaData_tag_remove(ID *id, AssetMetaData *asset_data, ReportList *reports, PointerRNA *tag_ptr) { const char *disabled_info = NULL; if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) { BKE_report(reports, RPT_WARNING, disabled_info); return; } AssetTag *tag = tag_ptr->data; if (BLI_findindex(&asset_data->tags, tag) == -1) { BKE_reportf(reports, RPT_ERROR, "Tag '%s' not found in given asset", tag->name); return; } BKE_asset_metadata_tag_remove(asset_data, tag); RNA_POINTER_INVALIDATE(tag_ptr); } static IDProperty **rna_AssetMetaData_idprops(PointerRNA *ptr) { AssetMetaData *asset_data = ptr->data; return &asset_data->properties; } static void rna_AssetMetaData_author_get(PointerRNA *ptr, char *value) { AssetMetaData *asset_data = ptr->data; if (asset_data->author) { strcpy(value, asset_data->author); } else { value[0] = '\0'; } } static int rna_AssetMetaData_author_length(PointerRNA *ptr) { AssetMetaData *asset_data = ptr->data; return asset_data->author ? strlen(asset_data->author) : 0; } static void rna_AssetMetaData_author_set(PointerRNA *ptr, const char *value) { AssetMetaData *asset_data = ptr->data; if (asset_data->author) { MEM_freeN(asset_data->author); } if (value[0]) { asset_data->author = BLI_strdup(value); } else { asset_data->author = NULL; } } static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value) { AssetMetaData *asset_data = ptr->data; if (asset_data->description) { strcpy(value, asset_data->description); } else { value[0] = '\0'; } } static int rna_AssetMetaData_description_length(PointerRNA *ptr) { AssetMetaData *asset_data = ptr->data; return asset_data->description ? strlen(asset_data->description) : 0; } static void rna_AssetMetaData_description_set(PointerRNA *ptr, const char *value) { AssetMetaData *asset_data = ptr->data; if (asset_data->description) { MEM_freeN(asset_data->description); } if (value[0]) { asset_data->description = BLI_strdup(value); } else { asset_data->description = NULL; } } static void rna_AssetMetaData_active_tag_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { const AssetMetaData *asset_data = ptr->data; *min = *softmin = 0; *max = *softmax = MAX2(asset_data->tot_tags - 1, 0); } static void rna_AssetMetaData_catalog_id_get(PointerRNA *ptr, char *value) { const AssetMetaData *asset_data = ptr->data; BLI_uuid_format(value, asset_data->catalog_id); } static int rna_AssetMetaData_catalog_id_length(PointerRNA *UNUSED(ptr)) { return UUID_STRING_LEN - 1; } static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value) { AssetMetaData *asset_data = ptr->data; bUUID new_uuid; if (value[0] == '\0') { BKE_asset_metadata_catalog_id_clear(asset_data); return; } if (!BLI_uuid_parse_string(&new_uuid, value)) { /* TODO(@sybren): raise ValueError exception once that's possible from an RNA setter. */ printf("UUID %s not formatted correctly, ignoring new value\n", value); return; } /* This just sets the new UUID and clears the catalog simple name. The actual * catalog simple name will be updated by some update function, as it * needs the asset library from the context. */ /* TODO(Sybren): write that update function. */ BKE_asset_metadata_catalog_id_set(asset_data, new_uuid, ""); } void rna_AssetMetaData_catalog_id_update(struct bContext *C, struct PointerRNA *ptr) { SpaceFile *sfile = CTX_wm_space_file(C); if (sfile == NULL) { /* Until there is a proper Asset Service available, it's only possible to get the asset library * from within the asset browser context. */ return; } AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); if (asset_library == NULL) { /* The SpaceFile may not be an asset browser but a regular file browser. */ return; } AssetMetaData *asset_data = ptr->data; BKE_asset_library_refresh_catalog_simplename(asset_library, asset_data); } static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr) { AssetHandle *asset_handle = ptr->data; /* Have to cast away const, but the file entry API doesn't allow modifications anyway. */ return rna_pointer_inherit_refine( ptr, &RNA_FileSelectEntry, (FileDirEntry *)asset_handle->file_data); } static void rna_AssetHandle_file_data_set(PointerRNA *ptr, PointerRNA value, struct ReportList *UNUSED(reports)) { AssetHandle *asset_handle = ptr->data; asset_handle->file_data = value.data; } static void rna_AssetHandle_get_full_library_path( // AssetHandle *asset, bContext *C, FileDirEntry *asset_file, AssetLibraryReference *library, char r_result[/*FILE_MAX_LIBEXTRA*/]) { AssetHandle asset = {.file_data = asset_file}; ED_asset_handle_get_full_library_path(C, library, &asset, r_result); } static PointerRNA rna_AssetHandle_local_id_get(PointerRNA *ptr) { const AssetHandle *asset = ptr->data; ID *id = ED_asset_handle_get_local_id(asset); return rna_pointer_inherit_refine(ptr, &RNA_ID, id); } 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(true); if (!items) { *r_free = false; } *r_free = true; return items; } #else static void rna_def_asset_tag(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "AssetTag", NULL); RNA_def_struct_ui_text(srna, "Asset Tag", "User defined tag (name token)"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_editable_func(prop, "rna_AssetTag_editable"); RNA_def_property_string_maxlength(prop, MAX_NAME); RNA_def_property_ui_text(prop, "Name", "The identifier that makes up this tag"); RNA_def_struct_name_property(srna, prop); } static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; FunctionRNA *func; PropertyRNA *parm; RNA_def_property_srna(cprop, "AssetTags"); srna = RNA_def_struct(brna, "AssetTags", NULL); RNA_def_struct_sdna(srna, "AssetMetaData"); RNA_def_struct_ui_text(srna, "Asset Tags", "Collection of custom asset tags"); /* Tag collection */ func = RNA_def_function(srna, "new", "rna_AssetMetaData_tag_new"); RNA_def_function_ui_description(func, "Add a new tag to this asset"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_boolean(func, "skip_if_exists", false, "Skip if Exists", "Do not add a new tag if one of the same type already exists"); /* return type */ parm = RNA_def_pointer(func, "tag", "AssetTag", "", "New tag"); RNA_def_function_return(func, parm); func = RNA_def_function(srna, "remove", "rna_AssetMetaData_tag_remove"); RNA_def_function_ui_description(func, "Remove an existing tag from this asset"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); /* tag to remove */ parm = RNA_def_pointer(func, "tag", "AssetTag", "", "Removed tag"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); } static void rna_def_asset_data(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "AssetMetaData", NULL); RNA_def_struct_ui_text(srna, "Asset Data", "Additional data stored for an asset data-block"); // RNA_def_struct_ui_icon(srna, ICON_ASSET); /* TODO: Icon doesn't exist! */ /* The struct has custom properties, but no pointer properties to other IDs! */ RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops"); RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE); RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_string_funcs(prop, "rna_AssetMetaData_author_get", "rna_AssetMetaData_author_length", "rna_AssetMetaData_author_set"); RNA_def_property_ui_text(prop, "Author", "Name of the creator of the asset"); prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_string_funcs(prop, "rna_AssetMetaData_description_get", "rna_AssetMetaData_description_length", "rna_AssetMetaData_description_set"); RNA_def_property_ui_text( prop, "Description", "A description of the asset to be displayed for the user"); prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "AssetTag"); RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_ui_text(prop, "Tags", "Custom tags (name tokens) for the asset, used for filtering and " "general asset management"); rna_def_asset_tags_api(brna, prop); prop = RNA_def_property(srna, "active_tag", PROP_INT, PROP_NONE); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_AssetMetaData_active_tag_range"); RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing"); prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs(prop, "rna_AssetMetaData_catalog_id_get", "rna_AssetMetaData_catalog_id_length", "rna_AssetMetaData_catalog_id_set"); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, 0, "rna_AssetMetaData_catalog_id_update"); RNA_def_property_ui_text(prop, "Catalog UUID", "Identifier for the asset's catalog, used by Blender to look up the " "asset's catalog path. Must be a UUID according to RFC4122"); prop = RNA_def_property(srna, "catalog_simple_name", PROP_STRING, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Catalog Simple Name", "Simple name of the asset's catalog, for debugging and " "data recovery purposes"); } static void rna_def_asset_handle_api(StructRNA *srna) { FunctionRNA *func; PropertyRNA *parm; func = RNA_def_function(srna, "get_full_library_path", "rna_AssetHandle_get_full_library_path"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); /* TODO temporarily static function, for until .py can receive the asset handle from context * properly. `asset_file_handle` should go away too then. */ RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer(func, "asset_library_ref", "AssetLibraryReference", "", "The asset library containing the given asset, only valid if the asset " "library is external (i.e. not the \"Current File\" one"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_string(func, "result", NULL, FILE_MAX_LIBEXTRA, "result", ""); RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0); RNA_def_function_output(func, parm); } static void rna_def_asset_handle(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "AssetHandle", "PropertyGroup"); RNA_def_struct_ui_text(srna, "Asset Handle", "Reference to some asset"); /* TODO It is super ugly to expose the file data here. We have to do it though so the asset view * template can populate a RNA collection with asset-handles, which are just file entries * currently. A proper design is being worked on. */ prop = RNA_def_property(srna, "file_data", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "FileSelectEntry"); RNA_def_property_pointer_funcs( prop, "rna_AssetHandle_file_data_get", "rna_AssetHandle_file_data_set", NULL, NULL); RNA_def_property_ui_text( prop, "File Entry", "TEMPORARY, DO NOT USE - File data used to refer to the asset"); prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "ID"); RNA_def_property_pointer_funcs(prop, "rna_AssetHandle_local_id_get", NULL, NULL, NULL); RNA_def_property_ui_text(prop, "", "The local data-block this asset represents; only valid if that is a " "data-block in this file"); RNA_def_property_flag(prop, PROP_HIDDEN); rna_def_asset_handle_api(srna); } static void rna_def_asset_catalog_path(BlenderRNA *brna) { StructRNA *srna = RNA_def_struct(brna, "AssetCatalogPath", NULL); RNA_def_struct_ui_text(srna, "Catalog Path", ""); } static void rna_def_asset_library_reference(BlenderRNA *brna) { StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL); RNA_def_struct_ui_text( srna, "Asset Library Reference", "Identifier to refer to the asset library"); } PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set) { PropertyRNA *prop = RNA_def_property(srna, "asset_library_ref", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, DummyRNA_NULL_items); RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf"); return prop; } void RNA_def_asset(BlenderRNA *brna) { RNA_define_animate_sdna(false); rna_def_asset_tag(brna); rna_def_asset_data(brna); rna_def_asset_library_reference(brna); rna_def_asset_handle(brna); rna_def_asset_catalog_path(brna); RNA_define_animate_sdna(true); } #endif