Outliner: Collection - Duplicate Hierarchy, and Duplicate Linked Hierarchy
authorDalai Felinto <dfelinto@gmail.com>
Tue, 26 Feb 2019 19:15:30 +0000 (16:15 -0300)
committerDalai Felinto <dfelinto@gmail.com>
Fri, 1 Mar 2019 14:44:19 +0000 (11:44 -0300)
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
source/blender/blenkernel/BKE_collection.h
source/blender/blenkernel/BKE_object.h
source/blender/blenkernel/intern/collection.c
source/blender/blenkernel/intern/object.c
source/blender/editors/object/object_add.c
source/blender/editors/space_outliner/outliner_collections.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_outliner/outliner_ops.c

index 33c68d50d56a0ce357dbe84b75d2e3c123601230..159a97e8625b2b707a3cd1bafe5b00eb9ee9b75d 100644 (file)
@@ -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,
index 2e4d2a7bf2cb5d52a71a71714f0fc5743e40f816..5e6f87e7f97869c224d79a42ad2ba04f1c96d090 100644 (file)
@@ -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);
index 58d648b4aa44d45036bb8b05418b2f1b76996eb4..32bc2f03b9e61a1c1510d0c4b89f266aee8da5d5 100644 (file)
@@ -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]);
index 37ee6fbeaae5704c8cd66a3159dff4a866c9c284..ce7a5b141a49d716991df241695f6e12fc9ff183 100644 (file)
@@ -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;
index 3c57a5f7086868116423787e9a83c985cbd0e9a0..c4d80fdd5e2746e5bc573cedd527d4f85d966ce2 100644 (file)
@@ -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;
index 425035d41cc01474641bd17143574c67f29d6a6d..b69c88ecb7febcfcd5926f47db68e99da56f820c 100644 (file)
@@ -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 */
index 67126a3e155954522ba16d18704879f7857813ec..61c02d18f8f5390c68ee8fec36975ce326882151 100644 (file)
@@ -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;
index 81b06cab3a18ac5b2fb6394507e057d14b82330a..a1751e90660e83284065629aba444584f74193d9 100644 (file)
@@ -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);
index 81001c0160f1eadbdbbf5a1b9a1efd168b40acb3..404ad17ba4e151966cbcff367eabdada1792bdba 100644 (file)
@@ -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);