Fix collection syncing when creating new collections from the outliner
[blender.git] / source / blender / editors / space_outliner / outliner_collections.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Blender Foundation, Dalai Felinto
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/space_outliner/outliner_collections.c
24  *  \ingroup spoutliner
25  */
26
27 #include "BLI_utildefines.h"
28 #include "BLI_listbase.h"
29
30 #include "BKE_context.h"
31 #include "BKE_collection.h"
32 #include "BKE_layer.h"
33 #include "BKE_main.h"
34 #include "BKE_report.h"
35
36 #include "DEG_depsgraph.h"
37 #include "DEG_depsgraph_build.h"
38
39 #include "DNA_group_types.h"
40 #include "DNA_object_types.h"
41
42 #include "ED_screen.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46
47 #include "RNA_access.h"
48 #include "RNA_define.h"
49 #include "RNA_enum_types.h"
50
51 #include "UI_resources.h"
52
53 #include "outliner_intern.h" /* own include */
54
55 /* Prototypes. */
56 static int collection_delete_exec(struct bContext *C, struct wmOperator *op);
57
58 /* -------------------------------------------------------------------- */
59
60 static LayerCollection *outliner_collection_active(bContext *C)
61 {
62         return CTX_data_layer_collection(C);
63 }
64
65 SceneCollection *outliner_scene_collection_from_tree_element(TreeElement *te)
66 {
67         TreeStoreElem *tselem = TREESTORE(te);
68
69         if (tselem->type == TSE_SCENE_COLLECTION) {
70                 return te->directdata;
71         }
72         else if (tselem->type == TSE_LAYER_COLLECTION) {
73                 LayerCollection *lc = te->directdata;
74                 return lc->scene_collection;
75         }
76
77         return NULL;
78 }
79
80 /* -------------------------------------------------------------------- */
81 /* Poll functions. */
82
83 static int collections_editor_poll(bContext *C)
84 {
85         SpaceOops *so = CTX_wm_space_outliner(C);
86         return (so != NULL) && (so->outlinevis == SO_COLLECTIONS);
87 }
88
89 static int view_layer_editor_poll(bContext *C)
90 {
91         SpaceOops *so = CTX_wm_space_outliner(C);
92         return (so != NULL) && (so->outlinevis == SO_VIEW_LAYER);
93 }
94
95 static int outliner_either_collection_editor_poll(bContext *C)
96 {
97         SpaceOops *so = CTX_wm_space_outliner(C);
98         return (so != NULL) && (ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS));
99 }
100
101 static int outliner_objects_collection_poll(bContext *C)
102 {
103         SpaceOops *so = CTX_wm_space_outliner(C);
104         if (so == NULL) {
105                 return 0;
106         }
107
108         /* Groups don't support filtering. */
109         if ((so->outlinevis != SO_GROUPS) &&
110             ((so->filter & (SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)) ==
111             (SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)))
112         {
113                 return 0;
114         }
115
116         return ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS, SO_GROUPS);
117 }
118
119 /* -------------------------------------------------------------------- */
120 /* collection manager operators */
121
122 /**
123  * Recursively get the collection for a given index
124  */
125 static SceneCollection *scene_collection_from_index(ListBase *lb, const int number, int *i)
126 {
127         for (SceneCollection *sc = lb->first; sc; sc = sc->next) {
128                 if (*i == number) {
129                         return sc;
130                 }
131
132                 (*i)++;
133
134                 SceneCollection *sc_nested = scene_collection_from_index(&sc->scene_collections, number, i);
135                 if (sc_nested) {
136                         return sc_nested;
137                 }
138         }
139         return NULL;
140 }
141
142 typedef struct TreeElementFindData {
143         SceneCollection *collection;
144         TreeElement *r_result_te;
145 } TreeElementFindData;
146
147 static TreeTraversalAction tree_element_find_by_scene_collection_cb(TreeElement *te, void *customdata)
148 {
149         TreeElementFindData *data = customdata;
150         const SceneCollection *current_element_sc = outliner_scene_collection_from_tree_element(te);
151
152         if (current_element_sc == data->collection) {
153                 data->r_result_te = te;
154                 return TRAVERSE_BREAK;
155         }
156
157         return TRAVERSE_CONTINUE;
158 }
159
160 static TreeElement *outliner_tree_element_from_layer_collection_index(
161         SpaceOops *soops, ViewLayer *view_layer,
162         const int index)
163 {
164         LayerCollection *lc = BKE_layer_collection_from_index(view_layer, index);
165
166         if (lc == NULL) {
167                 return NULL;
168         }
169
170         /* Find the tree element containing the LayerCollection's scene_collection. */
171         TreeElementFindData data = {
172                 .collection = lc->scene_collection,
173                 .r_result_te = NULL,
174         };
175         outliner_tree_traverse(soops, &soops->tree, 0, 0, tree_element_find_by_scene_collection_cb, &data);
176
177         return data.r_result_te;
178 }
179
180 static int collection_link_exec(bContext *C, wmOperator *op)
181 {
182         Scene *scene = CTX_data_scene(C);
183         ViewLayer *view_layer = CTX_data_view_layer(C);
184         SceneCollection *sc_master = BKE_collection_master(&scene->id);
185         SceneCollection *sc;
186
187         int scene_collection_index = RNA_enum_get(op->ptr, "scene_collection");
188         if (scene_collection_index == 0) {
189                 sc = sc_master;
190         }
191         else {
192                 int index = 1;
193                 sc = scene_collection_from_index(&sc_master->scene_collections, scene_collection_index, &index);
194                 BLI_assert(sc);
195         }
196
197         BKE_collection_link(view_layer, sc);
198
199         DEG_relations_tag_update(CTX_data_main(C));
200
201         /* TODO(sergey): Use proper flag for tagging here. */
202         DEG_id_tag_update(&scene->id, 0);
203
204         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
205         return OPERATOR_FINISHED;
206 }
207
208 static int collection_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
209 {
210         Scene *scene = CTX_data_scene(C);
211         SceneCollection *master_collection = BKE_collection_master(&scene->id);
212         if (master_collection->scene_collections.first == NULL) {
213                 RNA_enum_set(op->ptr, "scene_collection", 0);
214                 return collection_link_exec(C, op);
215         }
216         else {
217                 return WM_enum_search_invoke(C, op, event);
218         }
219 }
220
221 static void collection_scene_collection_itemf_recursive(
222         EnumPropertyItem *tmp, EnumPropertyItem **item, int *totitem, int *value, SceneCollection *sc)
223 {
224         tmp->value = *value;
225         tmp->icon = ICON_COLLAPSEMENU;
226         tmp->identifier = sc->name;
227         tmp->name = sc->name;
228         RNA_enum_item_add(item, totitem, tmp);
229
230         (*value)++;
231
232         for (SceneCollection *ncs = sc->scene_collections.first; ncs; ncs = ncs->next) {
233                 collection_scene_collection_itemf_recursive(tmp, item, totitem, value, ncs);
234         }
235 }
236
237 static const EnumPropertyItem *collection_scene_collection_itemf(
238         bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
239 {
240         EnumPropertyItem tmp = {0, "", 0, "", ""};
241         EnumPropertyItem *item = NULL;
242         int value = 0, totitem = 0;
243
244         Scene *scene = CTX_data_scene(C);
245         SceneCollection *sc = BKE_collection_master(&scene->id);
246
247         collection_scene_collection_itemf_recursive(&tmp, &item, &totitem, &value, sc);
248         RNA_enum_item_end(&item, &totitem);
249         *r_free = true;
250
251         return item;
252 }
253
254 void OUTLINER_OT_collection_link(wmOperatorType *ot)
255 {
256         PropertyRNA *prop;
257
258         /* identifiers */
259         ot->name = "Link Collection";
260         ot->idname = "OUTLINER_OT_collection_link";
261         ot->description = "Link a new collection to the active layer";
262
263         /* api callbacks */
264         ot->exec = collection_link_exec;
265         ot->invoke = collection_link_invoke;
266
267         /* flags */
268         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
269
270         prop = RNA_def_enum(ot->srna, "scene_collection", DummyRNA_NULL_items, 0, "Scene Collection", "");
271         RNA_def_enum_funcs(prop, collection_scene_collection_itemf);
272         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
273         ot->prop = prop;
274 }
275
276 /**
277  * Returns true if selected element is a collection directly
278  * linked to the active ViewLayer (not a nested collection)
279  */
280 static int collection_unlink_poll(bContext *C)
281 {
282         if (view_layer_editor_poll(C) == 0) {
283                 return 0;
284         }
285
286         LayerCollection *lc = outliner_collection_active(C);
287
288         if (lc == NULL) {
289                 return 0;
290         }
291
292         ViewLayer *view_layer = CTX_data_view_layer(C);
293         return BLI_findindex(&view_layer->layer_collections, lc) != -1 ? 1 : 0;
294 }
295
296 static int collection_unlink_exec(bContext *C, wmOperator *op)
297 {
298         LayerCollection *lc = outliner_collection_active(C);
299         SpaceOops *soops = CTX_wm_space_outliner(C);
300
301         if (lc == NULL) {
302                 BKE_report(op->reports, RPT_ERROR, "Active element is not a collection");
303                 return OPERATOR_CANCELLED;
304         }
305
306         ViewLayer *view_layer = CTX_data_view_layer(C);
307         BKE_collection_unlink(view_layer, lc);
308
309         if (soops) {
310                 outliner_cleanup_tree(soops);
311         }
312
313         DEG_relations_tag_update(CTX_data_main(C));
314
315         /* TODO(sergey): Use proper flag for tagging here. */
316         DEG_id_tag_update(&CTX_data_scene(C)->id, 0);
317
318         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
319         return OPERATOR_FINISHED;
320 }
321
322 void OUTLINER_OT_collection_unlink(wmOperatorType *ot)
323 {
324         /* identifiers */
325         ot->name = "Unlink Collection";
326         ot->idname = "OUTLINER_OT_collection_unlink";
327         ot->description = "Unlink collection from the active layer";
328
329         /* api callbacks */
330         ot->exec = collection_unlink_exec;
331         ot->poll = collection_unlink_poll;
332
333         /* flags */
334         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
335 }
336
337 /**********************************************************************************/
338 /* Add new collection. */
339
340 static int collection_new_exec(bContext *C, wmOperator *UNUSED(op))
341 {
342         Main *bmain = CTX_data_main(C);
343         Scene *scene = CTX_data_scene(C);
344         ViewLayer *view_layer = CTX_data_view_layer(C);
345         SceneCollection *scene_collection_parent = BKE_collection_master(&scene->id);
346         SceneCollection *scene_collection = BKE_collection_add(&scene->id, scene_collection_parent, COLLECTION_TYPE_NONE, NULL);
347         BKE_collection_link(view_layer, scene_collection);
348
349         DEG_relations_tag_update(bmain);
350         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
351         return OPERATOR_FINISHED;
352 }
353
354 void OUTLINER_OT_collection_new(wmOperatorType *ot)
355 {
356         /* identifiers */
357         ot->name = "New Collection";
358         ot->idname = "OUTLINER_OT_collection_new";
359         ot->description = "Add a new collection to the scene";
360
361         /* api callbacks */
362         ot->exec = collection_new_exec;
363
364         /* flags */
365         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
366 }
367
368 /**********************************************************************************/
369 /* Add new nested collection. */
370
371 struct CollectionNewData
372 {
373         bool error;
374         SceneCollection *scene_collection;
375 };
376
377 static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata)
378 {
379         struct CollectionNewData *data = customdata;
380         SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
381
382         if (!scene_collection) {
383                 return TRAVERSE_SKIP_CHILDS;
384         }
385
386         if (data->scene_collection != NULL) {
387                 data->error = true;
388                 return TRAVERSE_BREAK;
389         }
390
391         data->scene_collection = scene_collection;
392         return TRAVERSE_CONTINUE;
393 }
394
395 static int collection_nested_new_exec(bContext *C, wmOperator *op)
396 {
397         SpaceOops *soops = CTX_wm_space_outliner(C);
398         Main *bmain = CTX_data_main(C);
399         Scene *scene = CTX_data_scene(C);
400
401         struct CollectionNewData data = {
402                 .error = false,
403                 .scene_collection = NULL,
404         };
405
406         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_to_add, &data);
407
408         if (data.error) {
409                 BKE_report(op->reports, RPT_ERROR, "More than one collection is selected");
410                 return OPERATOR_CANCELLED;
411         }
412
413         BKE_collection_add(
414                     &scene->id,
415                     data.scene_collection,
416                     COLLECTION_TYPE_NONE,
417                     NULL);
418
419         outliner_cleanup_tree(soops);
420         DEG_relations_tag_update(bmain);
421         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
422         return OPERATOR_FINISHED;
423 }
424
425 void OUTLINER_OT_collection_nested_new(wmOperatorType *ot)
426 {
427         /* identifiers */
428         ot->name = "New Nested Collection";
429         ot->idname = "OUTLINER_OT_collection_nested_new";
430         ot->description = "Add a new collection inside selected collection";
431
432         /* api callbacks */
433         ot->exec = collection_nested_new_exec;
434         ot->poll = collections_editor_poll;
435
436         /* flags */
437         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
438 }
439
440 /**********************************************************************************/
441 /* Delete selected collection. */
442
443 void OUTLINER_OT_collection_delete_selected(wmOperatorType *ot)
444 {
445         /* identifiers */
446         ot->name = "Delete Selected Collections";
447         ot->idname = "OUTLINER_OT_collection_delete_selected";
448         ot->description = "Delete all the selected collections";
449
450         /* api callbacks */
451         ot->exec = collection_delete_exec;
452         ot->poll = collections_editor_poll;
453
454         /* flags */
455         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
456 }
457
458 /**********************************************************************************/
459 /* Add new selected objects. */
460
461 struct SceneCollectionSelectedData {
462         ListBase scene_collections_array;
463 };
464
465 static TreeTraversalAction collection_find_selected_scene_collections(TreeElement *te, void *customdata)
466 {
467         struct SceneCollectionSelectedData *data = customdata;
468         SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
469
470         if (!scene_collection) {
471                 return TRAVERSE_SKIP_CHILDS;
472         }
473
474         BLI_addtail(&data->scene_collections_array, BLI_genericNodeN(scene_collection));
475         return TRAVERSE_CONTINUE;
476 }
477
478 static int collection_objects_add_exec(bContext *C, wmOperator *op)
479 {
480         SpaceOops *soops = CTX_wm_space_outliner(C);
481         Main *bmain = CTX_data_main(C);
482         Scene *scene = CTX_data_scene(C);
483
484         struct SceneCollectionSelectedData data = {
485                 .scene_collections_array = {NULL, NULL},
486         };
487
488         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_scene_collections, &data);
489
490         if (BLI_listbase_is_empty(&data.scene_collections_array)) {
491                 BKE_report(op->reports, RPT_ERROR, "No collection is selected");
492                 return OPERATOR_CANCELLED;
493         }
494
495         CTX_DATA_BEGIN (C, struct Object *, ob, selected_objects)
496         {
497                 BLI_LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) {
498                         SceneCollection *scene_collection = link->data;
499                         BKE_collection_object_add(
500                                     &scene->id,
501                                     scene_collection,
502                                     ob);
503                 }
504         }
505         CTX_DATA_END;
506         BLI_freelistN(&data.scene_collections_array);
507
508         outliner_cleanup_tree(soops);
509         DEG_relations_tag_update(bmain);
510         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
511         return OPERATOR_FINISHED;
512 }
513
514 void OUTLINER_OT_collection_objects_add(wmOperatorType *ot)
515 {
516         /* identifiers */
517         ot->name = "Add Objects";
518         ot->idname = "OUTLINER_OT_collection_objects_add";
519         ot->description = "Add selected objects to collection";
520
521         /* api callbacks */
522         ot->exec = collection_objects_add_exec;
523         ot->poll = collections_editor_poll;
524
525         /* flags */
526         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
527 }
528
529 /**********************************************************************************/
530 /* Remove selected objects. */
531
532
533 static int collection_objects_remove_exec(bContext *C, wmOperator *op)
534 {
535         SpaceOops *soops = CTX_wm_space_outliner(C);
536         Main *bmain = CTX_data_main(C);
537         Scene *scene = CTX_data_scene(C);
538
539         struct SceneCollectionSelectedData data = {
540                 .scene_collections_array = {NULL, NULL},
541         };
542
543         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_scene_collections, &data);
544
545         if (BLI_listbase_is_empty(&data.scene_collections_array)) {
546                 BKE_report(op->reports, RPT_ERROR, "No collection is selected");
547                 return OPERATOR_CANCELLED;
548         }
549
550         CTX_DATA_BEGIN (C, struct Object *, ob, selected_objects)
551         {
552                 BLI_LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) {
553                         SceneCollection *scene_collection = link->data;
554                         BKE_collection_object_remove(
555                                     bmain,
556                                     &scene->id,
557                                     scene_collection,
558                                     ob,
559                                     true);
560                 }
561         }
562         CTX_DATA_END;
563         BLI_freelistN(&data.scene_collections_array);
564
565         outliner_cleanup_tree(soops);
566         DEG_relations_tag_update(bmain);
567         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
568         return OPERATOR_FINISHED;
569 }
570
571 void OUTLINER_OT_collection_objects_remove(wmOperatorType *ot)
572 {
573         /* identifiers */
574         ot->name = "Remove Objects";
575         ot->idname = "OUTLINER_OT_collection_objects_remove";
576         ot->description = "Remove selected objects from collection";
577
578         /* api callbacks */
579         ot->exec = collection_objects_remove_exec;
580         ot->poll = collections_editor_poll;
581
582         /* flags */
583         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
584 }
585
586 static TreeElement *outliner_collection_parent_element_get(TreeElement *te)
587 {
588         TreeElement *te_parent = te;
589         while ((te_parent = te_parent->parent)) {
590                 if (outliner_scene_collection_from_tree_element(te->parent)) {
591                         return te_parent;
592                 }
593         }
594         return NULL;
595 }
596
597 static int object_collection_remove_exec(bContext *C, wmOperator *UNUSED(op))
598 {
599         SpaceOops *soops = CTX_wm_space_outliner(C);
600         Main *bmain = CTX_data_main(C);
601
602         struct ObjectsSelectedData data = {
603                 .objects_selected_array = {NULL, NULL},
604         };
605
606         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
607
608         BLI_LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
609                 TreeElement *te = (TreeElement *)link->data;
610                 Object *ob = (Object *)TREESTORE(te)->id;
611                 SceneCollection *scene_collection = NULL;
612
613                 TreeElement *te_parent = outliner_collection_parent_element_get(te);
614                 if (te_parent != NULL) {
615                         scene_collection = outliner_scene_collection_from_tree_element(te_parent);
616                         ID *owner_id = TREESTORE(te_parent)->id;
617                         BKE_collection_object_remove(bmain, owner_id, scene_collection, ob, true);
618                         DEG_id_tag_update(owner_id, DEG_TAG_BASE_FLAGS_UPDATE);
619                 }
620         }
621
622         BLI_freelistN(&data.objects_selected_array);
623
624         outliner_cleanup_tree(soops);
625         DEG_relations_tag_update(bmain);
626
627         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
628         WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, NULL);
629         WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, NULL);
630
631         return OPERATOR_FINISHED;
632 }
633
634 void OUTLINER_OT_object_remove_from_collection(wmOperatorType *ot)
635 {
636         /* identifiers */
637         ot->name = "Remove Object from Collection";
638         ot->idname = "OUTLINER_OT_object_remove_from_collection";
639         ot->description = "Remove selected objects from their respective collection";
640
641         /* api callbacks */
642         ot->exec = object_collection_remove_exec;
643         ot->poll = outliner_objects_collection_poll;
644
645         /* flags */
646         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
647 }
648
649 static int object_add_to_new_collection_exec(bContext *C, wmOperator *op)
650 {
651         int operator_result = OPERATOR_CANCELLED;
652
653         SpaceOops *soops = CTX_wm_space_outliner(C);
654         Main *bmain = CTX_data_main(C);
655
656         SceneCollection *scene_collection_parent, *scene_collection_new;
657         TreeElement *te_active, *te_parent;
658
659         struct ObjectsSelectedData data = {NULL}, active = {NULL};
660
661         outliner_tree_traverse(soops, &soops->tree, 0, TSE_HIGHLIGHTED, outliner_find_selected_objects, &active);
662         if (BLI_listbase_is_empty(&active.objects_selected_array)) {
663                 BKE_report(op->reports, RPT_ERROR, "No object is selected");
664                 goto cleanup;
665         }
666
667         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
668         if (BLI_listbase_is_empty(&data.objects_selected_array)) {
669                 BKE_report(op->reports, RPT_ERROR, "No objects are selected");
670                 goto cleanup;
671         }
672
673         /* Heuristic to get the "active" / "last object" */
674         te_active = ((LinkData *)active.objects_selected_array.first)->data;
675         te_parent = outliner_collection_parent_element_get(te_active);
676
677         if (te_parent == NULL) {
678                 BKE_reportf(op->reports, RPT_ERROR, "Couldn't find collection of \"%s\" object", te_active->name);
679                 goto cleanup;
680         }
681
682         ID *owner_id = TREESTORE(te_parent)->id;
683         scene_collection_parent = outliner_scene_collection_from_tree_element(te_parent);
684         scene_collection_new = BKE_collection_add(owner_id, scene_collection_parent, scene_collection_parent->type, NULL);
685
686         BLI_LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
687                 TreeElement *te = (TreeElement *)link->data;
688                 Object *ob = (Object *)TREESTORE(te)->id;
689                 BKE_collection_object_add(owner_id, scene_collection_new, ob);
690         }
691
692         outliner_cleanup_tree(soops);
693         DEG_relations_tag_update(bmain);
694
695         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
696
697         operator_result = OPERATOR_FINISHED;
698 cleanup:
699         BLI_freelistN(&active.objects_selected_array);
700         BLI_freelistN(&data.objects_selected_array);
701         return operator_result;
702 }
703
704 void OUTLINER_OT_object_add_to_new_collection(wmOperatorType *ot)
705 {
706         /* identifiers */
707         ot->name = "Add Objects to New Collection";
708         ot->idname = "OUTLINER_OT_object_add_to_new_collection";
709         ot->description = "Add objects to a new collection";
710
711         /* api callbacks */
712         ot->exec = object_add_to_new_collection_exec;
713         ot->poll = outliner_objects_collection_poll;
714
715         /* flags */
716         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
717 }
718
719 struct CollectionDeleteData {
720         Scene *scene;
721         SpaceOops *soops;
722         GSet *collections_to_delete;
723 };
724
725 static TreeTraversalAction collection_find_data_to_delete(TreeElement *te, void *customdata)
726 {
727         struct CollectionDeleteData *data = customdata;
728         SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
729
730         if (!scene_collection) {
731                 return TRAVERSE_SKIP_CHILDS;
732         }
733
734         if (scene_collection == BKE_collection_master(&data->scene->id)) {
735                 /* skip - showing warning/error message might be missleading
736                  * when deleting multiple collections, so just do nothing */
737         }
738         else {
739                 BLI_gset_add(data->collections_to_delete, scene_collection);
740                 return TRAVERSE_SKIP_CHILDS; /* Childs will be gone anyway, no need to recurse deeper. */
741         }
742
743         return TRAVERSE_CONTINUE;
744 }
745
746 static TreeTraversalAction collection_delete_elements_from_collection(TreeElement *te, void *customdata)
747 {
748         struct CollectionDeleteData *data = customdata;
749         SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
750
751         if (!scene_collection) {
752                 return TRAVERSE_SKIP_CHILDS;
753         }
754
755         const bool will_be_deleted = BLI_gset_haskey(data->collections_to_delete, scene_collection);
756         if (will_be_deleted) {
757                 outliner_free_tree_element(te, te->parent ? &te->parent->subtree : &data->soops->tree);
758                 /* Childs are freed now, so don't recurse into them. */
759                 return TRAVERSE_SKIP_CHILDS;
760         }
761
762         return TRAVERSE_CONTINUE;
763 }
764
765 static int collection_delete_exec(bContext *C, wmOperator *UNUSED(op))
766 {
767         Scene *scene = CTX_data_scene(C);
768         SpaceOops *soops = CTX_wm_space_outliner(C);
769         struct CollectionDeleteData data = {.scene = scene, .soops = soops};
770
771         data.collections_to_delete = BLI_gset_ptr_new(__func__);
772
773         /* We first walk over and find the SceneCollections we actually want to delete (ignoring duplicates). */
774         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_delete, &data);
775
776         /* Now, delete all tree elements representing a collection that will be deleted. We'll look for a
777          * new element to select in a few lines, so we can't wait until the tree is recreated on redraw. */
778         outliner_tree_traverse(soops, &soops->tree, 0, 0, collection_delete_elements_from_collection, &data);
779
780         /* Effectively delete the collections. */
781         GSetIterator collections_to_delete_iter;
782         GSET_ITER(collections_to_delete_iter, data.collections_to_delete) {
783                 SceneCollection *sc = BLI_gsetIterator_getKey(&collections_to_delete_iter);
784                 BKE_collection_remove(&data.scene->id, sc);
785         }
786
787         BLI_gset_free(data.collections_to_delete, NULL);
788
789         TreeElement *select_te = outliner_tree_element_from_layer_collection_index(soops, CTX_data_view_layer(C), 0);
790         if (select_te) {
791                 outliner_item_select(soops, select_te, false, false);
792         }
793
794         DEG_relations_tag_update(CTX_data_main(C));
795
796         /* TODO(sergey): Use proper flag for tagging here. */
797         DEG_id_tag_update(&scene->id, 0);
798
799         soops->storeflag |= SO_TREESTORE_REDRAW;
800         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
801
802         return OPERATOR_FINISHED;
803 }
804
805 void OUTLINER_OT_collections_delete(wmOperatorType *ot)
806 {
807         /* identifiers */
808         ot->name = "Delete";
809         ot->idname = "OUTLINER_OT_collections_delete";
810         ot->description = "Delete selected overrides or collections";
811
812         /* api callbacks */
813         ot->exec = collection_delete_exec;
814
815         /* flags */
816         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
817 }
818
819 static int collection_select_exec(bContext *C, wmOperator *op)
820 {
821         ViewLayer *view_layer = CTX_data_view_layer(C);
822         const int collection_index = RNA_int_get(op->ptr, "collection_index");
823         view_layer->active_collection = collection_index;
824         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
825         return OPERATOR_FINISHED;
826 }
827
828 void OUTLINER_OT_collection_select(wmOperatorType *ot)
829 {
830         /* identifiers */
831         ot->name = "Select";
832         ot->idname = "OUTLINER_OT_collection_select";
833         ot->description = "Change active collection or override";
834
835         /* api callbacks */
836         ot->exec = collection_select_exec;
837
838         /* flags */
839         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
840
841         RNA_def_int(ot->srna, "collection_index", 0, 0, INT_MAX, "Index",
842                     "Index of collection to select", 0, INT_MAX);
843 }
844
845 #define ACTION_DISABLE 0
846 #define ACTION_ENABLE 1
847 #define ACTION_TOGGLE 2
848
849 static int collection_toggle_exec(bContext *C, wmOperator *op)
850 {
851         Main *bmain = CTX_data_main(C);
852         Scene *scene = CTX_data_scene(C);
853         int action = RNA_enum_get(op->ptr, "action");
854         LayerCollection *layer_collection = CTX_data_layer_collection(C);
855
856         if (layer_collection->flag & COLLECTION_DISABLED) {
857                 if (ELEM(action, ACTION_TOGGLE, ACTION_ENABLE)) {
858                         layer_collection->flag &= ~COLLECTION_DISABLED;
859                 }
860                 else { /* ACTION_DISABLE */
861                         BKE_reportf(op->reports, RPT_ERROR, "Layer collection %s already disabled",
862                                     layer_collection->scene_collection->name);
863                         return OPERATOR_CANCELLED;
864                 }
865         }
866         else {
867                 if (ELEM(action, ACTION_TOGGLE, ACTION_DISABLE)) {
868                         layer_collection->flag |= COLLECTION_DISABLED;
869                 }
870                 else { /* ACTION_ENABLE */
871                         BKE_reportf(op->reports, RPT_ERROR, "Layer collection %s already enabled",
872                                     layer_collection->scene_collection->name);
873                         return OPERATOR_CANCELLED;
874                 }
875         }
876
877         DEG_relations_tag_update(bmain);
878         /* TODO(sergey): Use proper flag for tagging here. */
879         DEG_id_tag_update(&scene->id, 0);
880
881         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
882         WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
883
884         return OPERATOR_FINISHED;
885 }
886
887 void OUTLINER_OT_collection_toggle(wmOperatorType *ot)
888 {
889         PropertyRNA *prop;
890
891         static EnumPropertyItem actions_items[] = {
892                 {ACTION_DISABLE, "DISABLE", 0, "Disable", "Disable selected markers"},
893                 {ACTION_ENABLE, "ENABLE", 0, "Enable", "Enable selected markers"},
894                 {ACTION_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
895                 {0, NULL, 0, NULL, NULL}
896         };
897
898         /* identifiers */
899         ot->name = "Toggle Collection";
900         ot->idname = "OUTLINER_OT_collection_toggle";
901         ot->description = "Deselect collection objects";
902
903         /* api callbacks */
904         ot->exec = collection_toggle_exec;
905
906         /* flags */
907         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
908
909         /* properties */
910         prop = RNA_def_int(ot->srna, "collection_index", -1, -1, INT_MAX, "Collection Index", "Index of collection to toggle", 0, INT_MAX);
911         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
912         prop = RNA_def_enum(ot->srna, "action", actions_items, ACTION_TOGGLE, "Action", "Selection action to execute");
913         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
914 }
915
916 #undef ACTION_TOGGLE
917 #undef ACTION_ENABLE
918 #undef ACTION_DISABLE
919
920 struct CollectionObjectsSelectData {
921         bool error;
922         LayerCollection *layer_collection;
923 };
924
925 static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata)
926 {
927         struct CollectionObjectsSelectData *data = customdata;
928         TreeStoreElem *tselem = TREESTORE(te);
929
930         switch (tselem->type) {
931                 case TSE_LAYER_COLLECTION:
932                         data->layer_collection = te->directdata;
933                         return TRAVERSE_BREAK;
934                 case TSE_LAYER_COLLECTION_BASE:
935                         return TRAVERSE_CONTINUE;
936                 default:
937                         return TRAVERSE_SKIP_CHILDS;
938         }
939 }
940
941 static LayerCollection *outliner_active_layer_collection(bContext *C)
942 {
943         SpaceOops *soops = CTX_wm_space_outliner(C);
944
945         struct CollectionObjectsSelectData data = {
946                 .layer_collection = NULL,
947         };
948
949         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_layer_collection, &data);
950         return data.layer_collection;
951 }
952
953 static int collection_objects_select_exec(bContext *C, wmOperator *UNUSED(op))
954 {
955         LayerCollection *layer_collection = outliner_active_layer_collection(C);
956
957         if (layer_collection == NULL) {
958                 return OPERATOR_CANCELLED;
959         }
960
961         BKE_layer_collection_objects_select(layer_collection);
962         WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
963
964         return OPERATOR_FINISHED;
965 }
966
967 void OUTLINER_OT_collection_objects_select(wmOperatorType *ot)
968 {
969         /* identifiers */
970         ot->name = "Select Objects";
971         ot->idname = "OUTLINER_OT_collection_objects_select";
972         ot->description = "Select all the collection objects";
973
974         /* api callbacks */
975         ot->exec = collection_objects_select_exec;
976         ot->poll = view_layer_editor_poll;
977
978         /* flags */
979         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
980 }
981
982 struct CollectionDuplicateData {
983         TreeElement *te;
984 };
985
986 static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata)
987 {
988         struct CollectionDuplicateData *data = customdata;
989         TreeStoreElem *tselem = TREESTORE(te);
990
991         switch (tselem->type) {
992                 case TSE_LAYER_COLLECTION:
993                 case TSE_SCENE_COLLECTION:
994                         data->te = te;
995                         return TRAVERSE_BREAK;
996                 case TSE_LAYER_COLLECTION_BASE:
997                 default:
998                         return TRAVERSE_CONTINUE;
999         }
1000 }
1001
1002 static TreeElement *outliner_active_collection(bContext *C)
1003 {
1004         SpaceOops *soops = CTX_wm_space_outliner(C);
1005
1006         struct CollectionDuplicateData data = {
1007                 .te = NULL,
1008         };
1009
1010         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_collection, &data);
1011         return data.te;
1012 }
1013
1014 static int collection_duplicate_exec(bContext *C, wmOperator *op)
1015 {
1016         SpaceOops *soops = CTX_wm_space_outliner(C);
1017         TreeElement *te = outliner_active_collection(C);
1018
1019         BLI_assert(te != NULL);
1020         if (BKE_collection_master(TREESTORE(te)->id) == outliner_scene_collection_from_tree_element(te)) {
1021                 BKE_report(op->reports, RPT_ERROR, "You can't duplicate the master collection");
1022                 return OPERATOR_CANCELLED;
1023         }
1024
1025         switch (soops->outlinevis) {
1026                 case SO_COLLECTIONS:
1027                         BKE_collection_duplicate(TREESTORE(te)->id, (SceneCollection *)te->directdata);
1028                         break;
1029                 case SO_VIEW_LAYER:
1030                 case SO_GROUPS:
1031                         BKE_layer_collection_duplicate(TREESTORE(te)->id, (LayerCollection *)te->directdata);
1032                         break;
1033         }
1034
1035         DEG_relations_tag_update(CTX_data_main(C));
1036         WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C));
1037
1038         return OPERATOR_FINISHED;
1039 }
1040
1041 void OUTLINER_OT_collection_duplicate(wmOperatorType *ot)
1042 {
1043         /* identifiers */
1044         ot->name = "Duplicate Collection";
1045         ot->idname = "OUTLINER_OT_collection_duplicate";
1046         ot->description = "Duplicate collection";
1047
1048         /* api callbacks */
1049         ot->exec = collection_duplicate_exec;
1050         ot->poll = outliner_either_collection_editor_poll;
1051
1052         /* flags */
1053         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1054 }