diff options
Diffstat (limited to 'source/blender/editors/space_outliner/outliner_collections.c')
-rw-r--r-- | source/blender/editors/space_outliner/outliner_collections.c | 1056 |
1 files changed, 357 insertions, 699 deletions
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 4ea2c243365..9bb3871c247 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -24,9 +24,14 @@ * \ingroup spoutliner */ +#include <string.h> + #include "BLI_utildefines.h" #include "BLI_listbase.h" +#include "DNA_group_types.h" +#include "DNA_object_types.h" + #include "BKE_context.h" #include "BKE_collection.h" #include "BKE_layer.h" @@ -36,9 +41,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "DNA_group_types.h" -#include "DNA_object_types.h" - +#include "ED_object.h" #include "ED_screen.h" #include "WM_api.h" @@ -52,26 +55,44 @@ #include "outliner_intern.h" /* own include */ -/* Prototypes. */ -static int collection_delete_exec(struct bContext *C, struct wmOperator *op); - /* -------------------------------------------------------------------- */ -static LayerCollection *outliner_collection_active(bContext *C) +bool outliner_is_collection_tree_element(const TreeElement *te) { - return CTX_data_layer_collection(C); + TreeStoreElem *tselem = TREESTORE(te); + + if (!tselem) { + return false; + } + + if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { + return true; + } + else if (tselem->type == 0 && te->idcode == ID_GR) { + return true; + } + + return false; } -SceneCollection *outliner_scene_collection_from_tree_element(TreeElement *te) +Collection *outliner_collection_from_tree_element(const TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == TSE_SCENE_COLLECTION) { - return te->directdata; + if (!tselem) { + return false; } - else if (tselem->type == TSE_LAYER_COLLECTION) { + + if (tselem->type == TSE_LAYER_COLLECTION) { LayerCollection *lc = te->directdata; - return lc->scene_collection; + return lc->collection; + } + else if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { + Scene *scene = (Scene*)tselem->id; + return BKE_collection_master(scene); + } + else if (tselem->type == 0 && te->idcode == ID_GR) { + return (Collection*)tselem->id; } return NULL; @@ -83,956 +104,593 @@ SceneCollection *outliner_scene_collection_from_tree_element(TreeElement *te) static int collections_editor_poll(bContext *C) { SpaceOops *so = CTX_wm_space_outliner(C); - return (so != NULL) && (so->outlinevis == SO_COLLECTIONS); + return (so != NULL) && ELEM(so->outlinevis, SO_VIEW_LAYER, SO_SCENES); } -static int outliner_objects_collection_poll(bContext *C) -{ - SpaceOops *so = CTX_wm_space_outliner(C); - if (so == NULL) { - return 0; - } - - /* Groups don't support filtering. */ - if ((so->outlinevis != SO_GROUPS) && (so->filter & SO_FILTER_NO_COLLECTION)) { - return 0; - } +/********************************* New Collection ****************************/ - return ELEM(so->outlinevis, SO_COLLECTIONS, SO_GROUPS); -} - -/* -------------------------------------------------------------------- */ -/* collection manager operators */ - -/** - * Recursively get the collection for a given index - */ -static SceneCollection *scene_collection_from_index(ListBase *lb, const int number, int *i) +struct CollectionNewData { - for (SceneCollection *sc = lb->first; sc; sc = sc->next) { - if (*i == number) { - return sc; - } + bool error; + Collection *collection; +}; - (*i)++; +static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata) +{ + struct CollectionNewData *data = customdata; + Collection *collection = outliner_collection_from_tree_element(te); - SceneCollection *sc_nested = scene_collection_from_index(&sc->scene_collections, number, i); - if (sc_nested) { - return sc_nested; - } + if (!collection) { + return TRAVERSE_SKIP_CHILDS; } - return NULL; -} -typedef struct TreeElementFindData { - SceneCollection *collection; - TreeElement *r_result_te; -} TreeElementFindData; - -static TreeTraversalAction tree_element_find_by_scene_collection_cb(TreeElement *te, void *customdata) -{ - TreeElementFindData *data = customdata; - const SceneCollection *current_element_sc = outliner_scene_collection_from_tree_element(te); - - if (current_element_sc == data->collection) { - data->r_result_te = te; + if (data->collection != NULL) { + data->error = true; return TRAVERSE_BREAK; } + data->collection = collection; return TRAVERSE_CONTINUE; } -static TreeElement *outliner_tree_element_from_layer_collection_index( - SpaceOops *soops, ViewLayer *view_layer, - const int index) +static int collection_new_exec(bContext *C, wmOperator *op) { - LayerCollection *lc = BKE_layer_collection_from_index(view_layer, index); - - if (lc == NULL) { - return NULL; - } + SpaceOops *soops = CTX_wm_space_outliner(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); - /* Find the tree element containing the LayerCollection's scene_collection. */ - TreeElementFindData data = { - .collection = lc->scene_collection, - .r_result_te = NULL, + struct CollectionNewData data = { + .error = false, + .collection = NULL, }; - outliner_tree_traverse(soops, &soops->tree, 0, 0, tree_element_find_by_scene_collection_cb, &data); - - return data.r_result_te; -} -static int collection_link_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - SceneCollection *sc_master = BKE_collection_master(&scene->id); - SceneCollection *sc; + if (RNA_boolean_get(op->ptr, "nested")) { + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_to_add, &data); - int scene_collection_index = RNA_enum_get(op->ptr, "scene_collection"); - if (scene_collection_index == 0) { - sc = sc_master; - } - else { - int index = 1; - sc = scene_collection_from_index(&sc_master->scene_collections, scene_collection_index, &index); - BLI_assert(sc); + if (data.error) { + BKE_report(op->reports, RPT_ERROR, "More than one collection is selected"); + return OPERATOR_CANCELLED; + } } - BKE_collection_link(view_layer, sc); - - DEG_relations_tag_update(CTX_data_main(C)); + if (!data.collection && (soops->outlinevis == SO_VIEW_LAYER)) { + data.collection = BKE_collection_master(scene); + } - /* TODO(sergey): Use proper flag for tagging here. */ - DEG_id_tag_update(&scene->id, 0); + BKE_collection_add( + bmain, + data.collection, + NULL); + outliner_cleanup_tree(soops); + DEG_relations_tag_update(bmain); WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); return OPERATOR_FINISHED; } -static int collection_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Scene *scene = CTX_data_scene(C); - SceneCollection *master_collection = BKE_collection_master(&scene->id); - if (master_collection->scene_collections.first == NULL) { - RNA_enum_set(op->ptr, "scene_collection", 0); - return collection_link_exec(C, op); - } - else { - return WM_enum_search_invoke(C, op, event); - } -} - -static void collection_scene_collection_itemf_recursive( - EnumPropertyItem *tmp, EnumPropertyItem **item, int *totitem, int *value, SceneCollection *sc) -{ - tmp->value = *value; - tmp->icon = ICON_COLLAPSEMENU; - tmp->identifier = sc->name; - tmp->name = sc->name; - RNA_enum_item_add(item, totitem, tmp); - - (*value)++; - - for (SceneCollection *ncs = sc->scene_collections.first; ncs; ncs = ncs->next) { - collection_scene_collection_itemf_recursive(tmp, item, totitem, value, ncs); - } -} - -static const EnumPropertyItem *collection_scene_collection_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - EnumPropertyItem tmp = {0, "", 0, "", ""}; - EnumPropertyItem *item = NULL; - int value = 0, totitem = 0; - - Scene *scene = CTX_data_scene(C); - SceneCollection *sc = BKE_collection_master(&scene->id); - - collection_scene_collection_itemf_recursive(&tmp, &item, &totitem, &value, sc); - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - -void OUTLINER_OT_collection_link(wmOperatorType *ot) +void OUTLINER_OT_collection_new(wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ - ot->name = "Link Collection"; - ot->idname = "OUTLINER_OT_collection_link"; - ot->description = "Link a new collection to the active layer"; + ot->name = "New Collection"; + ot->idname = "OUTLINER_OT_collection_new"; + ot->description = "Add a new collection inside selected collection"; /* api callbacks */ - ot->exec = collection_link_exec; - ot->invoke = collection_link_invoke; + ot->exec = collection_new_exec; + ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_enum(ot->srna, "scene_collection", DummyRNA_NULL_items, 0, "Scene Collection", ""); - RNA_def_enum_funcs(prop, collection_scene_collection_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; + /* properties */ + PropertyRNA *prop = RNA_def_boolean(ot->srna, "nested", true, "Nested", "Add as child of selected collection");; + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/** - * Returns true if selected element is a collection directly - * linked to the active ViewLayer (not a nested collection) - */ -static int collection_unlink_poll(bContext *C) +/**************************** Delete Collection ******************************/ + +struct CollectionEditData { + Scene *scene; + SpaceOops *soops; + GSet *collections_to_edit; +}; + +static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata) { - if (collections_editor_poll(C) == 0) { - return 0; - } + struct CollectionEditData *data = customdata; + Collection *collection = outliner_collection_from_tree_element(te); - LayerCollection *lc = outliner_collection_active(C); + if (!collection) { + return TRAVERSE_SKIP_CHILDS; + } - if (lc == NULL) { - return 0; + if (collection == BKE_collection_master(data->scene)) { + /* skip - showing warning/error message might be missleading + * when deleting multiple collections, so just do nothing */ + } + else { + /* Delete, duplicate and link don't edit children, those will come along + * with the parents. */ + BLI_gset_add(data->collections_to_edit, collection); + return TRAVERSE_SKIP_CHILDS; } - ViewLayer *view_layer = CTX_data_view_layer(C); - return BLI_findindex(&view_layer->layer_collections, lc) != -1 ? 1 : 0; + return TRAVERSE_CONTINUE; } -static int collection_unlink_exec(bContext *C, wmOperator *op) +static int collection_delete_exec(bContext *C, wmOperator *op) { - LayerCollection *lc = outliner_collection_active(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + bool hierarchy = RNA_boolean_get(op->ptr, "hierarchy"); - if (lc == NULL) { - BKE_report(op->reports, RPT_ERROR, "Active element is not a collection"); - return OPERATOR_CANCELLED; - } + data.collections_to_edit = BLI_gset_ptr_new(__func__); - ViewLayer *view_layer = CTX_data_view_layer(C); - BKE_collection_unlink(view_layer, lc); + /* We first walk over and find the Collections we actually want to delete (ignoring duplicates). */ + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); - if (soops) { - outliner_cleanup_tree(soops); + /* Effectively delete the collections. */ + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + /* TODO: what if collection was child and got deleted in the meantime? */ + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + BKE_collection_delete(bmain, collection, hierarchy); } - DEG_relations_tag_update(CTX_data_main(C)); + BLI_gset_free(data.collections_to_edit, NULL); + + DEG_relations_tag_update(bmain); /* TODO(sergey): Use proper flag for tagging here. */ - DEG_id_tag_update(&CTX_data_scene(C)->id, 0); + DEG_id_tag_update(&scene->id, 0); WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_collection_unlink(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Unlink Collection"; - ot->idname = "OUTLINER_OT_collection_unlink"; - ot->description = "Unlink collection from the active layer"; - - /* api callbacks */ - ot->exec = collection_unlink_exec; - ot->poll = collection_unlink_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/**********************************************************************************/ -/* Add new collection. */ -static int collection_new_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - SceneCollection *scene_collection_parent = BKE_collection_master(&scene->id); - SceneCollection *scene_collection = BKE_collection_add(&scene->id, scene_collection_parent, COLLECTION_TYPE_NONE, NULL); - BKE_collection_link(view_layer, scene_collection); - - DEG_relations_tag_update(bmain); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); return OPERATOR_FINISHED; } -void OUTLINER_OT_collection_new(wmOperatorType *ot) +void OUTLINER_OT_collection_delete(wmOperatorType *ot) { /* identifiers */ - ot->name = "New Collection"; - ot->idname = "OUTLINER_OT_collection_new"; - ot->description = "Add a new collection to the scene"; + ot->name = "Delete Collection"; + ot->idname = "OUTLINER_OT_collection_delete"; + ot->description = "Delete selected collections"; /* api callbacks */ - ot->exec = collection_new_exec; + ot->exec = collection_delete_exec; + ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop = RNA_def_boolean(ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/**********************************************************************************/ -/* Add new nested collection. */ +/****************************** Select Objects *******************************/ -struct CollectionNewData -{ +struct CollectionObjectsSelectData { bool error; - SceneCollection *scene_collection; + LayerCollection *layer_collection; }; -static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata) +static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata) { - struct CollectionNewData *data = customdata; - SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te); - - if (!scene_collection) { - return TRAVERSE_SKIP_CHILDS; - } + struct CollectionObjectsSelectData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); - if (data->scene_collection != NULL) { - data->error = true; - return TRAVERSE_BREAK; + switch (tselem->type) { + case TSE_LAYER_COLLECTION: + data->layer_collection = te->directdata; + return TRAVERSE_BREAK; + case TSE_R_LAYER: + case TSE_SCENE_COLLECTION_BASE: + case TSE_VIEW_COLLECTION_BASE: + return TRAVERSE_CONTINUE; + default: + return TRAVERSE_SKIP_CHILDS; } - - data->scene_collection = scene_collection; - return TRAVERSE_CONTINUE; } -static int collection_nested_new_exec(bContext *C, wmOperator *op) +static LayerCollection *outliner_active_layer_collection(bContext *C) { SpaceOops *soops = CTX_wm_space_outliner(C); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - struct CollectionNewData data = { - .error = false, - .scene_collection = NULL, + struct CollectionObjectsSelectData data = { + .layer_collection = NULL, }; - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_to_add, &data); + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_layer_collection, &data); + return data.layer_collection; +} + +static int collection_objects_select_exec(bContext *C, wmOperator *op) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + LayerCollection *layer_collection = outliner_active_layer_collection(C); + bool deselect = STREQ(op->idname, "OUTLINER_OT_collection_objects_deselect"); - if (data.error) { - BKE_report(op->reports, RPT_ERROR, "More than one collection is selected"); + if (layer_collection == NULL) { return OPERATOR_CANCELLED; } - BKE_collection_add( - &scene->id, - data.scene_collection, - COLLECTION_TYPE_NONE, - NULL); + BKE_layer_collection_objects_select(view_layer, layer_collection, deselect); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, CTX_data_scene(C)); - outliner_cleanup_tree(soops); - DEG_relations_tag_update(bmain); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); return OPERATOR_FINISHED; } -void OUTLINER_OT_collection_nested_new(wmOperatorType *ot) +void OUTLINER_OT_collection_objects_select(wmOperatorType *ot) { /* identifiers */ - ot->name = "New Nested Collection"; - ot->idname = "OUTLINER_OT_collection_nested_new"; - ot->description = "Add a new collection inside selected collection"; + ot->name = "Select Objects"; + ot->idname = "OUTLINER_OT_collection_objects_select"; + ot->description = "Select objects in collection"; /* api callbacks */ - ot->exec = collection_nested_new_exec; + ot->exec = collection_objects_select_exec; ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/**********************************************************************************/ -/* Delete selected collection. */ - -void OUTLINER_OT_collection_delete_selected(wmOperatorType *ot) +void OUTLINER_OT_collection_objects_deselect(wmOperatorType *ot) { /* identifiers */ - ot->name = "Delete Selected Collections"; - ot->idname = "OUTLINER_OT_collection_delete_selected"; - ot->description = "Delete all the selected collections"; + ot->name = "Deselect Objects"; + ot->idname = "OUTLINER_OT_collection_objects_deselect"; + ot->description = "Deselect objects in collection"; /* api callbacks */ - ot->exec = collection_delete_exec; + ot->exec = collection_objects_select_exec; ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/**********************************************************************************/ -/* Add new selected objects. */ +/************************** Duplicate Collection *****************************/ -struct SceneCollectionSelectedData { - ListBase scene_collections_array; +struct CollectionDuplicateData { + TreeElement *te; }; -static TreeTraversalAction collection_find_selected_scene_collections(TreeElement *te, void *customdata) +static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata) { - struct SceneCollectionSelectedData *data = customdata; - SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te); + struct CollectionDuplicateData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); - if (!scene_collection) { - return TRAVERSE_SKIP_CHILDS; + switch (tselem->type) { + case TSE_LAYER_COLLECTION: + data->te = te; + return TRAVERSE_BREAK; + case TSE_R_LAYER: + case TSE_SCENE_COLLECTION_BASE: + case TSE_VIEW_COLLECTION_BASE: + default: + return TRAVERSE_CONTINUE; } - - BLI_addtail(&data->scene_collections_array, BLI_genericNodeN(scene_collection)); - return TRAVERSE_CONTINUE; } -static int collection_objects_add_exec(bContext *C, wmOperator *op) +static TreeElement *outliner_active_collection(bContext *C) { SpaceOops *soops = CTX_wm_space_outliner(C); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - struct SceneCollectionSelectedData data = { - .scene_collections_array = {NULL, NULL}, + struct CollectionDuplicateData data = { + .te = NULL, }; - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_scene_collections, &data); - - if (BLI_listbase_is_empty(&data.scene_collections_array)) { - BKE_report(op->reports, RPT_ERROR, "No collection is selected"); - return OPERATOR_CANCELLED; - } - - CTX_DATA_BEGIN (C, struct Object *, ob, selected_objects) - { - LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) { - SceneCollection *scene_collection = link->data; - BKE_collection_object_add( - &scene->id, - scene_collection, - ob); - } - } - CTX_DATA_END; - BLI_freelistN(&data.scene_collections_array); - - outliner_cleanup_tree(soops); - DEG_relations_tag_update(bmain); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_collection_objects_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Objects"; - ot->idname = "OUTLINER_OT_collection_objects_add"; - ot->description = "Add selected objects to collection"; - - /* api callbacks */ - ot->exec = collection_objects_add_exec; - ot->poll = collections_editor_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_collection, &data); + return data.te; } -/**********************************************************************************/ -/* Remove selected objects. */ - - -static int collection_objects_remove_exec(bContext *C, wmOperator *op) +static int collection_duplicate_exec(bContext *C, wmOperator *op) { - SpaceOops *soops = CTX_wm_space_outliner(C); Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - - struct SceneCollectionSelectedData data = { - .scene_collections_array = {NULL, NULL}, - }; + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te = outliner_active_collection(C); + BLI_assert(te != NULL); - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_scene_collections, &data); + Collection *collection = outliner_collection_from_tree_element(te); + Collection *parent = (te->parent) ? outliner_collection_from_tree_element(te->parent) : NULL; - if (BLI_listbase_is_empty(&data.scene_collections_array)) { - BKE_report(op->reports, RPT_ERROR, "No collection is selected"); + if (collection->flag & COLLECTION_IS_MASTER) { + BKE_report(op->reports, RPT_ERROR, "Can't duplicate the master collection"); return OPERATOR_CANCELLED; } - CTX_DATA_BEGIN (C, struct Object *, ob, selected_objects) - { - LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) { - SceneCollection *scene_collection = link->data; - BKE_collection_object_remove( - bmain, - &scene->id, - scene_collection, - ob, - true); - } + switch (soops->outlinevis) { + case SO_SCENES: + case SO_VIEW_LAYER: + case SO_LIBRARIES: + BKE_collection_copy(bmain, parent, collection); + break; } - CTX_DATA_END; - BLI_freelistN(&data.scene_collections_array); - outliner_cleanup_tree(soops); DEG_relations_tag_update(bmain); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C)); + return OPERATOR_FINISHED; } -void OUTLINER_OT_collection_objects_remove(wmOperatorType *ot) +void OUTLINER_OT_collection_duplicate(wmOperatorType *ot) { /* identifiers */ - ot->name = "Remove Objects"; - ot->idname = "OUTLINER_OT_collection_objects_remove"; - ot->description = "Remove selected objects from collection"; + ot->name = "Duplicate Collection"; + ot->idname = "OUTLINER_OT_collection_duplicate"; + ot->description = "Duplicate selected collections"; /* api callbacks */ - ot->exec = collection_objects_remove_exec; + ot->exec = collection_duplicate_exec; ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static TreeElement *outliner_collection_parent_element_get(TreeElement *te) -{ - TreeElement *te_parent = te; - while ((te_parent = te_parent->parent)) { - if (outliner_scene_collection_from_tree_element(te->parent)) { - return te_parent; - } - } - return NULL; -} +/**************************** Link Collection ******************************/ -static int object_collection_remove_exec(bContext *C, wmOperator *UNUSED(op)) +static int collection_link_exec(bContext *C, wmOperator *UNUSED(op)) { - SpaceOops *soops = CTX_wm_space_outliner(C); Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Collection *active_collection = CTX_data_layer_collection(C)->collection; + SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; - struct ObjectsSelectedData data = { - .objects_selected_array = {NULL, NULL}, - }; - - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data); + data.collections_to_edit = BLI_gset_ptr_new(__func__); - LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) { - TreeElement *te = (TreeElement *)link->data; - Object *ob = (Object *)TREESTORE(te)->id; - SceneCollection *scene_collection = NULL; + /* We first walk over and find the Collections we actually want to link (ignoring duplicates). */ + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); - TreeElement *te_parent = outliner_collection_parent_element_get(te); - if (te_parent != NULL) { - scene_collection = outliner_scene_collection_from_tree_element(te_parent); - ID *owner_id = TREESTORE(te_parent)->id; - BKE_collection_object_remove(bmain, owner_id, scene_collection, ob, true); - DEG_id_tag_update(owner_id, DEG_TAG_BASE_FLAGS_UPDATE); - } + /* Effectively link the collections. */ + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + BKE_collection_child_add(bmain, active_collection, collection); + id_fake_user_clear(&collection->id); } - BLI_freelistN(&data.objects_selected_array); + BLI_gset_free(data.collections_to_edit, NULL); - outliner_cleanup_tree(soops); DEG_relations_tag_update(bmain); + /* TODO(sergey): Use proper flag for tagging here. */ + DEG_id_tag_update(&scene->id, 0); + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); - WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, NULL); - WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, NULL); return OPERATOR_FINISHED; } -void OUTLINER_OT_object_remove_from_collection(wmOperatorType *ot) +void OUTLINER_OT_collection_link(wmOperatorType *ot) { /* identifiers */ - ot->name = "Remove Object from Collection"; - ot->idname = "OUTLINER_OT_object_remove_from_collection"; - ot->description = "Remove selected objects from their respective collection"; + ot->name = "Link Collection"; + ot->idname = "OUTLINER_OT_collection_link"; + ot->description = "Link selected collections to active scene"; /* api callbacks */ - ot->exec = object_collection_remove_exec; - ot->poll = outliner_objects_collection_poll; + ot->exec = collection_link_exec; + ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static int object_add_to_new_collection_exec(bContext *C, wmOperator *op) -{ - int operator_result = OPERATOR_CANCELLED; +/************************** Instance Collection ******************************/ - SpaceOops *soops = CTX_wm_space_outliner(C); +static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) +{ Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; - SceneCollection *scene_collection_parent, *scene_collection_new; - TreeElement *te_active, *te_parent; - - struct ObjectsSelectedData data = {{NULL}}, active = {{NULL}}; + data.collections_to_edit = BLI_gset_ptr_new(__func__); - outliner_tree_traverse(soops, &soops->tree, 0, TSE_HIGHLIGHTED, outliner_find_selected_objects, &active); - if (BLI_listbase_is_empty(&active.objects_selected_array)) { - BKE_report(op->reports, RPT_ERROR, "No object is selected"); - goto cleanup; - } + /* We first walk over and find the Collections we actually want to instance (ignoring duplicates). */ + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data); - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data); - if (BLI_listbase_is_empty(&data.objects_selected_array)) { - BKE_report(op->reports, RPT_ERROR, "No objects are selected"); - goto cleanup; - } + /* Find an active collection to add to, that doesn't give dependency cycles. */ + LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer); - /* Heuristic to get the "active" / "last object" */ - te_active = ((LinkData *)active.objects_selected_array.first)->data; - te_parent = outliner_collection_parent_element_get(te_active); + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); - if (te_parent == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "Couldn't find collection of \"%s\" object", te_active->name); - goto cleanup; + while (BKE_collection_find_cycle(active_lc->collection, collection)) { + active_lc = BKE_layer_collection_activate_parent(view_layer, active_lc); + } } - ID *owner_id = TREESTORE(te_parent)->id; - scene_collection_parent = outliner_scene_collection_from_tree_element(te_parent); - scene_collection_new = BKE_collection_add(owner_id, scene_collection_parent, scene_collection_parent->type, NULL); - - LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) { - TreeElement *te = (TreeElement *)link->data; - Object *ob = (Object *)TREESTORE(te)->id; - BKE_collection_object_add(owner_id, scene_collection_new, ob); + /* Effectively instance the collections. */ + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter); + Object *ob = ED_object_add_type(C, OB_EMPTY, collection->id.name + 2, scene->cursor.location, NULL, false, scene->layact); + ob->dup_group = collection; + ob->transflag |= OB_DUPLICOLLECTION; + id_lib_extern(&collection->id); } - outliner_cleanup_tree(soops); + BLI_gset_free(data.collections_to_edit, NULL); + DEG_relations_tag_update(bmain); + /* TODO(sergey): Use proper flag for tagging here. */ + DEG_id_tag_update(&scene->id, 0); + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); - operator_result = OPERATOR_FINISHED; -cleanup: - BLI_freelistN(&active.objects_selected_array); - BLI_freelistN(&data.objects_selected_array); - return operator_result; + return OPERATOR_FINISHED; } -void OUTLINER_OT_object_add_to_new_collection(wmOperatorType *ot) +void OUTLINER_OT_collection_instance(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Objects to New Collection"; - ot->idname = "OUTLINER_OT_object_add_to_new_collection"; - ot->description = "Add objects to a new collection"; + ot->name = "Instance Collection"; + ot->idname = "OUTLINER_OT_collection_instance"; + ot->description = "Instance selected collections to active scene"; /* api callbacks */ - ot->exec = object_add_to_new_collection_exec; - ot->poll = outliner_objects_collection_poll; + ot->exec = collection_instance_exec; + ot->poll = collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -struct CollectionDeleteData { - Scene *scene; - SpaceOops *soops; - GSet *collections_to_delete; -}; +/************************** Exclude Collection ******************************/ -static TreeTraversalAction collection_find_data_to_delete(TreeElement *te, void *customdata) +static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, void *customdata) { - struct CollectionDeleteData *data = customdata; - SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te); + struct CollectionEditData *data = customdata; + TreeStoreElem *tselem = TREESTORE(te); - if (!scene_collection) { - return TRAVERSE_SKIP_CHILDS; + if (!(tselem && tselem->type == TSE_LAYER_COLLECTION)) { + return TRAVERSE_CONTINUE; } - if (scene_collection == BKE_collection_master(&data->scene->id)) { + LayerCollection *lc = te->directdata; + + if (lc->collection->flag & COLLECTION_IS_MASTER) { /* skip - showing warning/error message might be missleading * when deleting multiple collections, so just do nothing */ } else { - BLI_gset_add(data->collections_to_delete, scene_collection); - return TRAVERSE_SKIP_CHILDS; /* Childs will be gone anyway, no need to recurse deeper. */ + /* Delete, duplicate and link don't edit children, those will come along + * with the parents. */ + BLI_gset_add(data->collections_to_edit, lc); } return TRAVERSE_CONTINUE; } -static TreeTraversalAction collection_delete_elements_from_collection(TreeElement *te, void *customdata) +static int collections_view_layer_poll(bContext *C, bool include) { - struct CollectionDeleteData *data = customdata; - SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te); - - if (!scene_collection) { - return TRAVERSE_SKIP_CHILDS; - } - - const bool will_be_deleted = BLI_gset_haskey(data->collections_to_delete, scene_collection); - if (will_be_deleted) { - outliner_free_tree_element(te, te->parent ? &te->parent->subtree : &data->soops->tree); - /* Childs are freed now, so don't recurse into them. */ - return TRAVERSE_SKIP_CHILDS; + /* Poll function so the right click menu show current state of selected collections. */ + SpaceOops *soops = CTX_wm_space_outliner(C); + if (!(soops && soops->outlinevis == SO_VIEW_LAYER)) { + return false; } - return TRAVERSE_CONTINUE; -} - -static int collection_delete_exec(bContext *C, wmOperator *UNUSED(op)) -{ Scene *scene = CTX_data_scene(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - struct CollectionDeleteData data = {.scene = scene, .soops = soops}; - - data.collections_to_delete = BLI_gset_ptr_new(__func__); - - /* We first walk over and find the SceneCollections we actually want to delete (ignoring duplicates). */ - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_delete, &data); - - /* Now, delete all tree elements representing a collection that will be deleted. We'll look for a - * new element to select in a few lines, so we can't wait until the tree is recreated on redraw. */ - outliner_tree_traverse(soops, &soops->tree, 0, 0, collection_delete_elements_from_collection, &data); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + data.collections_to_edit = BLI_gset_ptr_new(__func__); + bool result = false; - /* Effectively delete the collections. */ - GSetIterator collections_to_delete_iter; - GSET_ITER(collections_to_delete_iter, data.collections_to_delete) { - SceneCollection *sc = BLI_gsetIterator_getKey(&collections_to_delete_iter); - BKE_collection_remove(&data.scene->id, sc); - } + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data); - BLI_gset_free(data.collections_to_delete, NULL); + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); - TreeElement *select_te = outliner_tree_element_from_layer_collection_index(soops, CTX_data_view_layer(C), 0); - if (select_te) { - outliner_item_select(soops, select_te, false, false); + if (include && (lc->flag & LAYER_COLLECTION_EXCLUDE)) { + result = true; + } + else if (!include && !(lc->flag & LAYER_COLLECTION_EXCLUDE)) { + result = true; + } } - DEG_relations_tag_update(CTX_data_main(C)); - - /* TODO(sergey): Use proper flag for tagging here. */ - DEG_id_tag_update(&scene->id, 0); - - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); - - return OPERATOR_FINISHED; + BLI_gset_free(data.collections_to_edit, NULL); + return result; } -void OUTLINER_OT_collections_delete(wmOperatorType *ot) +static int collections_exclude_poll(bContext *C) { - /* identifiers */ - ot->name = "Delete"; - ot->idname = "OUTLINER_OT_collections_delete"; - ot->description = "Delete selected overrides or collections"; - - /* api callbacks */ - ot->exec = collection_delete_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + return collections_view_layer_poll(C, false); } -static int collection_select_exec(bContext *C, wmOperator *op) +static int collections_include_poll(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const int collection_index = RNA_int_get(op->ptr, "collection_index"); - view_layer->active_collection = collection_index; - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_collection_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select"; - ot->idname = "OUTLINER_OT_collection_select"; - ot->description = "Change active collection or override"; - - /* api callbacks */ - ot->exec = collection_select_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_int(ot->srna, "collection_index", 0, 0, INT_MAX, "Index", - "Index of collection to select", 0, INT_MAX); + return collections_view_layer_poll(C, true); } -#define ACTION_DISABLE 0 -#define ACTION_ENABLE 1 -#define ACTION_TOGGLE 2 - -static int collection_toggle_exec(bContext *C, wmOperator *op) +static void layer_collection_exclude_recursive_set(LayerCollection *lc) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - int action = RNA_enum_get(op->ptr, "action"); - LayerCollection *layer_collection = CTX_data_layer_collection(C); - - if (layer_collection->flag & COLLECTION_DISABLED) { - if (ELEM(action, ACTION_TOGGLE, ACTION_ENABLE)) { - layer_collection->flag &= ~COLLECTION_DISABLED; - } - else { /* ACTION_DISABLE */ - BKE_reportf(op->reports, RPT_ERROR, "Layer collection %s already disabled", - layer_collection->scene_collection->name); - return OPERATOR_CANCELLED; + for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) { + if (lc->flag & LAYER_COLLECTION_EXCLUDE) { + nlc->flag |= LAYER_COLLECTION_EXCLUDE; } - } - else { - if (ELEM(action, ACTION_TOGGLE, ACTION_DISABLE)) { - layer_collection->flag |= COLLECTION_DISABLED; - } - else { /* ACTION_ENABLE */ - BKE_reportf(op->reports, RPT_ERROR, "Layer collection %s already enabled", - layer_collection->scene_collection->name); - return OPERATOR_CANCELLED; + else { + nlc->flag &= ~LAYER_COLLECTION_EXCLUDE; } - } - - DEG_relations_tag_update(bmain); - /* TODO(sergey): Use proper flag for tagging here. */ - DEG_id_tag_update(&scene->id, 0); - - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); - return OPERATOR_FINISHED; + layer_collection_exclude_recursive_set(nlc); + } } -void OUTLINER_OT_collection_toggle(wmOperatorType *ot) +static int collection_view_layer_exec(bContext *C, wmOperator *op) { - PropertyRNA *prop; - - static EnumPropertyItem actions_items[] = { - {ACTION_DISABLE, "DISABLE", 0, "Disable", "Disable selected markers"}, - {ACTION_ENABLE, "ENABLE", 0, "Enable", "Enable selected markers"}, - {ACTION_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Toggle Collection"; - ot->idname = "OUTLINER_OT_collection_toggle"; - ot->description = "Deselect collection objects"; - - /* api callbacks */ - ot->exec = collection_toggle_exec; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = {.scene = scene, .soops = soops}; + bool include = STREQ(op->idname, "OUTLINER_OT_collection_include_set"); - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + data.collections_to_edit = BLI_gset_ptr_new(__func__); - /* properties */ - prop = RNA_def_int(ot->srna, "collection_index", -1, -1, INT_MAX, "Collection Index", "Index of collection to toggle", 0, INT_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_enum(ot->srna, "action", actions_items, ACTION_TOGGLE, "Action", "Selection action to execute"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} + outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data); -#undef ACTION_TOGGLE -#undef ACTION_ENABLE -#undef ACTION_DISABLE + GSetIterator collections_to_edit_iter; + GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { + LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); -struct CollectionObjectsSelectData { - bool error; - LayerCollection *layer_collection; -}; + if (!(lc->collection->flag & COLLECTION_IS_MASTER)) { + if (include) { + lc->flag &= ~LAYER_COLLECTION_EXCLUDE; + } + else { + lc->flag |= LAYER_COLLECTION_EXCLUDE; + } -static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata) -{ - struct CollectionObjectsSelectData *data = customdata; - TreeStoreElem *tselem = TREESTORE(te); - - switch (tselem->type) { - case TSE_LAYER_COLLECTION: - data->layer_collection = te->directdata; - return TRAVERSE_BREAK; - case TSE_LAYER_COLLECTION_BASE: - return TRAVERSE_CONTINUE; - default: - return TRAVERSE_SKIP_CHILDS; + layer_collection_exclude_recursive_set(lc); + } } -} - -static LayerCollection *outliner_active_layer_collection(bContext *C) -{ - SpaceOops *soops = CTX_wm_space_outliner(C); - struct CollectionObjectsSelectData data = { - .layer_collection = NULL, - }; - - outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_layer_collection, &data); - return data.layer_collection; -} + BLI_gset_free(data.collections_to_edit, NULL); -static int collection_objects_select_exec(bContext *C, wmOperator *UNUSED(op)) -{ - LayerCollection *layer_collection = outliner_active_layer_collection(C); - - if (layer_collection == NULL) { - return OPERATOR_CANCELLED; - } + BKE_layer_collection_sync(scene, view_layer); + DEG_relations_tag_update(bmain); - BKE_layer_collection_objects_select(layer_collection); - WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, CTX_data_scene(C)); + WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); return OPERATOR_FINISHED; } -void OUTLINER_OT_collection_objects_select(wmOperatorType *ot) +void OUTLINER_OT_collection_exclude_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Objects"; - ot->idname = "OUTLINER_OT_collection_objects_select"; - ot->description = "Select all the collection objects"; + ot->name = "Exclude from View Layer"; + ot->idname = "OUTLINER_OT_collection_exclude_set"; + ot->description = "Exclude collection from the active view layer"; /* api callbacks */ - ot->exec = collection_objects_select_exec; - ot->poll = collections_editor_poll; + ot->exec = collection_view_layer_exec; + ot->poll = collections_exclude_poll; /* 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_SCENES: - BKE_collection_duplicate(TREESTORE(te)->id, (SceneCollection *)te->directdata); - break; - case SO_COLLECTIONS: - 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) +void OUTLINER_OT_collection_include_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Duplicate Collection"; - ot->idname = "OUTLINER_OT_collection_duplicate"; - ot->description = "Duplicate collection"; + ot->name = "Include in View Layer"; + ot->idname = "OUTLINER_OT_collection_include_set"; + ot->description = "Include collection in the active view layer"; /* api callbacks */ - ot->exec = collection_duplicate_exec; - ot->poll = collections_editor_poll; + ot->exec = collection_view_layer_exec; + ot->poll = collections_include_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; |