From 9ca9af2ae43b127431e40c77e489ff911c5e7914 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 8 Mar 2022 15:51:08 +0100 Subject: LibOverride: Add hierarchy creation from IDTemplate UI widget. This is fairly tricky to perform, since there is often very limited contextual information available about the 'active' hierarchy to override. This commit is a first step, it is expected to handle decently well cases like obdata (recreating necessary object and collection hierarchy), at least in most common cases. --- .../editors/interface/interface_templates.c | 297 ++++++++++++++++++--- 1 file changed, 259 insertions(+), 38 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 43968e2c986..215198656a8 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -13,6 +13,7 @@ #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" #include "DNA_constraint_types.h" #include "DNA_curveprofile_types.h" #include "DNA_gpencil_modifier_types.h" @@ -587,6 +588,252 @@ void UI_context_active_but_prop_get_templateID(bContext *C, } } +static void template_id_liboverride_hierarchy_collection_root_find_recursive( + Collection *collection, + const int parent_level, + Collection **r_collection_parent_best, + int *r_parent_level_best) +{ + if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { + return; + } + if (ID_IS_OVERRIDABLE_LIBRARY(collection) || ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { + if (parent_level > *r_parent_level_best) { + *r_parent_level_best = parent_level; + *r_collection_parent_best = collection; + } + } + for (CollectionParent *iter = collection->parents.first; iter != NULL; iter = iter->next) { + if (iter->collection->id.lib != collection->id.lib && ID_IS_LINKED(iter->collection)) { + continue; + } + template_id_liboverride_hierarchy_collection_root_find_recursive( + iter->collection, parent_level + 1, r_collection_parent_best, r_parent_level_best); + } +} + +static void template_id_liboverride_hierarchy_collections_tag_recursive( + Collection *root_collection, ID *target_id, const bool do_parents) +{ + root_collection->id.tag |= LIB_TAG_DOIT; + + /* Tag all local parents of the root collection, so that usages of the root collection and other + * linked ones can be replaced by the local overrides in those parents too. */ + if (do_parents) { + for (CollectionParent *iter = root_collection->parents.first; iter != NULL; + iter = iter->next) { + if (ID_IS_LINKED(iter->collection)) { + continue; + } + iter->collection->id.tag |= LIB_TAG_DOIT; + } + } + + for (CollectionChild *iter = root_collection->children.first; iter != NULL; iter = iter->next) { + if (iter->collection->id.lib != root_collection->id.lib && ID_IS_LINKED(root_collection)) { + continue; + } + if (ID_IS_LINKED(iter->collection) && iter->collection->id.lib != target_id->lib) { + continue; + } + if (GS(target_id->name) == ID_OB && + !BKE_collection_has_object_recursive(iter->collection, (Object *)target_id)) { + continue; + } + if (GS(target_id->name) == ID_GR && + !BKE_collection_has_collection(iter->collection, (Collection *)target_id)) { + continue; + } + template_id_liboverride_hierarchy_collections_tag_recursive( + iter->collection, target_id, false); + } +} + +static void template_id_liboverride_hierarchy_create(bContext *C, + Main *bmain, + TemplateID *template_ui, + PointerRNA *idptr, + const char **r_undo_push_label) +{ + ID *id = idptr->data; + ID *owner_id = template_ui->ptr.owner_id; + + /* If the ID is directly linked, do not try to perform a hierarchy override, just override this + * single ID, remap this single usage to it, and in case current user is already a hierechy + * override, add the new override to this hierarchy. */ + if (ID_IS_OVERRIDABLE_LIBRARY(id) && !ID_IS_LINKED(owner_id)) { + /* Only remap that specific ID usage to overriding local data-block. */ + ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); + if (override_id != NULL) { + BKE_main_id_newptr_and_tag_clear(bmain); + + if (GS(override_id->name) == ID_OB) { + Scene *scene = CTX_data_scene(C); + if (!BKE_collection_has_object_recursive(scene->master_collection, + (Object *)override_id)) { + BKE_collection_object_add_from(bmain, scene, (Object *)id, (Object *)override_id); + } + } + + /* Assign new pointer, takes care of updates/notifiers. */ + if (owner_id == NULL || !ID_IS_LINKED(owner_id)) { + RNA_id_pointer_create(override_id, idptr); + } + /* Insert into override hierarchy if possible. */ + if (owner_id != NULL && !ID_IS_LINKED(owner_id) && ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) { + override_id->override_library->hierarchy_root = owner_id->override_library->hierarchy_root; + override_id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY; + } + } + *r_undo_push_label = "Make Library Override Single"; + return; + } + /* Otherwise, attempt to perform a hierarchy override, based on contextual data available. + * NOTE: do not attempt to perform such hierarchy override at all cost, if there is not enough + * context, better to abort than create random overrides all over the place. */ + if (ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id)) { + Object *object_active = CTX_data_active_object(C); + if (object_active == NULL && GS(owner_id->name) == ID_OB) { + object_active = (Object *)owner_id; + } + if (object_active != NULL) { + if (ID_IS_LINKED(object_active)) { + if (object_active->id.lib != id->lib || + !ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(object_active)) { + /* The active object is from a different library than the overriden ID, or otherwise + * cannot be used in hierarchy. */ + object_active = NULL; + } + } + else if (!ID_IS_OVERRIDE_LIBRARY_REAL(object_active)) { + object_active = NULL; + } + } + + Collection *collection = CTX_data_collection(C); + if (collection == NULL && GS(owner_id->name) == ID_GR) { + collection = (Collection *)owner_id; + } + if (collection != NULL) { + if (ID_IS_LINKED(collection)) { + if (collection->id.lib != id->lib || !ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(collection)) { + /* The active collection is from a different library than the overriden ID, or otherwise + * cannot be used in hierarchy. */ + collection = NULL; + } + else { + int parent_level_best = -1; + Collection *collection_parent_best = NULL; + template_id_liboverride_hierarchy_collection_root_find_recursive( + collection, 0, &collection_parent_best, &parent_level_best); + collection = collection_parent_best; + } + } + else if (!ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { + collection = NULL; + } + } + if (collection == NULL && object_active != NULL && + (ID_IS_LINKED(object_active) || ID_IS_OVERRIDE_LIBRARY_REAL(object_active))) { + LISTBASE_FOREACH (Collection *, collection_iter, &bmain->collections) { + if (!(ID_IS_LINKED(collection_iter) || ID_IS_OVERRIDE_LIBRARY_REAL(collection_iter))) { + continue; + } + if (!BKE_collection_has_object_recursive(collection_iter, object_active)) { + continue; + } + int parent_level_best = -1; + Collection *collection_parent_best = NULL; + template_id_liboverride_hierarchy_collection_root_find_recursive( + collection_iter, 0, &collection_parent_best, &parent_level_best); + collection = collection_parent_best; + break; + } + } + + ID *id_override = NULL; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + switch (GS(id->name)) { + case ID_GR: + if (collection != NULL && BKE_collection_has_collection(collection, (Collection *)id)) { + template_id_liboverride_hierarchy_collections_tag_recursive(collection, id, true); + if (object_active != NULL) { + object_active->id.tag |= LIB_TAG_DOIT; + } + BKE_lib_override_library_create( + bmain, scene, view_layer, NULL, id, &collection->id, NULL, &id_override); + } + else if (object_active != NULL && !ID_IS_LINKED(object_active) && + &object_active->instance_collection->id == id) { + object_active->id.tag |= LIB_TAG_DOIT; + BKE_lib_override_library_create(bmain, + scene, + view_layer, + id->lib, + id, + &object_active->id, + &object_active->id, + &id_override); + } + break; + case ID_OB: + if (collection != NULL && BKE_collection_has_object_recursive(collection, (Object *)id)) { + template_id_liboverride_hierarchy_collections_tag_recursive(collection, id, true); + if (object_active != NULL) { + object_active->id.tag |= LIB_TAG_DOIT; + } + BKE_lib_override_library_create( + bmain, scene, view_layer, NULL, id, &collection->id, NULL, &id_override); + } + break; + case ID_ME: + case ID_CU_LEGACY: + case ID_MB: + case ID_LT: + case ID_LA: + case ID_CA: + case ID_SPK: + case ID_AR: + case ID_GD: + case ID_CV: + case ID_PT: + case ID_VO: + if (object_active != NULL && object_active->data == id) { + if (collection != NULL && + BKE_collection_has_object_recursive(collection, object_active)) { + template_id_liboverride_hierarchy_collections_tag_recursive(collection, id, true); + if (object_active != NULL) { + object_active->id.tag |= LIB_TAG_DOIT; + } + BKE_lib_override_library_create( + bmain, scene, view_layer, NULL, id, &collection->id, NULL, &id_override); + } + else { + object_active->id.tag |= LIB_TAG_DOIT; + BKE_lib_override_library_create( + bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override); + } + } + break; + case ID_MA: + case ID_TE: + case ID_IM: + break; + case ID_WO: + break; + case ID_PA: + break; + default: + break; + } + if (id_override != NULL) { + id_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED; + *r_undo_push_label = "Make Library Override Hierarchy"; + } + } +} + static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) { TemplateID *template_ui = (TemplateID *)arg_litem; @@ -636,33 +883,8 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) if (id) { Main *bmain = CTX_data_main(C); if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - if (ID_IS_OVERRIDABLE_LIBRARY(id)) { - /* Only remap that specific ID usage to overriding local data-block. */ - ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); - if (override_id != NULL) { - BKE_main_id_newptr_and_tag_clear(bmain); - - if (GS(override_id->name) == ID_OB) { - Scene *scene = CTX_data_scene(C); - if (!BKE_collection_has_object_recursive(scene->master_collection, - (Object *)override_id)) { - BKE_collection_object_add_from( - bmain, scene, (Object *)id, (Object *)override_id); - } - } - - /* Assign new pointer, takes care of updates/notifiers */ - RNA_id_pointer_create(override_id, &idptr); - /* Insert into override hierarchy if possible. */ - ID *owner_id = template_ui->ptr.owner_id; - if (owner_id != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) { - override_id->override_library->hierarchy_root = - owner_id->override_library->hierarchy_root; - owner_id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY; - } - } - undo_push_label = "Make Library Override"; - } + template_id_liboverride_hierarchy_create( + C, bmain, template_ui, &idptr, &undo_push_label); } else { if (BKE_lib_id_make_local(bmain, id, 0)) { @@ -1005,6 +1227,7 @@ static void template_ID(const bContext *C, } if (ID_IS_LINKED(id)) { + const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name)); if (id->tag & LIB_TAG_INDIRECT) { but = uiDefIconBut(block, UI_BTYPE_BUT, @@ -1019,12 +1242,10 @@ static void template_ID(const bContext *C, 0, 0, 0, - TIP_("Indirect library data-block, cannot change")); - UI_but_flag_enable(but, UI_BUT_DISABLED); + TIP_("Indirect library data-block, cannot be made local, " + "Shift + Click to create a library override hierarchy")); } else { - const bool disabled = (!BKE_idtype_idcode_is_localizable(GS(id->name)) || - (idfrom && idfrom->lib)); but = uiDefIconBut(block, UI_BTYPE_BUT, 0, @@ -1040,13 +1261,13 @@ static void template_ID(const bContext *C, 0, TIP_("Direct linked library data-block, click to make local, " "Shift + Click to create a library override")); - if (disabled) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - else { - UI_but_funcN_set( - but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL)); - } + } + if (disabled) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + else { + UI_but_funcN_set( + but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL)); } } else if (ID_IS_OVERRIDE_LIBRARY(id)) { -- cgit v1.2.3