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