Merge branch 'master' into blender2.8
[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 <string.h>
28
29 #include "BLI_utildefines.h"
30 #include "BLI_listbase.h"
31
32 #include "DNA_group_types.h"
33 #include "DNA_object_types.h"
34
35 #include "BKE_context.h"
36 #include "BKE_collection.h"
37 #include "BKE_layer.h"
38 #include "BKE_main.h"
39 #include "BKE_report.h"
40
41 #include "DEG_depsgraph.h"
42 #include "DEG_depsgraph_build.h"
43
44 #include "ED_object.h"
45 #include "ED_outliner.h"
46 #include "ED_screen.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "RNA_access.h"
52 #include "RNA_define.h"
53 #include "RNA_enum_types.h"
54
55 #include "UI_resources.h"
56
57 #include "outliner_intern.h" /* own include */
58
59 /* -------------------------------------------------------------------- */
60
61 bool outliner_is_collection_tree_element(const TreeElement *te)
62 {
63         TreeStoreElem *tselem = TREESTORE(te);
64
65         if (!tselem) {
66                 return false;
67         }
68
69         if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
70                 return true;
71         }
72         else if (tselem->type == 0 && te->idcode == ID_GR) {
73                 return true;
74         }
75
76         return false;
77 }
78
79 Collection *outliner_collection_from_tree_element(const TreeElement *te)
80 {
81         TreeStoreElem *tselem = TREESTORE(te);
82
83         if (!tselem) {
84                 return false;
85         }
86
87         if (tselem->type == TSE_LAYER_COLLECTION) {
88                 LayerCollection *lc = te->directdata;
89                 return lc->collection;
90         }
91         else if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
92                 Scene *scene = (Scene *)tselem->id;
93                 return BKE_collection_master(scene);
94         }
95         else if (tselem->type == 0 && te->idcode == ID_GR) {
96                 return (Collection *)tselem->id;
97         }
98
99         return NULL;
100 }
101
102 TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata)
103 {
104         struct ObjectsSelectedData *data = customdata;
105         TreeStoreElem *tselem = TREESTORE(te);
106
107         if (outliner_is_collection_tree_element(te)) {
108                 return TRAVERSE_CONTINUE;
109         }
110
111         if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) {
112                 return TRAVERSE_SKIP_CHILDS;
113         }
114
115         BLI_addtail(&data->objects_selected_array, BLI_genericNodeN(te));
116
117         return TRAVERSE_CONTINUE;
118 }
119
120 /* -------------------------------------------------------------------- */
121 /* Poll functions. */
122
123 bool ED_outliner_collections_editor_poll(bContext *C)
124 {
125         SpaceOops *so = CTX_wm_space_outliner(C);
126         return (so != NULL) && ELEM(so->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_LIBRARIES);
127 }
128
129
130 /********************************* New Collection ****************************/
131
132 struct CollectionNewData
133 {
134         bool error;
135         Collection *collection;
136 };
137
138 static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata)
139 {
140         struct CollectionNewData *data = customdata;
141         Collection *collection = outliner_collection_from_tree_element(te);
142
143         if (!collection) {
144                 return TRAVERSE_SKIP_CHILDS;
145         }
146
147         if (data->collection != NULL) {
148                 data->error = true;
149                 return TRAVERSE_BREAK;
150         }
151
152         data->collection = collection;
153         return TRAVERSE_CONTINUE;
154 }
155
156 static int collection_new_exec(bContext *C, wmOperator *op)
157 {
158         SpaceOops *soops = CTX_wm_space_outliner(C);
159         ARegion *ar = CTX_wm_region(C);
160         Main *bmain = CTX_data_main(C);
161         Scene *scene = CTX_data_scene(C);
162         ViewLayer *view_layer = CTX_data_view_layer(C);
163
164         struct CollectionNewData data = {
165                 .error = false,
166                 .collection = NULL,
167         };
168
169         if (RNA_boolean_get(op->ptr, "nested")) {
170                 outliner_build_tree(bmain, scene, view_layer, soops, ar);
171
172                 outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_to_add, &data);
173
174                 if (data.error) {
175                         BKE_report(op->reports, RPT_ERROR, "More than one collection is selected");
176                         return OPERATOR_CANCELLED;
177                 }
178         }
179
180         if (!data.collection && (soops->outlinevis == SO_VIEW_LAYER)) {
181                 data.collection = BKE_collection_master(scene);
182         }
183
184         BKE_collection_add(
185                     bmain,
186                     data.collection,
187                     NULL);
188
189         DEG_id_tag_update(&data.collection->id, DEG_TAG_COPY_ON_WRITE);
190         DEG_relations_tag_update(bmain);
191
192         outliner_cleanup_tree(soops);
193         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
194         return OPERATOR_FINISHED;
195 }
196
197 void OUTLINER_OT_collection_new(wmOperatorType *ot)
198 {
199         /* identifiers */
200         ot->name = "New Collection";
201         ot->idname = "OUTLINER_OT_collection_new";
202         ot->description = "Add a new collection inside selected collection";
203
204         /* api callbacks */
205         ot->exec = collection_new_exec;
206         ot->poll = ED_outliner_collections_editor_poll;
207
208         /* flags */
209         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
210
211         /* properties */
212         PropertyRNA *prop = RNA_def_boolean(ot->srna, "nested", true, "Nested", "Add as child of selected collection");;
213         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
214 }
215
216 /**************************** Delete Collection ******************************/
217
218 struct CollectionEditData {
219         Scene *scene;
220         SpaceOops *soops;
221         GSet *collections_to_edit;
222 };
223
224 static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata)
225 {
226         struct CollectionEditData *data = customdata;
227         Collection *collection = outliner_collection_from_tree_element(te);
228
229         if (!collection) {
230                 return TRAVERSE_SKIP_CHILDS;
231         }
232
233         if (collection == BKE_collection_master(data->scene)) {
234                 /* skip - showing warning/error message might be missleading
235                  * when deleting multiple collections, so just do nothing */
236         }
237         else {
238                 /* Delete, duplicate and link don't edit children, those will come along
239                  * with the parents. */
240                 BLI_gset_add(data->collections_to_edit, collection);
241                 return TRAVERSE_SKIP_CHILDS;
242         }
243
244         return TRAVERSE_CONTINUE;
245 }
246
247 static int collection_delete_exec(bContext *C, wmOperator *op)
248 {
249         Main *bmain = CTX_data_main(C);
250         Scene *scene = CTX_data_scene(C);
251         SpaceOops *soops = CTX_wm_space_outliner(C);
252         struct CollectionEditData data = {.scene = scene, .soops = soops};
253         bool hierarchy = RNA_boolean_get(op->ptr, "hierarchy");
254
255         data.collections_to_edit = BLI_gset_ptr_new(__func__);
256
257         /* We first walk over and find the Collections we actually want to delete (ignoring duplicates). */
258         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
259
260         /* Effectively delete the collections. */
261         GSetIterator collections_to_edit_iter;
262         GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
263                 Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
264
265                 /* Test in case collection got deleted as part of another one. */
266                 if (BLI_findindex(&bmain->collection, collection) != -1) {
267                         BKE_collection_delete(bmain, collection, hierarchy);
268                 }
269         }
270
271         BLI_gset_free(data.collections_to_edit, NULL);
272
273         DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE);
274         DEG_relations_tag_update(bmain);
275
276         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
277
278         return OPERATOR_FINISHED;
279 }
280
281 void OUTLINER_OT_collection_delete(wmOperatorType *ot)
282 {
283         /* identifiers */
284         ot->name = "Delete Collection";
285         ot->idname = "OUTLINER_OT_collection_delete";
286         ot->description = "Delete selected collections";
287
288         /* api callbacks */
289         ot->exec = collection_delete_exec;
290         ot->poll = ED_outliner_collections_editor_poll;
291
292         /* flags */
293         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
294
295         /* properties */
296         PropertyRNA *prop = RNA_def_boolean(ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections");
297         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
298 }
299
300 /****************************** Select Objects *******************************/
301
302 struct CollectionObjectsSelectData {
303         bool error;
304         LayerCollection *layer_collection;
305 };
306
307 static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata)
308 {
309         struct CollectionObjectsSelectData *data = customdata;
310         TreeStoreElem *tselem = TREESTORE(te);
311
312         switch (tselem->type) {
313                 case TSE_LAYER_COLLECTION:
314                         data->layer_collection = te->directdata;
315                         return TRAVERSE_BREAK;
316                 case TSE_R_LAYER:
317                 case TSE_SCENE_COLLECTION_BASE:
318                 case TSE_VIEW_COLLECTION_BASE:
319                         return TRAVERSE_CONTINUE;
320                 default:
321                         return TRAVERSE_SKIP_CHILDS;
322         }
323 }
324
325 static LayerCollection *outliner_active_layer_collection(bContext *C)
326 {
327         SpaceOops *soops = CTX_wm_space_outliner(C);
328
329         struct CollectionObjectsSelectData data = {
330                 .layer_collection = NULL,
331         };
332
333         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_layer_collection, &data);
334         return data.layer_collection;
335 }
336
337 static int collection_objects_select_exec(bContext *C, wmOperator *op)
338 {
339         ViewLayer *view_layer = CTX_data_view_layer(C);
340         LayerCollection *layer_collection = outliner_active_layer_collection(C);
341         bool deselect = STREQ(op->idname, "OUTLINER_OT_collection_objects_deselect");
342
343         if (layer_collection == NULL) {
344                 return OPERATOR_CANCELLED;
345         }
346
347         BKE_layer_collection_objects_select(view_layer, layer_collection, deselect);
348
349         Scene *scene = CTX_data_scene(C);
350         DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE);
351         WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene);
352
353         return OPERATOR_FINISHED;
354 }
355
356 void OUTLINER_OT_collection_objects_select(wmOperatorType *ot)
357 {
358         /* identifiers */
359         ot->name = "Select Objects";
360         ot->idname = "OUTLINER_OT_collection_objects_select";
361         ot->description = "Select objects in collection";
362
363         /* api callbacks */
364         ot->exec = collection_objects_select_exec;
365         ot->poll = ED_outliner_collections_editor_poll;
366
367         /* flags */
368         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
369 }
370
371 void OUTLINER_OT_collection_objects_deselect(wmOperatorType *ot)
372 {
373         /* identifiers */
374         ot->name = "Deselect Objects";
375         ot->idname = "OUTLINER_OT_collection_objects_deselect";
376         ot->description = "Deselect objects in collection";
377
378         /* api callbacks */
379         ot->exec = collection_objects_select_exec;
380         ot->poll = ED_outliner_collections_editor_poll;
381
382         /* flags */
383         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
384 }
385
386 /************************** Duplicate Collection *****************************/
387
388 struct CollectionDuplicateData {
389         TreeElement *te;
390 };
391
392 static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata)
393 {
394         struct CollectionDuplicateData *data = customdata;
395         TreeStoreElem *tselem = TREESTORE(te);
396
397         switch (tselem->type) {
398                 case TSE_LAYER_COLLECTION:
399                         data->te = te;
400                         return TRAVERSE_BREAK;
401                 case TSE_R_LAYER:
402                 case TSE_SCENE_COLLECTION_BASE:
403                 case TSE_VIEW_COLLECTION_BASE:
404                 default:
405                         return TRAVERSE_CONTINUE;
406         }
407 }
408
409 static TreeElement *outliner_active_collection(bContext *C)
410 {
411         SpaceOops *soops = CTX_wm_space_outliner(C);
412
413         struct CollectionDuplicateData data = {
414                 .te = NULL,
415         };
416
417         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_collection, &data);
418         return data.te;
419 }
420
421 static int collection_duplicate_exec(bContext *C, wmOperator *op)
422 {
423         Main *bmain = CTX_data_main(C);
424         SpaceOops *soops = CTX_wm_space_outliner(C);
425         TreeElement *te = outliner_active_collection(C);
426         BLI_assert(te != NULL);
427
428         Collection *collection = outliner_collection_from_tree_element(te);
429         Collection *parent = (te->parent) ? outliner_collection_from_tree_element(te->parent) : NULL;
430
431         if (collection->flag & COLLECTION_IS_MASTER) {
432                 BKE_report(op->reports, RPT_ERROR, "Can't duplicate the master collection");
433                 return OPERATOR_CANCELLED;
434         }
435
436         switch (soops->outlinevis) {
437                 case SO_SCENES:
438                 case SO_VIEW_LAYER:
439                 case SO_LIBRARIES:
440                         BKE_collection_copy(bmain, parent, collection);
441                         break;
442         }
443
444         DEG_relations_tag_update(bmain);
445         WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C));
446
447         return OPERATOR_FINISHED;
448 }
449
450 void OUTLINER_OT_collection_duplicate(wmOperatorType *ot)
451 {
452         /* identifiers */
453         ot->name = "Duplicate Collection";
454         ot->idname = "OUTLINER_OT_collection_duplicate";
455         ot->description = "Duplicate selected collections";
456
457         /* api callbacks */
458         ot->exec = collection_duplicate_exec;
459         ot->poll = ED_outliner_collections_editor_poll;
460
461         /* flags */
462         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
463 }
464
465 /**************************** Link Collection ******************************/
466
467 static int collection_link_exec(bContext *C, wmOperator *UNUSED(op))
468 {
469         Main *bmain = CTX_data_main(C);
470         Scene *scene = CTX_data_scene(C);
471         Collection *active_collection = CTX_data_layer_collection(C)->collection;
472         SpaceOops *soops = CTX_wm_space_outliner(C);
473         struct CollectionEditData data = {.scene = scene, .soops = soops};
474
475         data.collections_to_edit = BLI_gset_ptr_new(__func__);
476
477         /* We first walk over and find the Collections we actually want to link (ignoring duplicates). */
478         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
479
480         /* Effectively link the collections. */
481         GSetIterator collections_to_edit_iter;
482         GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
483                 Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
484                 BKE_collection_child_add(bmain, active_collection, collection);
485                 id_fake_user_clear(&collection->id);
486         }
487
488         BLI_gset_free(data.collections_to_edit, NULL);
489
490         DEG_id_tag_update(&active_collection->id, DEG_TAG_COPY_ON_WRITE);
491         DEG_relations_tag_update(bmain);
492
493         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
494
495         return OPERATOR_FINISHED;
496 }
497
498 void OUTLINER_OT_collection_link(wmOperatorType *ot)
499 {
500         /* identifiers */
501         ot->name = "Link Collection";
502         ot->idname = "OUTLINER_OT_collection_link";
503         ot->description = "Link selected collections to active scene";
504
505         /* api callbacks */
506         ot->exec = collection_link_exec;
507         ot->poll = ED_outliner_collections_editor_poll;
508
509         /* flags */
510         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
511 }
512
513 /************************** Instance Collection ******************************/
514
515 static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op))
516 {
517         Main *bmain = CTX_data_main(C);
518         Scene *scene = CTX_data_scene(C);
519         ViewLayer *view_layer = CTX_data_view_layer(C);
520         SpaceOops *soops = CTX_wm_space_outliner(C);
521         struct CollectionEditData data = {.scene = scene, .soops = soops};
522
523         data.collections_to_edit = BLI_gset_ptr_new(__func__);
524
525         /* We first walk over and find the Collections we actually want to instance (ignoring duplicates). */
526         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
527
528         /* Find an active collection to add to, that doesn't give dependency cycles. */
529         LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer);
530
531         GSetIterator collections_to_edit_iter;
532         GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
533                 Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
534
535                 while (BKE_collection_find_cycle(active_lc->collection, collection)) {
536                         active_lc = BKE_layer_collection_activate_parent(view_layer, active_lc);
537                 }
538         }
539
540         /* Effectively instance the collections. */
541         GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
542                 Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
543                 Object *ob = ED_object_add_type(C, OB_EMPTY, collection->id.name + 2, scene->cursor.location, NULL, false, scene->layact);
544                 ob->dup_group = collection;
545                 ob->transflag |= OB_DUPLICOLLECTION;
546                 id_lib_extern(&collection->id);
547         }
548
549         BLI_gset_free(data.collections_to_edit, NULL);
550
551         DEG_relations_tag_update(bmain);
552
553         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
554
555         return OPERATOR_FINISHED;
556 }
557
558 void OUTLINER_OT_collection_instance(wmOperatorType *ot)
559 {
560         /* identifiers */
561         ot->name = "Instance Collection";
562         ot->idname = "OUTLINER_OT_collection_instance";
563         ot->description = "Instance selected collections to active scene";
564
565         /* api callbacks */
566         ot->exec = collection_instance_exec;
567         ot->poll = ED_outliner_collections_editor_poll;
568
569         /* flags */
570         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
571 }
572
573 /************************** Exclude Collection ******************************/
574
575 static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, void *customdata)
576 {
577         struct CollectionEditData *data = customdata;
578         TreeStoreElem *tselem = TREESTORE(te);
579
580         if (!(tselem && tselem->type == TSE_LAYER_COLLECTION)) {
581                 return TRAVERSE_CONTINUE;
582         }
583
584         LayerCollection *lc = te->directdata;
585
586         if (lc->collection->flag & COLLECTION_IS_MASTER) {
587                 /* skip - showing warning/error message might be missleading
588                  * when deleting multiple collections, so just do nothing */
589         }
590         else {
591                 /* Delete, duplicate and link don't edit children, those will come along
592                  * with the parents. */
593                 BLI_gset_add(data->collections_to_edit, lc);
594         }
595
596         return TRAVERSE_CONTINUE;
597 }
598
599 static bool collections_view_layer_poll(bContext *C, bool clear, int flag)
600 {
601         /* Poll function so the right click menu show current state of selected collections. */
602         SpaceOops *soops = CTX_wm_space_outliner(C);
603         if (!(soops && soops->outlinevis == SO_VIEW_LAYER)) {
604                 return false;
605         }
606
607         Scene *scene = CTX_data_scene(C);
608         struct CollectionEditData data = {.scene = scene, .soops = soops};
609         data.collections_to_edit = BLI_gset_ptr_new(__func__);
610         bool result = false;
611
612         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data);
613
614         GSetIterator collections_to_edit_iter;
615         GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
616                 LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter);
617
618                 if (clear && (lc->flag & flag)) {
619                         result = true;
620                 }
621                 else if (!clear && !(lc->flag & flag)) {
622                         result = true;
623                 }
624         }
625
626         BLI_gset_free(data.collections_to_edit, NULL);
627         return result;
628 }
629
630 static bool collections_exclude_set_poll(bContext *C)
631 {
632         return collections_view_layer_poll(C, false, LAYER_COLLECTION_EXCLUDE);
633 }
634
635 static bool collections_exclude_clear_poll(bContext *C)
636 {
637         return collections_view_layer_poll(C, true, LAYER_COLLECTION_EXCLUDE);
638 }
639
640 static bool collections_holdout_set_poll(bContext *C)
641 {
642         return collections_view_layer_poll(C, false, LAYER_COLLECTION_HOLDOUT);
643 }
644
645 static bool collections_holdout_clear_poll(bContext *C)
646 {
647         return collections_view_layer_poll(C, true, LAYER_COLLECTION_HOLDOUT);
648 }
649
650 static bool collections_indirect_only_set_poll(bContext *C)
651 {
652         return collections_view_layer_poll(C, false, LAYER_COLLECTION_INDIRECT_ONLY);
653 }
654
655 static bool collections_indirect_only_clear_poll(bContext *C)
656 {
657         return collections_view_layer_poll(C, true, LAYER_COLLECTION_INDIRECT_ONLY);
658 }
659
660 static void layer_collection_flag_recursive_set(LayerCollection *lc, int flag)
661 {
662         for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
663                 if (lc->flag & flag) {
664                         nlc->flag |= flag;
665                 }
666                 else {
667                         nlc->flag &= ~flag;
668                 }
669
670                 layer_collection_flag_recursive_set(nlc, flag);
671         }
672 }
673
674 static int collection_view_layer_exec(bContext *C, wmOperator *op)
675 {
676         Main *bmain = CTX_data_main(C);
677         Scene *scene = CTX_data_scene(C);
678         ViewLayer *view_layer = CTX_data_view_layer(C);
679         SpaceOops *soops = CTX_wm_space_outliner(C);
680         struct CollectionEditData data = {.scene = scene, .soops = soops};
681         bool clear = strstr(op->idname, "clear") != NULL;
682         int flag = strstr(op->idname, "holdout") ?       LAYER_COLLECTION_HOLDOUT :
683                    strstr(op->idname, "indirect_only") ? LAYER_COLLECTION_INDIRECT_ONLY :
684                                                          LAYER_COLLECTION_EXCLUDE;
685
686         data.collections_to_edit = BLI_gset_ptr_new(__func__);
687
688         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data);
689
690         GSetIterator collections_to_edit_iter;
691         GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
692                 LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter);
693
694                 if (!(lc->collection->flag & COLLECTION_IS_MASTER)) {
695                         if (clear) {
696                                 lc->flag &= ~flag;
697                         }
698                         else {
699                                 lc->flag |= flag;
700                         }
701
702                         layer_collection_flag_recursive_set(lc, flag);
703                 }
704         }
705
706         BLI_gset_free(data.collections_to_edit, NULL);
707
708         BKE_layer_collection_sync(scene, view_layer);
709         DEG_relations_tag_update(bmain);
710
711         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
712
713         return OPERATOR_FINISHED;
714 }
715
716 void OUTLINER_OT_collection_exclude_set(wmOperatorType *ot)
717 {
718         /* identifiers */
719         ot->name = "Set Exclude";
720         ot->idname = "OUTLINER_OT_collection_exclude_set";
721         ot->description = "Exclude collection from the active view layer";
722
723         /* api callbacks */
724         ot->exec = collection_view_layer_exec;
725         ot->poll = collections_exclude_set_poll;
726
727         /* flags */
728         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
729 }
730
731 void OUTLINER_OT_collection_exclude_clear(wmOperatorType *ot)
732 {
733         /* identifiers */
734         ot->name = "Clear Exclude";
735         ot->idname = "OUTLINER_OT_collection_exclude_clear";
736         ot->description = "Include collection in the active view layer";
737
738         /* api callbacks */
739         ot->exec = collection_view_layer_exec;
740         ot->poll = collections_exclude_clear_poll;
741
742         /* flags */
743         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
744 }
745
746 void OUTLINER_OT_collection_holdout_set(wmOperatorType *ot)
747 {
748         /* identifiers */
749         ot->name = "Set Holdout";
750         ot->idname = "OUTLINER_OT_collection_holdout_set";
751         ot->description = "Mask collection in the active view layer";
752
753         /* api callbacks */
754         ot->exec = collection_view_layer_exec;
755         ot->poll = collections_holdout_set_poll;
756
757         /* flags */
758         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
759 }
760
761 void OUTLINER_OT_collection_holdout_clear(wmOperatorType *ot)
762 {
763         /* identifiers */
764         ot->name = "Clear Holdout";
765         ot->idname = "OUTLINER_OT_collection_holdout_clear";
766         ot->description = "Clear masking of collection in the active view layer";
767
768         /* api callbacks */
769         ot->exec = collection_view_layer_exec;
770         ot->poll = collections_holdout_clear_poll;
771
772         /* flags */
773         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
774 }
775
776 void OUTLINER_OT_collection_indirect_only_set(wmOperatorType *ot)
777 {
778         /* identifiers */
779         ot->name = "Set Indirect Only";
780         ot->idname = "OUTLINER_OT_collection_indirect_only_set";
781         ot->description = "Set collection to only contribute indirectly (through shadows and reflections) in the view layer";
782
783         /* api callbacks */
784         ot->exec = collection_view_layer_exec;
785         ot->poll = collections_indirect_only_set_poll;
786
787         /* flags */
788         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
789 }
790
791 void OUTLINER_OT_collection_indirect_only_clear(wmOperatorType *ot)
792 {
793         /* identifiers */
794         ot->name = "Clear Indirect Only";
795         ot->idname = "OUTLINER_OT_collection_indirect_only_clear";
796         ot->description = "Clear collection contributing only indirectly in the view layer";
797
798         /* api callbacks */
799         ot->exec = collection_view_layer_exec;
800         ot->poll = collections_indirect_only_clear_poll;
801
802         /* flags */
803         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
804 }
805
806 /**
807  * Populates the \param objects ListBase with all the outliner selected objects
808  * We store it as (Object *)LinkData->data
809  * \param objects expected to be empty
810  */
811 void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects)
812 {
813         SpaceOops *soops = CTX_wm_space_outliner(C);
814         struct ObjectsSelectedData data = {{NULL}};
815         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
816         LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
817                 TreeElement *ten_selected = (TreeElement *)link->data;
818                 Object *ob = (Object *)TREESTORE(ten_selected)->id;
819                 BLI_addtail(objects, BLI_genericNodeN(ob));
820         }
821         BLI_freelistN(&data.objects_selected_array);
822 }