From e7ea99af1b0d933b9323be39d8b1c6f683bfba52 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 26 Feb 2019 16:15:30 -0300 Subject: Outliner: Collection - Duplicate Hierarchy, and Duplicate Linked Hierarchy As per the suggestion on T57064, this introduces two new options to duplicate collections. We then have: * Duplicate > Collection (New collection with linked content). * Duplicate > Hierachy (Duplicate entire hierarchy and make all contents single user). * Duplicate > Linked Hierarchy (Duplicate entire hierarchy keeping content linked with original). Development TODO: `single_object_users` can/should use the new functions. Reviewers: brecht, mont29 Subscribers: pablovazquez, billreynish, JulienKaspar Differential Revision: https://developer.blender.org/D4394 --- release/scripts/startup/bl_ui/space_outliner.py | 14 +- source/blender/blenkernel/BKE_collection.h | 6 +- source/blender/blenkernel/BKE_object.h | 2 + source/blender/blenkernel/intern/collection.c | 65 +++++- source/blender/blenkernel/intern/object.c | 234 +++++++++++++++++++++ source/blender/editors/object/object_add.c | 214 +------------------ .../editors/space_outliner/outliner_collections.c | 36 +++- .../editors/space_outliner/outliner_intern.h | 2 + .../blender/editors/space_outliner/outliner_ops.c | 2 + 9 files changed, 354 insertions(+), 221 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 33c68d50d56..159a97e8625 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -157,6 +157,17 @@ class OUTLINER_MT_collection_view_layer(Menu): layout.operator("outliner.collection_holdout_clear") +class OUTLINER_MT_collection_duplicate(Menu): + bl_label = "Duplicate" + + def draw(self, context): + layout = self.layout + + layout.operator("outliner.collection_duplicate", text="Collection") + layout.operator("outliner.collection_duplicate_hierarchy", text="Hierarchy") + layout.operator("outliner.collection_duplicate_linked_hierarchy", text="Linked Hierarchy") + + class OUTLINER_MT_collection_visibility(Menu): bl_label = "Visibility" @@ -192,7 +203,7 @@ class OUTLINER_MT_collection(Menu): space = context.space_data layout.operator("outliner.collection_new", text="New").nested = True - layout.operator("outliner.collection_duplicate", text="Duplicate") + layout.menu("OUTLINER_MT_collection_duplicate") layout.separator() @@ -345,6 +356,7 @@ classes = ( OUTLINER_MT_editor_menus, OUTLINER_MT_edit_datablocks, OUTLINER_MT_collection, + OUTLINER_MT_collection_duplicate, OUTLINER_MT_collection_new, OUTLINER_MT_collection_visibility, OUTLINER_MT_collection_view_layer, diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 2e4d2a7bf2c..5e6f87e7f97 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -54,11 +54,13 @@ void BKE_collection_free(struct Collection *collection); bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy); struct Collection *BKE_collection_copy(struct Main *bmain, struct Collection *parent, struct Collection *collection); -struct Collection *BKE_collection_copy_master(struct Main *bmain, struct Collection *collection, const int flag); void BKE_collection_copy_data(struct Main *bmain, struct Collection *collection_dst, const struct Collection *collection_src, const int flag); -void BKE_collection_copy_full(struct Main *bmain, struct Collection *collection); void BKE_collection_make_local(struct Main *bmain, struct Collection *collection, const bool lib_local); +struct Collection *BKE_collection_duplicate(struct Main *bmain, struct Collection *parent, struct Collection *collection, const bool do_hierarchy, const bool do_deep_copy); +struct Collection *BKE_collection_copy_master(struct Main *bmain, struct Collection *collection, const int flag); +void BKE_collection_copy_full(struct Main *bmain, struct Collection *collection); + /* Master Collection for Scene */ struct Collection *BKE_collection_master(const struct Scene *scene); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 58d648b4aa4..32bc2f03b9e 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -126,6 +126,8 @@ void BKE_object_make_local_ex(struct Main *bmain, struct Object *ob, const bool bool BKE_object_is_libdata(const struct Object *ob); bool BKE_object_obdata_is_libdata(const struct Object *ob); +struct Object *BKE_object_duplicate(struct Main *bmain, const struct Object *ob, const int dupflag); + void BKE_object_obdata_size_init(struct Object *ob, const float scale); void BKE_object_scale_to_mat3(struct Object *ob, float mat[3][3]); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 37ee6fbeaae..ce7a5b141a4 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -209,17 +209,72 @@ void BKE_collection_copy_data( } } +static void collection_duplicate_recursive(Main *bmain, GHash *visited, Collection *collection, const int dupflag) +{ + const bool is_first_run = (visited == NULL); + if (is_first_run) { + visited = BLI_ghash_ptr_new(__func__); + BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false); + } + + if (collection->id.tag & LIB_TAG_DOIT) { + return; + } + collection->id.tag |= LIB_TAG_DOIT; + + ListBase collection_object_list = {NULL, NULL}; + BLI_duplicatelist(&collection_object_list, &collection->gobject); + for (CollectionObject *cob = collection_object_list.first; cob; cob = cob->next) { + Object *ob_old = cob->ob; + Object *ob_new = NULL; + void **ob_key_p, **ob_value_p; + + if (!BLI_ghash_ensure_p_ex(visited, ob_old, &ob_key_p, &ob_value_p)) { + ob_new = BKE_object_duplicate(bmain, ob_old, dupflag); + *ob_key_p = ob_old; + *ob_value_p = ob_new; + } + else { + ob_new = *ob_value_p; + } + + collection_object_add(bmain, collection, ob_new, 0, true); + collection_object_remove(bmain, collection, ob_old, false); + } + BLI_freelistN(&collection_object_list); + + ListBase collection_child_list = {NULL, NULL}; + BLI_duplicatelist(&collection_child_list, &collection->children); + for (CollectionChild *child = collection_child_list.first; child; child = child->next) { + Collection *child_collection_old = child->collection; + Collection *child_collection_new = BKE_collection_copy(bmain, collection, child_collection_old); + + collection_duplicate_recursive(bmain, visited, child_collection_new, dupflag); + collection_child_remove(collection, child_collection_old); + } + BLI_freelistN(&collection_child_list); + + if (is_first_run) { + BLI_ghash_free(visited, NULL, NULL); + } +} + /** * Makes a shallow copy of a Collection * - * Add a new collection in the same level as the old one, copy any nested collections - * but link the objects to the new collection (as oppose to copy them). + * Add a new collection in the same level as the old one, link any nested collections + * and finally link the objects to the new collection (as oppose to copy them). */ Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection) +{ + return BKE_collection_duplicate(bmain, parent, collection, false, false); +} + +Collection *BKE_collection_duplicate(Main *bmain, Collection *parent, Collection *collection, const bool do_hierarchy, const bool do_deep_copy) { /* It's not allowed to copy the master collection. */ if (collection->flag & COLLECTION_IS_MASTER) { - BLI_assert("!Master collection can't be copied"); + BLI_assert("!Master collection can't be duplicated"); return NULL; } @@ -241,6 +296,10 @@ Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *col } } + if (do_hierarchy) { + collection_duplicate_recursive(bmain, NULL, collection_new, (do_deep_copy) ? U.dupflag : 0); + } + BKE_main_collection_sync(bmain); return collection_new; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 3c57a5f7086..c4d80fdd5e2 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1427,6 +1427,240 @@ Object *BKE_object_copy(Main *bmain, const Object *ob) return ob_copy; } +Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag) +{ + Material ***matarar; + ID *id; + int a, didit; + Object *obn = BKE_object_copy(bmain, ob); + + /* 0 == full linked. */ + if (dupflag == 0) { + return obn; + } + +#define ID_NEW_REMAP_US(a) if ( (a)->id.newid) { (a) = (void *)(a)->id.newid; (a)->id.us++; } +#define ID_NEW_REMAP_US2(a) if (((ID *)a)->newid) { (a) = ((ID *)a)->newid; ((ID *)a)->us++; } + + /* duplicates using userflags */ + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &obn->id, true); + } + + if (dupflag & USER_DUP_MAT) { + for (a = 0; a < obn->totcol; a++) { + id = (ID *)obn->mat[a]; + if (id) { + ID_NEW_REMAP_US(obn->mat[a]) + else { + obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a])); + } + id_us_min(id); + + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &obn->mat[a]->id, true); + } + } + } + } + if (dupflag & USER_DUP_PSYS) { + ParticleSystem *psys; + for (psys = obn->particlesystem.first; psys; psys = psys->next) { + id = (ID *) psys->part; + if (id) { + ID_NEW_REMAP_US(psys->part) + else { + psys->part = ID_NEW_SET(psys->part, BKE_particlesettings_copy(bmain, psys->part)); + } + + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &psys->part->id, true); + } + + id_us_min(id); + } + } + } + + id = obn->data; + didit = 0; + + switch (obn->type) { + case OB_MESH: + if (dupflag & USER_DUP_MESH) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_mesh_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_CURVE: + if (dupflag & USER_DUP_CURVE) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_SURF: + if (dupflag & USER_DUP_SURF) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_FONT: + if (dupflag & USER_DUP_FONT) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_MBALL: + if (dupflag & USER_DUP_MBALL) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_mball_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_LAMP: + if (dupflag & USER_DUP_LAMP) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_light_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_ARMATURE: + DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); + if (obn->pose) + BKE_pose_tag_recalc(bmain, obn->pose); + if (dupflag & USER_DUP_ARM) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data)); + BKE_pose_rebuild(bmain, obn, obn->data, true); + didit = 1; + } + id_us_min(id); + } + break; + case OB_LATTICE: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_CAMERA: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_LIGHTPROBE: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_SPEAKER: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_speaker_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_GPENCIL: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + } + + /* Check if obdata is copied. */ + if (didit) { + Key *key = BKE_key_from_object(obn); + + Key *oldkey = BKE_key_from_object(ob); + if (oldkey != NULL) { + ID_NEW_SET(oldkey, key); + } + + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, (ID *)obn->data, true); + if (key) { + BKE_animdata_copy_id_action(bmain, (ID *)key, true); + } + } + + if (dupflag & USER_DUP_MAT) { + matarar = give_matarar(obn); + if (matarar) { + for (a = 0; a < obn->totcol; a++) { + id = (ID *)(*matarar)[a]; + if (id) { + ID_NEW_REMAP_US((*matarar)[a]) + else { + (*matarar)[a] = ID_NEW_SET((*matarar)[a], BKE_material_copy(bmain, (*matarar)[a])); + } + id_us_min(id); + } + } + } + } + } + +#undef ID_NEW_REMAP_US +#undef ID_NEW_REMAP_US2 + + BKE_libblock_relink_to_newid(&obn->id); + + /* DAG_relations_tag_update(bmain); */ /* caller must do */ + + if (ob->data != NULL) { + DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS); + } + + /* BKE_main_id_clear_newpoins(bmain); */ /* Called must do. */ + + return obn; +} + void BKE_object_make_local_ex(Main *bmain, Object *ob, const bool lib_local, const bool clear_proxy) { bool is_local = false, is_lib = false; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 425035d41cc..b69c88ecb7f 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2208,20 +2208,14 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* Does set ID->newid pointers. */ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, int dupflag) { -#define ID_NEW_REMAP_US(a) if ( (a)->id.newid) { (a) = (void *)(a)->id.newid; (a)->id.us++; } -#define ID_NEW_REMAP_US2(a) if (((ID *)a)->newid) { (a) = ((ID *)a)->newid; ((ID *)a)->us++; } - Base *base, *basen = NULL; - Material ***matarar; Object *obn; - ID *id; - int a, didit; if (ob->mode & OB_MODE_POSE) { ; /* nothing? */ } else { - obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob)); + obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag)); DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); base = BKE_view_layer_base_find(view_layer, ob); @@ -2249,214 +2243,8 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer BKE_collection_object_add(bmain, collection, obn); } } - - /* duplicates using userflags */ - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &obn->id, true); - } - - if (dupflag & USER_DUP_MAT) { - for (a = 0; a < obn->totcol; a++) { - id = (ID *)obn->mat[a]; - if (id) { - ID_NEW_REMAP_US(obn->mat[a]) - else { - obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a])); - } - id_us_min(id); - - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &obn->mat[a]->id, true); - } - } - } - } - if (dupflag & USER_DUP_PSYS) { - ParticleSystem *psys; - for (psys = obn->particlesystem.first; psys; psys = psys->next) { - id = (ID *) psys->part; - if (id) { - ID_NEW_REMAP_US(psys->part) - else { - psys->part = ID_NEW_SET(psys->part, BKE_particlesettings_copy(bmain, psys->part)); - } - - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &psys->part->id, true); - } - - id_us_min(id); - } - } - } - - id = obn->data; - didit = 0; - - switch (obn->type) { - case OB_MESH: - if (dupflag & USER_DUP_MESH) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_mesh_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_CURVE: - if (dupflag & USER_DUP_CURVE) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_SURF: - if (dupflag & USER_DUP_SURF) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_FONT: - if (dupflag & USER_DUP_FONT) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_MBALL: - if (dupflag & USER_DUP_MBALL) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_mball_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_LAMP: - if (dupflag & USER_DUP_LAMP) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_light_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_ARMATURE: - DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); - if (obn->pose) - BKE_pose_tag_recalc(bmain, obn->pose); - if (dupflag & USER_DUP_ARM) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data)); - BKE_pose_rebuild(bmain, obn, obn->data, true); - didit = 1; - } - id_us_min(id); - } - break; - case OB_LATTICE: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_CAMERA: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_LIGHTPROBE: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_SPEAKER: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_speaker_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_GPENCIL: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else { - obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - } - - /* check if obdata is copied */ - if (didit) { - Key *key = BKE_key_from_object(obn); - - Key *oldkey = BKE_key_from_object(ob); - if (oldkey != NULL) { - ID_NEW_SET(oldkey, key); - } - - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, (ID *)obn->data, true); - if (key) { - BKE_animdata_copy_id_action(bmain, (ID *)key, true); - } - } - - if (dupflag & USER_DUP_MAT) { - matarar = give_matarar(obn); - if (matarar) { - for (a = 0; a < obn->totcol; a++) { - id = (ID *)(*matarar)[a]; - if (id) { - ID_NEW_REMAP_US((*matarar)[a]) - else { - (*matarar)[a] = ID_NEW_SET((*matarar)[a], BKE_material_copy(bmain, (*matarar)[a])); - } - id_us_min(id); - } - } - } - } - } } return basen; - -#undef ID_NEW_REMAP_US -#undef ID_NEW_REMAP_US2 } /* single object duplicate, if dupflag==0, fully linked, else it uses the flags given */ diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 67126a3e155..61c02d18f8f 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -448,6 +448,8 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te = outliner_active_collection(C); + bool hierarchy = strstr(op->idname, "hierarchy") != NULL; + bool linked = strstr(op->idname, "linked") != NULL; /* Can happen when calling from a key binding. */ if (te == NULL) { @@ -467,7 +469,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) case SO_SCENES: case SO_VIEW_LAYER: case SO_LIBRARIES: - BKE_collection_copy(bmain, parent, collection); + BKE_collection_duplicate(bmain, parent, collection, hierarchy, !linked); break; } @@ -482,7 +484,37 @@ void OUTLINER_OT_collection_duplicate(wmOperatorType *ot) /* identifiers */ ot->name = "Duplicate Collection"; ot->idname = "OUTLINER_OT_collection_duplicate"; - ot->description = "Duplicate selected collections"; + ot->description = "Make a new collection with linked content (collection and objects)"; + + /* api callbacks */ + ot->exec = collection_duplicate_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_duplicate_hierarchy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Collection Hierarchy"; + ot->idname = "OUTLINER_OT_collection_duplicate_hierarchy"; + ot->description = "Duplicate entire hierarchy and make all content single user"; + + /* api callbacks */ + ot->exec = collection_duplicate_exec; + ot->poll = ED_outliner_collections_editor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_duplicate_linked_hierarchy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Linked Collection Hierarchy"; + ot->idname = "OUTLINER_OT_collection_duplicate_linked_hierarchy"; + ot->description = "Duplicate entire hierarchy with linked object data"; /* api callbacks */ ot->exec = collection_duplicate_exec; diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 81b06cab3a1..a1751e90660 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -316,6 +316,8 @@ struct Collection *outliner_collection_from_tree_element(const TreeElement *te); void OUTLINER_OT_collection_new(struct wmOperatorType *ot); void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot); +void OUTLINER_OT_collection_duplicate_hierarchy(struct wmOperatorType *ot); +void OUTLINER_OT_collection_duplicate_linked_hierarchy(struct wmOperatorType *ot); void OUTLINER_OT_collection_delete(struct wmOperatorType *ot); void OUTLINER_OT_collection_objects_select(struct wmOperatorType *ot); void OUTLINER_OT_collection_objects_deselect(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 81001c0160f..404ad17ba4e 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -94,6 +94,8 @@ void outliner_operatortypes(void) /* collections */ WM_operatortype_append(OUTLINER_OT_collection_new); WM_operatortype_append(OUTLINER_OT_collection_duplicate); + WM_operatortype_append(OUTLINER_OT_collection_duplicate_hierarchy); + WM_operatortype_append(OUTLINER_OT_collection_duplicate_linked_hierarchy); WM_operatortype_append(OUTLINER_OT_collection_delete); WM_operatortype_append(OUTLINER_OT_collection_objects_select); WM_operatortype_append(OUTLINER_OT_collection_objects_deselect); -- cgit v1.2.3