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