Outliner: Add object(s) to new collection

Suggested by Pablo Vazquez (venomgfx).

The idea here is that it should be easy to work in the outliner by picking a
bunch of objects and adding them to a new collection.
Where is the new collection? In the same level as the "outliner active" object.

Note, since the outliner has no pure concept of an active object, I'm using
the highlight tag for this. Hopefully it works fine.

It should work in "Collections", "View Layer", and "Groups".
Only when collections are not filtered out.
This commit is contained in:
Dalai Felinto 2018-02-06 18:27:26 -02:00
parent 107486e673
commit 77b61b554c
4 changed files with 113 additions and 26 deletions

View File

@ -223,6 +223,16 @@ class OUTLINER_MT_context_object_delete(Menu):
layout.operator("outliner.object_operation", text="Delete Hierarchy").type='DELETE_HIERARCHY'
class OUTLINER_MT_context_object_collection(Menu):
bl_label = "Object Operation Collection"
def draw(self, context):
layout = self.layout
layout.operator("outliner.object_add_to_new_collection", text="Add to New Collection", icon='ZOOMIN')
layout.operator("outliner.object_remove_from_collection", text="Remove from Collection", icon='ZOOMOUT')
class OUTLINER_MT_context_object(Menu):
bl_label = "Object"
@ -231,11 +241,10 @@ class OUTLINER_MT_context_object(Menu):
layout.menu("OUTLINER_MT_context_object_select", text="Select")
layout.menu("OUTLINER_MT_context_object_delete", text="Delete")
layout.menu("OUTLINER_MT_context_object_collection", text="Collection")
layout.separator()
layout.operator("outliner.object_operation", text="Remap Users").type='REMAP'
layout.operator("outliner.object_operation", text="Rename").type='RENAME'
layout.separator()
layout.operator("outliner.object_remove_from_collection", text="Remove from Collection")
classes = (
@ -249,6 +258,7 @@ classes = (
OUTLINER_MT_context_object,
OUTLINER_MT_context_object_delete,
OUTLINER_MT_context_object_select,
OUTLINER_MT_context_object_collection,
)
if __name__ == "__main__": # only for live edit.

View File

@ -98,6 +98,24 @@ static int outliner_either_collection_editor_poll(bContext *C)
return (so != NULL) && (ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS));
}
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_ENABLE | SO_FILTER_NO_COLLECTION)) ==
(SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)))
{
return 0;
}
return ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS, SO_GROUPS);
}
/* -------------------------------------------------------------------- */
/* collection manager operators */
@ -564,22 +582,15 @@ void OUTLINER_OT_collection_objects_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int object_collection_remove_poll(bContext *C)
static TreeElement *outliner_collection_parent_element_get(TreeElement *te)
{
SpaceOops *so = CTX_wm_space_outliner(C);
if (so == NULL) {
return 0;
TreeElement *te_parent = te;
while ((te_parent = te_parent->parent)) {
if (outliner_scene_collection_from_tree_element(te->parent)) {
return te_parent;
}
}
/* Groups don't support filtering. */
if ((so->outlinevis != SO_GROUPS) &&
((so->filter & (SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)) ==
(SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)))
{
return 0;
}
return ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS, SO_GROUPS);
return NULL;
}
static int object_collection_remove_exec(bContext *C, wmOperator *UNUSED(op))
@ -598,15 +609,9 @@ static int object_collection_remove_exec(bContext *C, wmOperator *UNUSED(op))
Object *ob = (Object *)TREESTORE(te)->id;
SceneCollection *scene_collection = NULL;
TreeElement *te_parent = te;
while ((te_parent = te_parent->parent)) {
scene_collection = outliner_scene_collection_from_tree_element(te->parent);
if (scene_collection != NULL) {
break;
}
}
if (scene_collection != NULL) {
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);
@ -634,7 +639,77 @@ void OUTLINER_OT_object_remove_from_collection(wmOperatorType *ot)
/* api callbacks */
ot->exec = object_collection_remove_exec;
ot->poll = object_collection_remove_poll;
ot->poll = outliner_objects_collection_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;
SpaceOops *soops = CTX_wm_space_outliner(C);
Main *bmain = CTX_data_main(C);
SceneCollection *scene_collection_parent, *scene_collection_new;
TreeElement *te_active, *te_parent;
struct ObjectsSelectedData data = {NULL}, active = {NULL};
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;
}
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;
}
/* 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);
if (te_parent == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "Couldn't find collection of \"%s\" object", te_active->name);
goto cleanup;
}
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);
BLI_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);
}
outliner_cleanup_tree(soops);
DEG_relations_tag_update(bmain);
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;
}
void OUTLINER_OT_object_add_to_new_collection(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";
/* api callbacks */
ot->exec = object_add_to_new_collection_exec;
ot->poll = outliner_objects_collection_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

View File

@ -356,6 +356,7 @@ void OUTLINER_OT_collection_new(struct wmOperatorType *ot);
void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_remove(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_select(struct wmOperatorType *ot);
void OUTLINER_OT_object_add_to_new_collection(struct wmOperatorType *ot);
void OUTLINER_OT_object_remove_from_collection(struct wmOperatorType *ot);
void OUTLINER_OT_collection_objects_add(struct wmOperatorType *ot);

View File

@ -481,6 +481,7 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_collection_objects_add);
WM_operatortype_append(OUTLINER_OT_collection_objects_remove);
WM_operatortype_append(OUTLINER_OT_collection_objects_select);
WM_operatortype_append(OUTLINER_OT_object_add_to_new_collection);
WM_operatortype_append(OUTLINER_OT_object_remove_from_collection);
}