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:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2018-04-30 16:57:22 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2018-05-18 14:34:24 +0300
commit17bd5c9d4b1e9377b896c13043e24eaa7e979714 (patch)
tree60ca4e4118c01d88ccc04919d4f7ed46014875c1 /source/blender/editors/space_outliner/outliner_collections.c
parent70aec3732d0f03138d6477fd04c81dfd9fd21256 (diff)
Collections and groups unification
OVERVIEW * In 2.7 terminology, all layers and groups are now collection datablocks. * These collections are nestable, linkable, instanceable, overrideable, .. which opens up new ways to set up scenes and link + override data. * Viewport/render visibility and selectability are now a part of the collection and shared across all view layers and linkable. * View layers define which subset of the scene collection hierarchy is excluded for each. For many workflows one view layer can be used, these are more of an advanced feature now. OUTLINER * The outliner now has a "View Layer" display mode instead of "Collections", which can display the collections and/or objects in the view layer. * In this display mode, collections can be excluded with the right click menu. These will then be greyed out and their objects will be excluded. * To view collections not linked to any scene, the "Blender File" display mode can be used, with the new filtering option to just see Colleciton datablocks. * The outliner right click menus for collections and objects were reorganized. * Drag and drop still needs to be improved. Like before, dragging the icon or text gives different results, we'll unify this later. LINKING AND OVERRIDES * Collections can now be linked into the scene without creating an instance, with the link/append operator or from the collections view in the outliner. * Collections can get static overrides with the right click menu in the outliner, but this is rather unreliable and not clearly communicated at the moment. * We still need to improve the make override operator to turn collection instances into collections with overrides directly in the scene. PERFORMANCE * We tried to make performance not worse than before and improve it in some cases. The main thing that's still a bit slower is multiple scenes, we have to change the layer syncing to only updated affected scenes. * Collections keep a list of their parent collections for faster incremental updates in syncing and caching. * View layer bases are now in a object -> base hash to avoid quadratic time lookups internally and in API functions like visible_get(). VERSIONING * Compatibility with 2.7 files should be improved due to the new visibility controls. Of course users may not want to set up their scenes differently now to avoid having separate layers and groups. * Compatibility with 2.8 is mostly there, and was tested on Eevee demo and Hero files. There's a few things which are know to be not quite compatible, like nested layer collections inside groups. * The versioning code for 2.8 files is quite complicated, and isolated behind #ifdef so it can be removed at the end of the release cycle. KNOWN ISSUES * The G-key group operators in the 3D viewport were left mostly as is, they need to be modified still to fit better. * Same for the groups panel in the object properties. This needs to be updated still, or perhaps replaced by something better. * Collections must all have a unique name. Less restrictive namespacing is to be done later, we'll have to see how important this is as all objects within the collections must also have a unique name anyway. * Full scene copy and delete scene are exactly doing the right thing yet. Differential Revision: https://developer.blender.org/D3383 https://code.blender.org/2018/05/collections-and-groups/
Diffstat (limited to 'source/blender/editors/space_outliner/outliner_collections.c')
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c1056
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;