Blender V4.3
object_collection.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstring>
10
11#include "BLI_path_utils.hh"
12#include "BLI_string.h"
13#include "BLI_utildefines.h"
14
16#include "DNA_object_types.h"
17#include "DNA_scene_types.h"
18
19#include "BKE_collection.hh"
20#include "BKE_context.hh"
21#include "BKE_file_handler.hh"
22#include "BKE_idprop.hh"
23#include "BKE_layer.hh"
24#include "BKE_lib_id.hh"
25#include "BKE_main.hh"
26#include "BKE_object.hh"
27#include "BKE_report.hh"
28#include "BKE_screen.hh"
29
30#include "BLT_translation.hh"
31
32#include "DEG_depsgraph.hh"
34
35#include "ED_object.hh"
36#include "ED_screen.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43#include "RNA_enum_types.hh"
44#include "RNA_prototypes.hh"
45
46#include "UI_interface.hh"
47#include "UI_interface_icons.hh"
48
49#include "object_intern.hh"
50
51namespace blender::ed::object {
52
53/********************* 3d view operators ***********************/
54
55/* can be called with C == nullptr */
57 PointerRNA * /*ptr*/,
58 PropertyRNA * /*prop*/,
59 bool *r_free)
60{
61 Main *bmain = CTX_data_main(C);
62 Scene *scene = CTX_data_scene(C);
63 Object *ob;
64 EnumPropertyItem *item = nullptr, item_tmp = {0};
65 int totitem = 0;
66
67 if (C == nullptr) {
69 }
70
71 ob = context_object(C);
72
73 /* check that the object exists */
74 if (ob) {
75 Collection *collection;
76 int i = 0, count = 0;
77
78 /* if 2 or more collections, add option to add to all collections */
79 collection = nullptr;
80 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
81 count++;
82 }
83
84 if (count >= 2) {
85 item_tmp.identifier = item_tmp.name = "All Collections";
86 item_tmp.value = INT_MAX; /* this will give nullptr on lookup */
87 RNA_enum_item_add(&item, &totitem, &item_tmp);
88 RNA_enum_item_add_separator(&item, &totitem);
89 }
90
91 /* add collections */
92 collection = nullptr;
93 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
94 item_tmp.identifier = item_tmp.name = collection->id.name + 2;
95 item_tmp.icon = UI_icon_color_from_collection(collection);
96 item_tmp.value = i;
97 RNA_enum_item_add(&item, &totitem, &item_tmp);
98 i++;
99 }
100 }
101
102 RNA_enum_item_end(&item, &totitem);
103 *r_free = true;
104
105 return item;
106}
107
108/* get the collection back from the enum index, quite awkward and UI specific */
110 Scene *scene,
111 Object *ob,
112 const int collection_object_index)
113{
114 Collection *collection = nullptr;
115 int i = 0;
116 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
117 if (i == collection_object_index) {
118 break;
119 }
120 i++;
121 }
122
123 return collection;
124}
125
127{
128 Object *ob = context_object(C);
129 Main *bmain = CTX_data_main(C);
130 Scene *scene = CTX_data_scene(C);
131 int single_collection_index = RNA_enum_get(op->ptr, "collection");
133 bmain, scene, ob, single_collection_index);
134 bool is_cycle = false;
135 bool changed_multi = false;
136
137 if (ob == nullptr) {
138 return OPERATOR_CANCELLED;
139 }
140
141 /* now add all selected objects to the collection(s) */
142 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
143 if (single_collection && collection != single_collection) {
144 continue;
145 }
146 if (!BKE_collection_has_object(collection, ob)) {
147 continue;
148 }
149
150 bool changed = false;
151 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
152 if (BKE_collection_has_object(collection, base->object)) {
153 continue;
154 }
155
156 if (!BKE_collection_object_cyclic_check(bmain, base->object, collection)) {
157 BKE_collection_object_add(bmain, collection, base->object);
158 changed = true;
159 }
160 else {
161 is_cycle = true;
162 }
163 }
165
166 if (changed) {
168 changed_multi = true;
169 }
170 }
172
173 if (is_cycle) {
174 BKE_report(op->reports, RPT_WARNING, "Skipped some collections because of cycle detected");
175 }
176
177 if (!changed_multi) {
178 return OPERATOR_CANCELLED;
179 }
180
183
184 return OPERATOR_FINISHED;
185}
186
188{
189 PropertyRNA *prop;
190
191 /* identifiers */
192 ot->name = "Add Selected to Active Collection";
193 ot->description = "Add the object to an object collection that contains the active object";
194 ot->idname = "COLLECTION_OT_objects_add_active";
195
196 /* api callbacks */
200
201 /* flags */
203
204 /* properties */
205 prop = RNA_def_enum(ot->srna,
206 "collection",
208 0,
209 "Collection",
210 "The collection to add other selected objects to");
213 ot->prop = prop;
214}
215
217{
218 Main *bmain = CTX_data_main(C);
219 Scene *scene = CTX_data_scene(C);
220 ViewLayer *view_layer = CTX_data_view_layer(C);
221 BKE_view_layer_synced_ensure(scene, view_layer);
223 int single_collection_index = RNA_enum_get(op->ptr, "collection");
225 bmain, scene, ob, single_collection_index);
226 bool changed_multi = false;
227
228 if (ob == nullptr) {
229 return OPERATOR_CANCELLED;
230 }
231
232 /* Linking to same collection requires its own loop so we can avoid
233 * looking up the active objects collections each time. */
234 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
235 if (single_collection && collection != single_collection) {
236 continue;
237 }
238
239 if (BKE_collection_has_object(collection, ob)) {
240 /* Remove collections from selected objects */
241 bool changed = false;
242 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
243 BKE_collection_object_remove(bmain, collection, base->object, false);
244 changed = true;
245 }
247
248 if (changed) {
250 changed_multi = true;
251 }
252 }
253 }
255
256 if (!changed_multi) {
257 BKE_report(op->reports, RPT_ERROR, "Active object contains no collections");
258 }
259
262
263 return OPERATOR_FINISHED;
264}
265
267{
268 PropertyRNA *prop;
269
270 /* identifiers */
271 ot->name = "Remove Selected from Active Collection";
272 ot->description = "Remove the object from an object collection that contains the active object";
273 ot->idname = "COLLECTION_OT_objects_remove_active";
274
275 /* api callbacks */
279
280 /* flags */
282
283 /* properties */
284 prop = RNA_def_enum(ot->srna,
285 "collection",
287 0,
288 "Collection",
289 "The collection to remove other selected objects from");
292 ot->prop = prop;
293}
294
296{
297 Main *bmain = CTX_data_main(C);
298 Scene *scene = CTX_data_scene(C);
299
300 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
301 BKE_object_groups_clear(bmain, scene, base->object);
302 }
304
307
308 return OPERATOR_FINISHED;
309}
310
312{
313 /* identifiers */
314 ot->name = "Remove from All Collections";
315 ot->description = "Remove selected objects from all collections";
316 ot->idname = "COLLECTION_OT_objects_remove_all";
317
318 /* api callbacks */
321
322 /* flags */
324}
325
327{
328 Object *ob = context_object(C);
329 Main *bmain = CTX_data_main(C);
330 Scene *scene = CTX_data_scene(C);
331 int single_collection_index = RNA_enum_get(op->ptr, "collection");
333 bmain, scene, ob, single_collection_index);
334 bool changed_multi = false;
335
336 if (ob == nullptr) {
337 return OPERATOR_CANCELLED;
338 }
339
340 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
341 if (single_collection && collection != single_collection) {
342 continue;
343 }
344 if (!BKE_collection_has_object(collection, ob)) {
345 continue;
346 }
347
348 /* now remove all selected objects from the collection */
349 bool changed = false;
350 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
351 BKE_collection_object_remove(bmain, collection, base->object, false);
352 changed = true;
353 }
355
356 if (changed) {
358 changed_multi = true;
359 }
360 }
362
363 if (!changed_multi) {
364 return OPERATOR_CANCELLED;
365 }
366
369
370 return OPERATOR_FINISHED;
371}
372
374{
375 PropertyRNA *prop;
376
377 /* identifiers */
378 ot->name = "Remove from Collection";
379 ot->description = "Remove selected objects from a collection";
380 ot->idname = "COLLECTION_OT_objects_remove";
381
382 /* api callbacks */
386
387 /* flags */
389
390 /* properties */
391 prop = RNA_def_enum(ot->srna,
392 "collection",
394 0,
395 "Collection",
396 "The collection to remove this object from");
399 ot->prop = prop;
400}
401
403{
404 Main *bmain = CTX_data_main(C);
405 char name[MAX_ID_NAME - 2]; /* id name */
406 bool changed = false;
407
408 RNA_string_get(op->ptr, "name", name);
409
410 Collection *collection = BKE_collection_add(bmain, nullptr, name);
411 id_fake_user_set(&collection->id);
412
413 CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
414 BKE_collection_object_add(bmain, collection, base->object);
415 changed = true;
416 }
418
419 if (changed) {
421 }
422
425
426 return OPERATOR_FINISHED;
427}
428
430{
431 /* identifiers */
432 ot->name = "Create New Collection";
433 ot->description = "Create an object collection from selected objects";
434 ot->idname = "COLLECTION_OT_create";
435
436 /* api callbacks */
439
440 /* flags */
442
444 ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Name of the new collection");
445}
446
447static bool collection_exporter_common_check(const Collection *collection)
448{
449 return collection != nullptr &&
450 !(ID_IS_LINKED(&collection->id) || ID_IS_OVERRIDE_LIBRARY(&collection->id));
451}
452
454{
455 const Collection *collection = CTX_data_collection(C);
456 return collection_exporter_common_check(collection);
457}
458
460{
461 const Collection *collection = CTX_data_collection(C);
462 return collection_exporter_common_check(collection) &&
463 !BLI_listbase_is_empty(&collection->exporters);
464}
465
467{
468 return CTX_data_view_layer(C) != nullptr;
469}
470
472{
473 using namespace blender;
474 Collection *collection = CTX_data_collection(C);
475 ListBase *exporters = &collection->exporters;
476
477 char name[MAX_ID_NAME - 2]; /* id name */
478 RNA_string_get(op->ptr, "name", name);
479
481 if (!fh) {
482 BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", name);
483 return OPERATOR_CANCELLED;
484 }
485
486 if (!WM_operatortype_find(fh->export_operator, true)) {
488 op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
489 return OPERATOR_CANCELLED;
490 }
491
492 /* Add a new #CollectionExport item to our handler list and fill it with #FileHandlerType
493 * information. Also load in the operator's properties now as well. */
494 CollectionExport *data = MEM_cnew<CollectionExport>("CollectionExport");
495 STRNCPY(data->fh_idname, fh->idname);
496
497 BKE_collection_exporter_name_set(exporters, data, fh->label);
498
499 IDPropertyTemplate val{};
500 data->export_properties = IDP_New(IDP_GROUP, &val, "export_properties");
501 data->flag |= IO_HANDLER_PANEL_OPEN;
502
503 BLI_addtail(exporters, data);
504 collection->active_exporter_index = BLI_listbase_count(exporters) - 1;
505
508
511
512 return OPERATOR_FINISHED;
513}
514
516{
517 /* identifiers */
518 ot->name = "Add Exporter";
519 ot->description = "Add Exporter";
520 ot->idname = "COLLECTION_OT_exporter_add";
521
522 /* api callbacks */
525
526 /* flags */
528
529 RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "FileHandler idname");
530}
531
533{
534 Collection *collection = CTX_data_collection(C);
535 ListBase *exporters = &collection->exporters;
536
537 int index = RNA_int_get(op->ptr, "index");
538 CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
539 if (!data) {
540 return OPERATOR_CANCELLED;
541 }
542
543 BLI_remlink(exporters, data);
545
546 MEM_freeN(data);
547
548 const int count = BLI_listbase_count(exporters);
549 const int new_index = count == 0 ? 0 : std::min(collection->active_exporter_index, count - 1);
550 collection->active_exporter_index = new_index;
551
554
557
558 return OPERATOR_FINISHED;
559}
560
562 wmOperator *op,
563 const wmEvent * /*event*/)
564{
566 C, op, IFACE_("Remove exporter?"), nullptr, IFACE_("Delete"), ALERT_ICON_NONE, false);
567}
568
570{
571 /* identifiers */
572 ot->name = "Remove Exporter";
573 ot->description = "Remove Exporter";
574 ot->idname = "COLLECTION_OT_exporter_remove";
575
576 /* api callbacks */
580
581 /* flags */
583
584 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
585}
586
588 wmOperator *op,
589 CollectionExport *data,
590 Collection *collection,
591 const bool report_success)
592{
593 using namespace blender;
594 bke::FileHandlerType *fh = bke::file_handler_find(data->fh_idname);
595 if (!fh) {
596 BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", data->fh_idname);
597 return OPERATOR_CANCELLED;
598 }
599
601 if (!ot) {
603 op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
604 return OPERATOR_CANCELLED;
605 }
606
607 /* Execute operator with our stored properties. */
608 /* TODO: Cascade settings down from parent collections(?) */
609 IDProperty *op_props = IDP_CopyProperty(data->export_properties);
610 PointerRNA properties = RNA_pointer_create(nullptr, ot->srna, op_props);
611 const char *collection_name = collection->id.name + 2;
612
613 /* Ensure we have a valid filepath set. Create one if the user has not specified anything yet. */
614 char filepath[FILE_MAX];
615 RNA_string_get(&properties, "filepath", filepath);
616 if (!filepath[0]) {
618 filepath, sizeof(filepath), "//", fh->get_default_filename(collection_name).c_str());
619 }
620 else {
621 const char *filename = BLI_path_basename(filepath);
622 if (!filename[0] || !BLI_path_extension(filename)) {
623 BKE_reportf(op->reports, RPT_ERROR, "File path '%s' is not a valid file", filepath);
624
625 IDP_FreeProperty(op_props);
626 return OPERATOR_CANCELLED;
627 }
628 }
629
630 const Main *bmain = CTX_data_main(C);
631 BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
632
633 /* Ensure that any properties from when this operator was "last used" are cleared. Save them for
634 * restoration later. Otherwise properties from a regular File->Export may contaminate this
635 * collection export. */
636 IDProperty *last_properties = ot->last_properties;
637 ot->last_properties = nullptr;
638
639 RNA_string_set(&properties, "filepath", filepath);
640 RNA_string_set(&properties, "collection", collection_name);
641 int op_result = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &properties, nullptr);
642
643 /* Free the "last used" properties that were just set from the collection export and restore the
644 * original "last used" properties. */
645 if (ot->last_properties) {
647 }
648 ot->last_properties = last_properties;
649
650 IDP_FreeProperty(op_props);
651
652 if (report_success && op_result == OPERATOR_FINISHED) {
653 BKE_reportf(op->reports, RPT_INFO, "Exported '%s'", filepath);
654 }
655
656 return op_result;
657}
658
660{
661 Collection *collection = CTX_data_collection(C);
662 ListBase *exporters = &collection->exporters;
663
664 int index = RNA_int_get(op->ptr, "index");
665 CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
666 if (!data) {
667 return OPERATOR_CANCELLED;
668 }
669
670 return collection_exporter_export(C, op, data, collection, true);
671}
672
674{
675 /* identifiers */
676 ot->name = "Export";
677 ot->description = "Invoke the export operation";
678 ot->idname = "COLLECTION_OT_exporter_export";
679
680 /* api callbacks */
683
684 /* flags */
685 ot->flag = 0;
686
687 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
688}
689
694
696 wmOperator *op,
697 Collection *collection,
699{
700 ListBase *exporters = &collection->exporters;
701 int files_num = 0;
702
703 LISTBASE_FOREACH (CollectionExport *, data, exporters) {
704 if (collection_exporter_export(C, op, data, collection, false) != OPERATOR_FINISHED) {
705 /* Do not continue calling exporters if we encounter one that fails. */
706 return OPERATOR_CANCELLED;
707 }
708 else {
709 files_num++;
710 }
711 }
712
713 if (files_num) {
714 stats.successful_exports_num += files_num;
715 stats.collections_num++;
716 }
717 return OPERATOR_FINISHED;
718}
719
721{
722 Collection *collection = CTX_data_collection(C);
724 int result = collection_export(C, op, collection, stats);
725
726 /* Only report if nothing was cancelled along the way. We don't want this UI report to happen
727 * over-top any reports from the actual failures. */
728 if (result == OPERATOR_FINISHED && stats.successful_exports_num > 0) {
730 RPT_INFO,
731 "Exported %d files from collection '%s'",
733 collection->id.name + 2);
734 }
735
736 return result;
737}
738
740{
741 /* identifiers */
742 ot->name = "Export All";
743 ot->description = "Invoke all configured exporters on this collection";
744 ot->idname = "COLLECTION_OT_export_all";
745
746 /* api callbacks */
749
750 /* flags */
751 ot->flag = 0;
752}
753
755 wmOperator *op,
756 LayerCollection *layer_collection,
758{
759 /* Skip collections which have been Excluded in the View Layer. */
760 if (layer_collection->flag & LAYER_COLLECTION_EXCLUDE) {
761 return OPERATOR_FINISHED;
762 }
763
764 if (!collection_exporter_common_check(layer_collection->collection)) {
765 return OPERATOR_FINISHED;
766 }
767
768 if (collection_export(C, op, layer_collection->collection, stats) != OPERATOR_FINISHED) {
769 return OPERATOR_CANCELLED;
770 }
771
772 LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
773 if (collection_export_recursive(C, op, child, stats) != OPERATOR_FINISHED) {
774 return OPERATOR_CANCELLED;
775 }
776 }
777
778 return OPERATOR_FINISHED;
779}
780
782{
783 ViewLayer *view_layer = CTX_data_view_layer(C);
784
786 LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
787 if (collection_export_recursive(C, op, layer_collection, stats) != OPERATOR_FINISHED) {
788 return OPERATOR_CANCELLED;
789 }
790 }
791
792 /* Only report if nothing was cancelled along the way. We don't want this UI report to happen
793 * over-top any reports from the actual failures. */
794 if (stats.successful_exports_num > 0) {
796 RPT_INFO,
797 "Exported %d files from %d collections",
799 stats.collections_num);
800 }
801
802 return OPERATOR_FINISHED;
803}
804
806{
807 /* identifiers */
808 ot->name = "Export All Collections";
809 ot->description = "Invoke all configured exporters for all collections";
810 ot->idname = "WM_OT_collection_export_all";
811
812 /* api callbacks */
815
816 /* flags */
817 ot->flag = 0;
818}
819
820static void collection_exporter_menu_draw(const bContext * /*C*/, Menu *menu)
821{
822 using namespace blender;
823 uiLayout *layout = menu->layout;
824
825 /* Add all file handlers capable of being exported to the menu. */
826 bool at_least_one = false;
827 for (const auto &fh : bke::file_handlers()) {
828 if (WM_operatortype_find(fh->export_operator, true)) {
830 layout, fh->label, ICON_NONE, "COLLECTION_OT_exporter_add", "name", fh->idname);
831 at_least_one = true;
832 }
833 }
834
835 if (!at_least_one) {
836 uiItemL(layout, IFACE_("No file handlers available"), ICON_NONE);
837 }
838}
839
854
855/****************** properties window operators *********************/
856
858{
859 Object *ob = context_object(C);
860 Main *bmain = CTX_data_main(C);
861
862 if (ob == nullptr) {
863 return OPERATOR_CANCELLED;
864 }
865
866 Collection *collection = BKE_collection_add(bmain, nullptr, "Collection");
867 id_fake_user_set(&collection->id);
868 BKE_collection_object_add(bmain, collection, ob);
869
872
874
875 return OPERATOR_FINISHED;
876}
877
879{
880 /* identifiers */
881 ot->name = "Add to Collection";
882 ot->idname = "OBJECT_OT_collection_add";
883 ot->description = "Add an object to a new collection";
884
885 /* api callbacks */
888
889 /* flags */
891}
892
894{
895 Main *bmain = CTX_data_main(C);
896 Object *ob = context_object(C);
897 Collection *collection = static_cast<Collection *>(
898 BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")));
899
900 if (ELEM(nullptr, ob, collection)) {
901 return OPERATOR_CANCELLED;
902 }
903
904 /* Early return check, if the object is already in collection
905 * we could skip all the dependency check and just consider
906 * operator is finished.
907 */
908 if (BKE_collection_has_object(collection, ob)) {
909 return OPERATOR_FINISHED;
910 }
911
912 /* Currently this should not be allowed (might be supported in the future though...). */
913 if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
914 BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is overridden");
915 return OPERATOR_CANCELLED;
916 }
917 /* Linked collections are already checked for by using RNA_collection_local_itemf
918 * but operator can be called without invoke */
919 if (!ID_IS_EDITABLE(&collection->id)) {
920 BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is linked");
921 return OPERATOR_CANCELLED;
922 }
923
924 /* Adding object to collection which is used as dupli-collection for self is bad idea.
925 *
926 * It is also bad idea to add object to collection which is in collection which
927 * contains our current object.
928 */
929 if (BKE_collection_object_cyclic_check(bmain, ob, collection)) {
931 RPT_ERROR,
932 "Could not add the collection because of dependency cycle detected");
933 return OPERATOR_CANCELLED;
934 }
935
936 BKE_collection_object_add(bmain, collection, ob);
937
940
942
943 return OPERATOR_FINISHED;
944}
945
947{
948 PropertyRNA *prop;
949
950 /* identifiers */
951 ot->name = "Link to Collection";
952 ot->idname = "OBJECT_OT_collection_link";
953 ot->description = "Add an object to an existing collection";
954
955 /* api callbacks */
959
960 /* flags */
962
963 /* properties */
964 prop = RNA_def_enum(ot->srna, "collection", rna_enum_dummy_NULL_items, 0, "Collection", "");
967 ot->prop = prop;
968}
969
971{
972 Main *bmain = CTX_data_main(C);
973 Object *ob = context_object(C);
974 Collection *collection = static_cast<Collection *>(
975 CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data);
976
977 if (!ob || !collection) {
978 return OPERATOR_CANCELLED;
979 }
980 if (!ID_IS_EDITABLE(collection) || ID_IS_OVERRIDE_LIBRARY(collection)) {
982 RPT_ERROR,
983 "Cannot remove an object from a linked or library override collection");
984 return OPERATOR_CANCELLED;
985 }
986
987 BKE_collection_object_remove(bmain, collection, ob, false);
988
991
993
994 return OPERATOR_FINISHED;
995}
996
998{
999 /* identifiers */
1000 ot->name = "Remove Collection";
1001 ot->idname = "OBJECT_OT_collection_remove";
1002 ot->description = "Remove the active object from this collection";
1003
1004 /* api callbacks */
1007
1008 /* flags */
1010}
1011
1013{
1014 Main *bmain = CTX_data_main(C);
1015 Collection *collection = CTX_data_collection(C);
1016
1017 if (!collection) {
1018 return OPERATOR_CANCELLED;
1019 }
1020 if (collection->flag & COLLECTION_IS_MASTER) {
1021 return OPERATOR_CANCELLED;
1022 }
1023 BLI_assert((collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0);
1024 if (ID_IS_OVERRIDE_LIBRARY(collection) &&
1025 collection->id.override_library->hierarchy_root != &collection->id)
1026 {
1027 BKE_report(op->reports,
1028 RPT_ERROR,
1029 "Cannot unlink a library override collection which is not the root of its override "
1030 "hierarchy");
1031 return OPERATOR_CANCELLED;
1032 }
1033
1034 BKE_id_delete(bmain, collection);
1035
1037
1039
1040 return OPERATOR_FINISHED;
1041}
1042
1044{
1045 Collection *collection = CTX_data_collection(C);
1046
1047 if (!collection) {
1048 return false;
1049 }
1050 if (collection->flag & COLLECTION_IS_MASTER) {
1051 return false;
1052 }
1053 BLI_assert((collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0);
1054 if (ID_IS_OVERRIDE_LIBRARY(collection) &&
1055 collection->id.override_library->hierarchy_root != &collection->id)
1056 {
1057 return false;
1058 }
1059
1060 return ED_operator_objectmode(C);
1061}
1062
1064{
1065 /* identifiers */
1066 ot->name = "Unlink Collection";
1067 ot->idname = "OBJECT_OT_collection_unlink";
1068 ot->description = "Unlink the collection from all objects";
1069
1070 /* api callbacks */
1073
1074 /* flags */
1076}
1077
1078/* Select objects in the same collection as the active */
1080{
1081 Scene *scene = CTX_data_scene(C);
1082 Collection *collection = static_cast<Collection *>(
1083 CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data);
1084
1085 if (!collection) {
1086 return OPERATOR_CANCELLED;
1087 }
1088
1089 CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
1090 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
1091 if (BKE_collection_has_object_recursive(collection, base->object)) {
1092 base_select(base, BA_SELECT);
1093 }
1094 }
1095 }
1097
1100
1101 return OPERATOR_FINISHED;
1102}
1103
1105{
1106 /* identifiers */
1107 ot->name = "Select Objects in Collection";
1108 ot->idname = "OBJECT_OT_collection_objects_select";
1109 ot->description = "Select all objects in collection";
1110
1111 /* api callbacks */
1114
1115 /* flags */
1117}
1118
1119} // namespace blender::ed::object
bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, bool free_us)
bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
void BKE_collection_exporter_free_data(CollectionExport *data)
#define FOREACH_COLLECTION_BEGIN(_bmain, _scene, Type, _instance)
Collection * BKE_collection_add(Main *bmain, Collection *collection_parent, const char *name_custom)
bool BKE_collection_has_object(Collection *collection, const Object *ob)
void BKE_collection_exporter_name_set(const ListBase *exporters, CollectionExport *data, const char *newname)
Collection * BKE_collection_object_find(Main *bmain, Scene *scene, Collection *collection, Object *ob)
bool BKE_collection_object_cyclic_check(Main *bmain, Object *object, Collection *collection)
#define FOREACH_COLLECTION_END
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
#define CTX_DATA_BEGIN(C, Type, instance, member)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
Collection * CTX_data_collection(const bContext *C)
#define CTX_DATA_END
ViewLayer * CTX_data_view_layer(const bContext *C)
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
IDProperty * IDP_New(char type, const IDPropertyTemplate *val, const char *name, eIDPropertyFlag flags={}) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:989
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:861
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
void BKE_view_layer_need_resync_tag(ViewLayer *view_layer)
void BKE_id_delete(Main *bmain, void *idv) ATTR_NONNULL()
void id_fake_user_set(ID *id)
Definition lib_id.cc:389
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:832
General operations, lookup, etc. for blender objects.
void BKE_object_groups_clear(Main *bmain, Scene *scene, Object *object)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
#define BLI_path_join(...)
const char * BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
#define MAX_ID_NAME
Definition DNA_ID.h:377
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:725
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ IDP_GROUP
Object groups, one object can be in many groups at once.
@ COLLECTION_IS_MASTER
@ IO_HANDLER_PANEL_OPEN
@ LAYER_COLLECTION_EXCLUDE
Object is a sort of wrapper for general info.
#define BASE_SELECTED(v3d, base)
#define BASE_SELECTABLE(v3d, base)
bool ED_operator_objectmode(bContext *C)
const EnumPropertyItem * RNA_collection_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool *r_free)
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:321
void uiItemL(uiLayout *layout, const char *name, int icon)
void uiItemStringO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, const char *value)
@ ALERT_ICON_NONE
int UI_icon_color_from_collection(const Collection *collection)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_DRAW
Definition WM_types.hh:428
#define ND_OB_SELECT
Definition WM_types.hh:409
#define ND_SPACE_PROPERTIES
Definition WM_types.hh:495
#define NC_SCENE
Definition WM_types.hh:345
#define NC_GROUP
Definition WM_types.hh:350
#define NA_EDITED
Definition WM_types.hh:550
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
#define NC_OBJECT
Definition WM_types.hh:346
#define NC_SPACE
Definition WM_types.hh:359
#define ND_SPACE_OUTLINER
Definition WM_types.hh:493
int count
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
FileHandlerType * file_handler_find(StringRef idname)
Span< std::unique_ptr< FileHandlerType > > file_handlers()
static int collection_exporter_export(bContext *C, wmOperator *op, CollectionExport *data, Collection *collection, const bool report_success)
static void COLLECTION_OT_exporter_add(wmOperatorType *ot)
static int select_grouped_exec(bContext *C, wmOperator *)
static int collection_add_exec(bContext *C, wmOperator *)
static bool collection_unlink_poll(bContext *C)
static int collection_objects_remove_all_exec(bContext *C, wmOperator *)
static bool collection_export_all_poll(bContext *C)
static int collection_objects_remove_exec(bContext *C, wmOperator *op)
static void COLLECTION_OT_exporter_remove(wmOperatorType *ot)
static void COLLECTION_OT_export_all(wmOperatorType *ot)
static bool collection_exporter_common_check(const Collection *collection)
static int objects_remove_active_exec(bContext *C, wmOperator *op)
void OBJECT_OT_collection_objects_select(wmOperatorType *ot)
void base_select(Base *base, eObjectSelect_Mode mode)
void OBJECT_OT_collection_link(wmOperatorType *ot)
Object * context_object(const bContext *C)
static bool collection_exporter_poll(bContext *C)
static int collection_link_exec(bContext *C, wmOperator *op)
static Collection * collection_object_active_find_index(Main *bmain, Scene *scene, Object *ob, const int collection_object_index)
static int wm_collection_export_all_exec(bContext *C, wmOperator *op)
static void WM_OT_collection_export_all(wmOperatorType *ot)
void OBJECT_OT_collection_add(wmOperatorType *ot)
static int collection_remove_exec(bContext *C, wmOperator *op)
static int collection_export_recursive(bContext *C, wmOperator *op, LayerCollection *layer_collection, CollectionExportStats &stats)
static const EnumPropertyItem * collection_object_active_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static int collection_exporter_export_exec(bContext *C, wmOperator *op)
static void collection_exporter_menu_draw(const bContext *, Menu *menu)
static int collection_io_export_all_exec(bContext *C, wmOperator *op)
static bool collection_exporter_remove_poll(bContext *C)
void COLLECTION_OT_create(wmOperatorType *ot)
static int collection_export(bContext *C, wmOperator *op, Collection *collection, CollectionExportStats &stats)
void OBJECT_OT_collection_unlink(wmOperatorType *ot)
static int collection_exporter_remove_invoke(bContext *C, wmOperator *op, const wmEvent *)
void OBJECT_OT_collection_remove(wmOperatorType *ot)
void COLLECTION_OT_objects_add_active(wmOperatorType *ot)
static int collection_unlink_exec(bContext *C, wmOperator *op)
static int objects_add_active_exec(bContext *C, wmOperator *op)
static int collection_exporter_remove_exec(bContext *C, wmOperator *op)
void COLLECTION_OT_objects_remove_all(wmOperatorType *ot)
void COLLECTION_OT_objects_remove_active(wmOperatorType *ot)
static void COLLECTION_OT_exporter_export(wmOperatorType *ot)
void COLLECTION_OT_objects_remove(wmOperatorType *ot)
static int collection_create_exec(bContext *C, wmOperator *op)
static int collection_exporter_add_exec(bContext *C, wmOperator *op)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_enum_item_add_separator(EnumPropertyItem **items, int *totitem)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:29
ListBase layer_collections
struct Collection * collection
ListBase collections
Definition BKE_main.hh:231
char label[BKE_ST_MAXNAME]
char idname[BKE_ST_MAXNAME]
void(* draw)(const bContext *C, Menu *menu)
uiLayout * layout
void * data
Definition RNA_types.hh:42
ListBase layer_collections
char export_operator[OP_MAX_TYPENAME]
std::string get_default_filename(const StringRefNull name)
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
IDProperty * last_properties
Definition WM_types.hh:1083
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
#define N_(msgid)
void WM_main_add_notifier(uint type, void *reference)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
bool WM_menutype_add(MenuType *mt)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *)