Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py1
-rw-r--r--source/blender/blenkernel/BKE_collection.h1
-rw-r--r--source/blender/blenkernel/BKE_layer.h7
-rw-r--r--source/blender/blenkernel/intern/collection.c55
-rw-r--r--source/blender/blenkernel/intern/layer.c164
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c80
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h1
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.c1
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c7
-rw-r--r--source/blender/makesrna/intern/rna_layer.c46
10 files changed, 346 insertions, 17 deletions
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index 28644428cf8..122fe84763d 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -195,6 +195,7 @@ class OUTLINER_MT_context_scene_collection(Menu):
layout = self.layout
layout.operator("outliner.collection_nested_new", text="New Collection", icon='NEW')
+ layout.operator("outliner.collection_duplicate", text="Duplicate Collection")
layout.operator("outliner.collection_delete_selected", text="Delete Collections", icon='X')
layout.separator()
layout.operator("outliner.collection_objects_add", text="Add Selected", icon='ZOOMIN')
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index 48b4ff881ae..618e60550b7 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -49,6 +49,7 @@ struct SceneCollection *BKE_collection_add(
struct ID *owner_id, struct SceneCollection *sc_parent, const int type, const char *name);
bool BKE_collection_remove(struct ID *owner_id, struct SceneCollection *sc);
void BKE_collection_copy_data(struct SceneCollection *sc_dst, struct SceneCollection *sc_src, const int flag);
+struct SceneCollection *BKE_collection_duplicate(struct ID *owner_id, struct SceneCollection *scene_collection);
struct SceneCollection *BKE_collection_master(const struct ID *owner_id);
void BKE_collection_rename(const struct Scene *scene, struct SceneCollection *sc, const char *name);
void BKE_collection_master_free(struct ID *owner_id, const bool do_id_user);
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index f098a7e7468..633cb2c6044 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -78,11 +78,18 @@ struct Base *BKE_view_layer_base_find(struct ViewLayer *view_layer, struct Objec
void BKE_view_layer_base_deselect_all(struct ViewLayer *view_layer);
void BKE_view_layer_base_select(struct ViewLayer *view_layer, struct Base *selbase);
+void BKE_layer_collection_sync_flags(
+ struct ID *owner_id,
+ struct SceneCollection *scene_collection_dst,
+ struct SceneCollection *scene_collection_src);
+
void BKE_view_layer_copy_data(
struct ViewLayer *view_layer_dst, struct ViewLayer *view_layer_src,
struct SceneCollection *mc_dst, struct SceneCollection *mc_src,
const int flag);
+struct LayerCollection *BKE_layer_collection_duplicate(struct ID *owner_id, struct LayerCollection *layer_collection);
+
void BKE_layer_collection_free(struct ViewLayer *view_layer, struct LayerCollection *lc);
struct LayerCollection *BKE_layer_collection_get_active(struct ViewLayer *view_layer);
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index ca77969ccaa..729be02bfc7 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -68,10 +68,9 @@ static SceneCollection *collection_master_from_id(const ID *owner_id)
}
/**
- * Add a collection to a collection ListBase and syncronize all render layers
- * The ListBase is NULL when the collection is to be added to the master collection
+ * Add a new collection, but don't handle syncing with layer collections
*/
-SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom)
+static SceneCollection *collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom)
{
SceneCollection *sc_master = collection_master_from_id(owner_id);
SceneCollection *sc = MEM_callocN(sizeof(SceneCollection), "New Collection");
@@ -99,8 +98,6 @@ SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, co
BLI_addtail(&sc_parent->scene_collections, sc);
BKE_collection_rename((Scene *)owner_id, sc, name);
- BKE_layer_sync_new_scene_collection(owner_id, sc_parent, sc);
-
if (name != name_custom) {
MEM_freeN((char *)name);
}
@@ -109,6 +106,17 @@ SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, co
}
/**
+ * Add a collection to a collection ListBase and syncronize all render layers
+ * The ListBase is NULL when the collection is to be added to the master collection
+ */
+SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom)
+{
+ SceneCollection *scene_collection = collection_add(owner_id, sc_parent, type, name_custom);
+ BKE_layer_sync_new_scene_collection(owner_id, sc_parent, scene_collection);
+ return scene_collection;
+}
+
+/**
* Free the collection items recursively
*/
static void collection_free(SceneCollection *sc, const bool do_id_user)
@@ -270,6 +278,43 @@ void BKE_collection_copy_data(SceneCollection *sc_dst, SceneCollection *sc_src,
}
}
+/**
+ * Makes a shallow copy of a SceneCollection
+ *
+ * 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).
+ */
+SceneCollection *BKE_collection_duplicate(ID *owner_id, SceneCollection *scene_collection)
+{
+ SceneCollection *scene_collection_master = BKE_collection_master(owner_id);
+ SceneCollection *scene_collection_parent = find_collection_parent(scene_collection, scene_collection_master);
+
+ /* It's not allowed to copy the master collection. */
+ if (scene_collection_master == scene_collection) {
+ return NULL;
+ }
+
+ SceneCollection *scene_collection_new = collection_add(
+ owner_id,
+ scene_collection_parent,
+ scene_collection->type,
+ scene_collection->name);
+
+ if (scene_collection_new != scene_collection->next) {
+ BLI_remlink(&scene_collection_parent->scene_collections, scene_collection_new);
+ BLI_insertlinkafter(&scene_collection_parent->scene_collections, scene_collection, scene_collection_new);
+ }
+
+ BKE_collection_copy_data(scene_collection_new, scene_collection, 0);
+ BKE_layer_sync_new_scene_collection(owner_id, scene_collection_parent, scene_collection_new);
+
+ /* Make sure every linked instance of the new collection has the same values (flags, overrides, ...) as the
+ * corresponding original collection. */
+ BKE_layer_collection_sync_flags(owner_id, scene_collection_new, scene_collection);
+
+ return scene_collection_new;
+}
+
static SceneCollection *master_collection_from_id(const ID *owner_id)
{
switch (GS(owner_id->name)) {
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 4333faea863..9807c6e4f10 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -61,6 +61,7 @@
/* prototype */
struct EngineSettingsCB_Type;
+static void layer_collections_sync_flags(ListBase *layer_collections_dst, const ListBase *layer_collections_src);
static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc);
static void layer_collection_objects_populate(ViewLayer *view_layer, LayerCollection *lc, ListBase *objects);
static LayerCollection *layer_collection_add(ViewLayer *view_layer, LayerCollection *parent, SceneCollection *sc);
@@ -353,27 +354,94 @@ static SceneCollection *scene_collection_from_new_tree(
return NULL;
}
+static void layer_collection_sync_flags(
+ LayerCollection *layer_collection_dst,
+ const LayerCollection *layer_collection_src)
+{
+ layer_collection_dst->flag = layer_collection_src->flag;
+
+ if (layer_collection_dst->properties != NULL) {
+ IDP_FreeProperty(layer_collection_dst->properties);
+ MEM_SAFE_FREE(layer_collection_dst->properties);
+ }
+
+ if (layer_collection_src->properties != NULL) {
+ layer_collection_dst->properties = IDP_CopyProperty(layer_collection_src->properties);
+ }
+
+ layer_collections_sync_flags(&layer_collection_dst->layer_collections,
+ &layer_collection_src->layer_collections);
+}
+
static void layer_collections_sync_flags(ListBase *layer_collections_dst, const ListBase *layer_collections_src)
{
LayerCollection *layer_collection_dst = (LayerCollection *)layer_collections_dst->first;
const LayerCollection *layer_collection_src = (const LayerCollection *)layer_collections_src->first;
while (layer_collection_dst != NULL) {
- layer_collection_dst->flag = layer_collection_src->flag;
+ layer_collection_sync_flags(layer_collection_dst, layer_collection_src);
+ layer_collection_dst = layer_collection_dst->next;
+ layer_collection_src = layer_collection_src->next;
+ }
+}
- if (layer_collection_dst->properties != NULL) {
- IDP_FreeProperty(layer_collection_dst->properties);
- MEM_SAFE_FREE(layer_collection_dst->properties);
+static bool layer_collection_sync_if_match(
+ ListBase *lb,
+ const SceneCollection *scene_collection_dst,
+ const SceneCollection *scene_collection_src)
+{
+ for (LayerCollection *layer_collection = lb->first;
+ layer_collection;
+ layer_collection = layer_collection->next)
+ {
+ if (layer_collection->scene_collection == scene_collection_src) {
+ LayerCollection *layer_collection_dst =
+ BLI_findptr(
+ lb,
+ scene_collection_dst,
+ offsetof(LayerCollection, scene_collection));
+
+ if (layer_collection_dst != NULL) {
+ layer_collection_sync_flags(layer_collection_dst, layer_collection);
+ }
+ return true;
}
-
- if (layer_collection_src->properties != NULL) {
- layer_collection_dst->properties = IDP_CopyProperty(layer_collection_src->properties);
+ else {
+ if (layer_collection_sync_if_match(
+ &layer_collection->layer_collections,
+ scene_collection_dst,
+ scene_collection_src))
+ {
+ return true;
+ }
}
+ }
+ return false;
+}
- layer_collections_sync_flags(&layer_collection_dst->layer_collections,
- &layer_collection_src->layer_collections);
-
- layer_collection_dst = layer_collection_dst->next;
- layer_collection_src = layer_collection_src->next;
+/**
+ * Sync sibling collections across all view layers
+ *
+ * Make sure every linked instance of \a scene_collection_dst has the same values
+ * (flags, overrides, ...) as the corresponding scene_collection_src.
+ *
+ * \note expect scene_collection_dst to be scene_collection_src->next, and it also
+ * expects both collections to have the same ammount of sub-collections.
+ */
+void BKE_layer_collection_sync_flags(
+ ID *owner_id,
+ SceneCollection *scene_collection_dst,
+ SceneCollection *scene_collection_src)
+{
+ for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
+ for (LayerCollection *layer_collection = view_layer->layer_collections.first;
+ layer_collection;
+ layer_collection = layer_collection->next)
+ {
+ layer_collection_sync_if_match(
+ &layer_collection->layer_collections,
+ scene_collection_dst,
+ scene_collection_src);
+ }
}
}
@@ -438,6 +506,78 @@ void BKE_view_layer_copy_data(
}
}
+/**
+ * Find and return the ListBase of LayerCollection that has \a lc_child as one of its directly
+ * nested LayerCollection.
+ *
+ * \param lb_parent Initial ListBase of LayerCollection to look into recursively
+ * usually the view layer's collection list
+ */
+static ListBase *find_layer_collection_parent_list_base(ListBase *lb_parent, const LayerCollection *lc_child)
+{
+ for (LayerCollection *lc_nested = lb_parent->first; lc_nested; lc_nested = lc_nested->next) {
+ if (lc_nested == lc_child) {
+ return lb_parent;
+ }
+
+ ListBase *found = find_layer_collection_parent_list_base(&lc_nested->layer_collections, lc_child);
+ if (found != NULL) {
+ return found;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Makes a shallow copy of a LayerCollection
+ *
+ * Add a new collection in the same level as the old one (linking if necessary),
+ * and copy all the collection data across them.
+ */
+struct LayerCollection *BKE_layer_collection_duplicate(struct ID *owner_id, struct LayerCollection *layer_collection)
+{
+ SceneCollection *scene_collection, *scene_collection_new;
+
+ scene_collection = layer_collection->scene_collection;
+ scene_collection_new = BKE_collection_duplicate(owner_id, scene_collection);
+
+ LayerCollection *layer_collection_new = NULL;
+
+ /* If the original layer_collection was directly linked to the view layer
+ we need to link the new scene collection here as well. */
+ for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
+ if (BLI_findindex(&view_layer->layer_collections, layer_collection) != -1) {
+ layer_collection_new = BKE_collection_link(view_layer, scene_collection_new);
+ layer_collection_sync_flags(layer_collection_new, layer_collection);
+
+ if (layer_collection_new != layer_collection->next) {
+ BLI_remlink(&view_layer->layer_collections, layer_collection_new);
+ BLI_insertlinkafter(&view_layer->layer_collections, layer_collection, layer_collection_new);
+ }
+ break;
+ }
+ }
+
+ /* Otherwise just try to find the corresponding layer collection to return it back. */
+ if (layer_collection_new == NULL) {
+ for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
+ ListBase *layer_collections_parent;
+ layer_collections_parent = find_layer_collection_parent_list_base(
+ &view_layer->layer_collections,
+ layer_collection);
+ if (layer_collections_parent != NULL) {
+ layer_collection_new = BLI_findptr(
+ layer_collections_parent,
+ scene_collection_new,
+ offsetof(LayerCollection, scene_collection));
+ break;
+ }
+ }
+ }
+ return layer_collection_new;
+}
+
static void view_layer_object_base_unref(ViewLayer *view_layer, Base *base)
{
base->refcount--;
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c
index 394a7a80b39..89f1240f86d 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -91,6 +91,12 @@ static int view_layer_editor_poll(bContext *C)
return (so != NULL) && (so->outlinevis == SO_VIEW_LAYER);
}
+static int outliner_either_collection_editor_poll(bContext *C)
+{
+ SpaceOops *so = CTX_wm_space_outliner(C);
+ return (so != NULL) && (ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS));
+}
+
/* -------------------------------------------------------------------- */
/* collection manager operators */
@@ -819,3 +825,77 @@ void OUTLINER_OT_collection_objects_select(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+struct CollectionDuplicateData {
+ TreeElement *te;
+};
+
+static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata)
+{
+ struct CollectionDuplicateData *data = customdata;
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ switch (tselem->type) {
+ case TSE_LAYER_COLLECTION:
+ case TSE_SCENE_COLLECTION:
+ data->te = te;
+ return TRAVERSE_BREAK;
+ case TSE_LAYER_COLLECTION_BASE:
+ default:
+ return TRAVERSE_CONTINUE;
+ }
+}
+
+static TreeElement *outliner_active_collection(bContext *C)
+{
+ SpaceOops *soops = CTX_wm_space_outliner(C);
+
+ struct CollectionDuplicateData data = {
+ .te = NULL,
+ };
+
+ outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_collection, &data);
+ return data.te;
+}
+
+static int collection_duplicate_exec(bContext *C, wmOperator *op)
+{
+ SpaceOops *soops = CTX_wm_space_outliner(C);
+ TreeElement *te = outliner_active_collection(C);
+
+ BLI_assert(te != NULL);
+ if (BKE_collection_master(TREESTORE(te)->id) == outliner_scene_collection_from_tree_element(te)) {
+ BKE_report(op->reports, RPT_ERROR, "You can't duplicate the master collection");
+ return OPERATOR_CANCELLED;
+ }
+
+ switch (soops->outlinevis) {
+ case SO_COLLECTIONS:
+ BKE_collection_duplicate(TREESTORE(te)->id, (SceneCollection *)te->directdata);
+ break;
+ case SO_VIEW_LAYER:
+ case SO_GROUPS:
+ BKE_layer_collection_duplicate(TREESTORE(te)->id, (LayerCollection *)te->directdata);
+ break;
+ }
+
+ DEG_relations_tag_update(CTX_data_main(C));
+ WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C));
+
+ return OPERATOR_FINISHED;
+}
+
+void OUTLINER_OT_collection_duplicate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Duplicate Collection";
+ ot->idname = "OUTLINER_OT_collection_duplicate";
+ ot->description = "Duplicate collection";
+
+ /* api callbacks */
+ ot->exec = collection_duplicate_exec;
+ ot->poll = outliner_either_collection_editor_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 06d06d66326..ab10e5a3036 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -344,6 +344,7 @@ void OUTLINER_OT_collection_toggle(struct wmOperatorType *ot);
void OUTLINER_OT_collection_link(struct wmOperatorType *ot);
void OUTLINER_OT_collection_unlink(struct wmOperatorType *ot);
void OUTLINER_OT_collection_new(struct wmOperatorType *ot);
+void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_remove(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_select(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c
index b5b4fbbf039..eb42fe47cbf 100644
--- a/source/blender/editors/space_outliner/outliner_ops.c
+++ b/source/blender/editors/space_outliner/outliner_ops.c
@@ -474,6 +474,7 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_collection_link);
WM_operatortype_append(OUTLINER_OT_collection_unlink);
WM_operatortype_append(OUTLINER_OT_collection_new);
+ WM_operatortype_append(OUTLINER_OT_collection_duplicate);
WM_operatortype_append(OUTLINER_OT_collection_nested_new);
WM_operatortype_append(OUTLINER_OT_collection_delete_selected);
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index c4d032e34ee..3ee0d011bbb 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -677,6 +677,7 @@ typedef enum eOutliner_PropCollectionOps {
OL_COLLECTION_OP_OBJECTS_REMOVE,
OL_COLLECTION_OP_OBJECTS_SELECT,
OL_COLLECTION_OP_COLLECTION_NEW,
+ OL_COLLECTION_OP_COLLECTION_COPY,
OL_COLLECTION_OP_COLLECTION_DEL,
OL_COLLECTION_OP_COLLECTION_UNLINK,
OL_COLLECTION_OP_GROUP_CREATE,
@@ -875,6 +876,11 @@ static void collection_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tsel
}
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
}
+ else if (event == OL_COLLECTION_OP_COLLECTION_COPY) {
+ BKE_layer_collection_duplicate(id, lc);
+ DEG_relations_tag_update(CTX_data_main(C));
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
+ }
else if (event == OL_COLLECTION_OP_COLLECTION_UNLINK) {
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1851,6 +1857,7 @@ static EnumPropertyItem prop_collection_op_types[] = {
{OL_COLLECTION_OP_OBJECTS_REMOVE, "OBJECTS_REMOVE", ICON_X, "Remove Selected", "Remove selected objects from collection"},
{OL_COLLECTION_OP_OBJECTS_SELECT, "OBJECTS_SELECT", ICON_RESTRICT_SELECT_OFF, "Select Objects", "Selected collection objects"},
{OL_COLLECTION_OP_COLLECTION_NEW, "COLLECTION_NEW", ICON_NEW, "New Collection", "Add a new nested collection"},
+ {OL_COLLECTION_OP_COLLECTION_COPY, "COLLECTION_DUPLI", ICON_NONE, "Duplicate Collection", "Duplicate the collection"},
{OL_COLLECTION_OP_COLLECTION_UNLINK, "COLLECTION_UNLINK", ICON_UNLINKED, "Unlink", "Unlink collection"},
{OL_COLLECTION_OP_COLLECTION_DEL, "COLLECTION_DEL", ICON_X, "Delete Collection", "Delete the collection"},
{OL_COLLECTION_OP_GROUP_CREATE, "GROUP_CREATE", ICON_GROUP, "Create Group", "Turn the collection into a group collection"},
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index 68c4a15e06b..6930aa115c7 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -141,6 +141,23 @@ static int rna_SceneCollection_move_into(ID *id, SceneCollection *sc_src, Main *
return 1;
}
+static SceneCollection *rna_SceneCollection_duplicate(
+ ID *id, SceneCollection *scene_collection, Main *bmain, bContext *C, ReportList *reports)
+{
+ if (scene_collection == BKE_collection_master(id)) {
+ BKE_report(reports, RPT_ERROR, "The master collection can't be duplicated");
+ return NULL;
+ }
+
+ SceneCollection *scene_collection_new = BKE_collection_duplicate(id, scene_collection);
+
+ DEG_relations_tag_update(bmain);
+ /* Don't use id here, since the layer collection may come from a group. */
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER, CTX_data_scene(C));
+
+ return scene_collection_new;
+}
+
static SceneCollection *rna_SceneCollection_new(
ID *id, SceneCollection *sc_parent, Main *bmain, const char *name)
{
@@ -717,6 +734,23 @@ static Group *rna_LayerCollection_create_group(
return group;
}
+static LayerCollection *rna_LayerCollection_duplicate(
+ ID *id, LayerCollection *layer_collection, Main *bmain, bContext *C, ReportList *reports)
+{
+ if (layer_collection->scene_collection == BKE_collection_master(id)) {
+ BKE_report(reports, RPT_ERROR, "The master collection can't be duplicated");
+ return NULL;
+ }
+
+ LayerCollection *layer_collection_new = BKE_layer_collection_duplicate(id, layer_collection);
+
+ DEG_relations_tag_update(bmain);
+ /* Don't use id here, since the layer collection may come from a group. */
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER, CTX_data_scene(C));
+
+ return layer_collection_new;
+}
+
static int rna_LayerCollections_active_collection_index_get(PointerRNA *ptr)
{
ViewLayer *view_layer = (ViewLayer *)ptr->data;
@@ -1084,6 +1118,12 @@ static void rna_def_scene_collection(BlenderRNA *brna)
parm = RNA_def_pointer(func, "sc_dst", "SceneCollection", "Collection", "Collection to insert into");
parm = RNA_def_boolean(func, "result", false, "Result", "Whether the operation succeded");
RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "duplicate", "rna_SceneCollection_duplicate");
+ RNA_def_function_ui_description(func, "Create a copy of the collection");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "result", "SceneCollection", "", "Newly created collection");
+ RNA_def_function_return(func, parm);
}
static void rna_def_layer_collection_override(BlenderRNA *brna)
@@ -1998,6 +2038,12 @@ static void rna_def_layer_collection(BlenderRNA *brna)
parm = RNA_def_pointer(func, "result", "Group", "", "Newly created Group");
RNA_def_function_return(func, parm);
+ func = RNA_def_function(srna, "duplicate", "rna_LayerCollection_duplicate");
+ RNA_def_function_ui_description(func, "Create a copy of the collection");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "result", "LayerCollection", "", "Newly created collection");
+ RNA_def_function_return(func, parm);
+
/* Flags */
prop = RNA_def_property(srna, "selectable", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_SELECTABLE);