Blender V5.0
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
8
9#include <cstring>
10
11#include "BLI_listbase.h"
12#include "BLI_path_utils.hh"
13#include "BLI_string_utf8.h"
14#include "BLI_utildefines.h"
15
17#include "DNA_object_types.h"
18#include "DNA_scene_types.h"
19
20#include "BKE_collection.hh"
21#include "BKE_context.hh"
22#include "BKE_file_handler.hh"
23#include "BKE_idprop.hh"
24#include "BKE_layer.hh"
25#include "BKE_lib_id.hh"
26#include "BKE_library.hh"
27#include "BKE_main.hh"
28#include "BKE_object.hh"
29#include "BKE_report.hh"
30#include "BKE_screen.hh"
31
32#include "BLT_translation.hh"
33
34#include "DEG_depsgraph.hh"
36
37#include "ED_object.hh"
38#include "ED_screen.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "RNA_access.hh"
44#include "RNA_define.hh"
45#include "RNA_enum_types.hh"
46#include "RNA_prototypes.hh"
47
48#include "UI_interface.hh"
49#include "UI_interface_icons.hh"
51
52#include "object_intern.hh"
53
54namespace blender::ed::object {
55
56/********************* 3d view operators ***********************/
57
58/* can be called with C == nullptr */
60 PointerRNA * /*ptr*/,
61 PropertyRNA * /*prop*/,
62 bool *r_free)
63{
64 Main *bmain = CTX_data_main(C);
65 Scene *scene = CTX_data_scene(C);
66 Object *ob;
67 EnumPropertyItem *item = nullptr, item_tmp = {0};
68 int totitem = 0;
69
70 if (C == nullptr) {
72 }
73
74 ob = context_object(C);
75
76 /* check that the object exists */
77 if (ob) {
78 Collection *collection;
79 int i = 0, count = 0;
80
81 /* if 2 or more collections, add option to add to all collections */
82 collection = nullptr;
83 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
84 count++;
85 }
86
87 if (count >= 2) {
88 item_tmp.identifier = item_tmp.name = "All Collections";
89 item_tmp.value = INT_MAX; /* this will give nullptr on lookup */
90 RNA_enum_item_add(&item, &totitem, &item_tmp);
91 RNA_enum_item_add_separator(&item, &totitem);
92 }
93
94 /* add collections */
95 collection = nullptr;
96 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
97 item_tmp.identifier = item_tmp.name = collection->id.name + 2;
98 item_tmp.icon = UI_icon_color_from_collection(collection);
99 item_tmp.value = i;
100 RNA_enum_item_add(&item, &totitem, &item_tmp);
101 i++;
102 }
103 }
104
105 RNA_enum_item_end(&item, &totitem);
106 *r_free = true;
107
108 return item;
109}
110
111/* get the collection back from the enum index, quite awkward and UI specific */
113 Scene *scene,
114 Object *ob,
115 const int collection_object_index)
116{
117 Collection *collection = nullptr;
118 int i = 0;
119 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
120 if (i == collection_object_index) {
121 break;
122 }
123 i++;
124 }
125
126 return collection;
127}
128
130{
131 Object *ob = context_object(C);
132 Main *bmain = CTX_data_main(C);
133 Scene *scene = CTX_data_scene(C);
134 int single_collection_index = RNA_enum_get(op->ptr, "collection");
136 bmain, scene, ob, single_collection_index);
137 bool is_cycle = false;
138 bool changed_multi = false;
139
140 if (ob == nullptr) {
141 return OPERATOR_CANCELLED;
142 }
143
144 /* now add all selected objects to the collection(s) */
145 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
146 if (single_collection && collection != single_collection) {
147 continue;
148 }
149 if (!BKE_collection_has_object(collection, ob)) {
150 continue;
151 }
152
153 bool changed = false;
154 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
155 if (BKE_collection_has_object(collection, base->object)) {
156 continue;
157 }
158
159 if (!BKE_collection_object_cyclic_check(bmain, base->object, collection)) {
160 BKE_collection_object_add(bmain, collection, base->object);
161 changed = true;
162 }
163 else {
164 is_cycle = true;
165 }
166 }
168
169 if (changed) {
171 changed_multi = true;
172 }
173 }
175
176 if (is_cycle) {
177 BKE_report(op->reports, RPT_WARNING, "Skipped some collections because of cycle detected");
178 }
179
180 if (!changed_multi) {
181 return OPERATOR_CANCELLED;
182 }
183
186
187 return OPERATOR_FINISHED;
188}
189
191{
192 PropertyRNA *prop;
193
194 /* identifiers */
195 ot->name = "Add Selected to Active Object's Collection";
196 ot->description =
197 "Add selected objects to one of the collections the active-object is part of. "
198 "Optionally add to \"All Collections\" to ensure selected objects are included in "
199 "the same collections as the active object";
200 ot->idname = "COLLECTION_OT_objects_add_active";
201
202 /* API callbacks. */
204 ot->invoke = WM_menu_invoke;
206
207 /* flags */
209
210 /* properties */
211 prop = RNA_def_enum(ot->srna,
212 "collection",
214 0,
215 "Collection",
216 "The collection to add other selected objects to");
219 ot->prop = prop;
220}
221
223{
224 Main *bmain = CTX_data_main(C);
225 Scene *scene = CTX_data_scene(C);
226 ViewLayer *view_layer = CTX_data_view_layer(C);
227 BKE_view_layer_synced_ensure(scene, view_layer);
229 int single_collection_index = RNA_enum_get(op->ptr, "collection");
231 bmain, scene, ob, single_collection_index);
232 bool changed_multi = false;
233
234 if (ob == nullptr) {
235 return OPERATOR_CANCELLED;
236 }
237
238 /* Linking to same collection requires its own loop so we can avoid
239 * looking up the active objects collections each time. */
240 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
241 if (single_collection && collection != single_collection) {
242 continue;
243 }
244
245 if (BKE_collection_has_object(collection, ob)) {
246 /* Remove collections from selected objects */
247 bool changed = false;
248 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
249 BKE_collection_object_remove(bmain, collection, base->object, false);
250 changed = true;
251 }
253
254 if (changed) {
256 changed_multi = true;
257 }
258 }
259 }
261
262 if (!changed_multi) {
263 BKE_report(op->reports, RPT_ERROR, "Active object contains no collections");
264 }
265
268
269 return OPERATOR_FINISHED;
270}
271
273{
274 PropertyRNA *prop;
275
276 /* identifiers */
277 ot->name = "Remove Selected from Active Collection";
278 ot->description = "Remove the object from an object collection that contains the active object";
279 ot->idname = "COLLECTION_OT_objects_remove_active";
280
281 /* API callbacks. */
283 ot->invoke = WM_menu_invoke;
285
286 /* flags */
288
289 /* properties */
290 prop = RNA_def_enum(ot->srna,
291 "collection",
293 0,
294 "Collection",
295 "The collection to remove other selected objects from");
298 ot->prop = prop;
299}
300
302{
303 Main *bmain = CTX_data_main(C);
304 Scene *scene = CTX_data_scene(C);
305
306 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
307 BKE_object_groups_clear(bmain, scene, base->object);
308 }
310
313
314 return OPERATOR_FINISHED;
315}
316
318{
319 /* identifiers */
320 ot->name = "Remove from All Collections";
321 ot->description = "Remove selected objects from all collections";
322 ot->idname = "COLLECTION_OT_objects_remove_all";
323
324 /* API callbacks. */
327
328 /* flags */
330}
331
333{
334 Object *ob = context_object(C);
335 Main *bmain = CTX_data_main(C);
336 Scene *scene = CTX_data_scene(C);
337 int single_collection_index = RNA_enum_get(op->ptr, "collection");
339 bmain, scene, ob, single_collection_index);
340 bool changed_multi = false;
341
342 if (ob == nullptr) {
343 return OPERATOR_CANCELLED;
344 }
345
346 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
347 if (single_collection && collection != single_collection) {
348 continue;
349 }
350 if (!BKE_collection_has_object(collection, ob)) {
351 continue;
352 }
353
354 /* now remove all selected objects from the collection */
355 bool changed = false;
356 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
357 BKE_collection_object_remove(bmain, collection, base->object, false);
358 changed = true;
359 }
361
362 if (changed) {
364 changed_multi = true;
365 }
366 }
368
369 if (!changed_multi) {
370 return OPERATOR_CANCELLED;
371 }
372
375
376 return OPERATOR_FINISHED;
377}
378
380{
381 PropertyRNA *prop;
382
383 /* identifiers */
384 ot->name = "Remove from Collection";
385 ot->description = "Remove selected objects from a collection";
386 ot->idname = "COLLECTION_OT_objects_remove";
387
388 /* API callbacks. */
390 ot->invoke = WM_menu_invoke;
392
393 /* flags */
395
396 /* properties */
397 prop = RNA_def_enum(ot->srna,
398 "collection",
400 0,
401 "Collection",
402 "The collection to remove this object from");
405 ot->prop = prop;
406}
407
409{
410 Main *bmain = CTX_data_main(C);
411 char name[MAX_ID_NAME - 2]; /* id name */
412 bool changed = false;
413
414 RNA_string_get(op->ptr, "name", name);
415
416 Collection *collection = BKE_collection_add(bmain, nullptr, name);
417 id_fake_user_set(&collection->id);
418
419 CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
420 BKE_collection_object_add(bmain, collection, base->object);
421 changed = true;
422 }
424
425 if (changed) {
427 }
428
431
432 return OPERATOR_FINISHED;
433}
434
436{
437 /* identifiers */
438 ot->name = "Create New Collection";
439 ot->description = "Create an object collection from selected objects";
440 ot->idname = "COLLECTION_OT_create";
441
442 /* API callbacks. */
445
446 /* flags */
448
449 RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Name of the new collection");
450}
451
452static bool collection_exporter_common_check(const Collection *collection)
453{
454 return collection != nullptr &&
455 !(ID_IS_LINKED(&collection->id) || ID_IS_OVERRIDE_LIBRARY(&collection->id));
456}
457
459{
460 const Collection *collection = CTX_data_collection(C);
461 return collection_exporter_common_check(collection);
462}
463
465{
466 const Collection *collection = CTX_data_collection(C);
467 return collection_exporter_common_check(collection) &&
468 !BLI_listbase_is_empty(&collection->exporters);
469}
470
472{
473 return CTX_data_view_layer(C) != nullptr;
474}
475
477{
478 using namespace blender;
479 Collection *collection = CTX_data_collection(C);
480
481 char name[MAX_ID_NAME - 2]; /* id name */
482 RNA_string_get(op->ptr, "name", name);
483
485 if (!fh) {
486 BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", name);
487 return OPERATOR_CANCELLED;
488 }
489
490 if (!WM_operatortype_find(fh->export_operator, true)) {
492 op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
493 return OPERATOR_CANCELLED;
494 }
495
496 BKE_collection_exporter_add(collection, fh->idname, fh->label);
497
500
503
504 return OPERATOR_FINISHED;
505}
506
508{
509 /* identifiers */
510 ot->name = "Add Exporter";
511 ot->description = "Add exporter to the exporter list";
512 ot->idname = "COLLECTION_OT_exporter_add";
513
514 /* API callbacks. */
517
518 /* flags */
520
521 RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "FileHandler idname");
522}
523
525{
526 Collection *collection = CTX_data_collection(C);
527 ListBase *exporters = &collection->exporters;
528
529 int index = RNA_int_get(op->ptr, "index");
530 CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
531 if (!data) {
532 return OPERATOR_CANCELLED;
533 }
534
536
539
542
543 return OPERATOR_FINISHED;
544}
545
547 wmOperator *op,
548 const wmEvent * /*event*/)
549{
551 C, op, IFACE_("Remove exporter?"), nullptr, IFACE_("Delete"), ALERT_ICON_NONE, false);
552}
553
555{
556 /* identifiers */
557 ot->name = "Remove Exporter";
558 ot->description = "Remove exporter from the exporter list";
559 ot->idname = "COLLECTION_OT_exporter_remove";
560
561 /* API callbacks. */
565
566 /* flags */
568
569 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
570}
571
573{
574 using namespace blender;
575 Collection *collection = CTX_data_collection(C);
576 const int dir = RNA_enum_get(op->ptr, "direction");
577 const int from = collection->active_exporter_index;
578
579 /* Move Up/down to index. */
580 const int to = from + dir;
581
582 if (!BKE_collection_exporter_move(collection, from, to)) {
583 return OPERATOR_CANCELLED;
584 }
585
586 collection->active_exporter_index = to;
587 return OPERATOR_FINISHED;
588}
589
591{
592 static const EnumPropertyItem exporter_move[] = {
593 {-1, "UP", 0, "Up", ""},
594 {1, "DOWN", 0, "Down", ""},
595 {0, nullptr, 0, nullptr, nullptr},
596 };
597
598 /* identifiers */
599 ot->name = "Move Exporter";
600 ot->description = "Move exporter up or down in the exporter list";
601 ot->idname = "COLLECTION_OT_exporter_move";
602
603 /* API callbacks. */
606
607 /* flags */
609
610 RNA_def_enum(ot->srna,
611 "direction",
612 exporter_move,
613 0,
614 "Direction",
615 "Direction to move the active exporter");
616}
617
619 wmOperator *op,
621 Collection *collection,
622 const bool report_success)
623{
624 using namespace blender;
626 if (!fh) {
627 BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", data->fh_idname);
628 return OPERATOR_CANCELLED;
629 }
630
632 if (!ot) {
634 op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
635 return OPERATOR_CANCELLED;
636 }
637
638 /* Execute operator with our stored properties. */
639 /* TODO: Cascade settings down from parent collections(?) */
640 IDProperty *op_props = IDP_CopyProperty(data->export_properties);
641 PointerRNA properties = RNA_pointer_create_discrete(nullptr, ot->srna, op_props);
642 const char *collection_name = collection->id.name + 2;
643
644 /* Ensure we have a valid filepath set. Create one if the user has not specified anything yet. */
645 char filepath[FILE_MAX];
646 RNA_string_get(&properties, "filepath", filepath);
647 if (!filepath[0]) {
649 filepath, sizeof(filepath), "//", fh->get_default_filename(collection_name).c_str());
650 }
651 else {
652 const char *filename = BLI_path_basename(filepath);
653 if (!filename[0] || !BLI_path_extension(filename)) {
654 BKE_reportf(op->reports, RPT_ERROR, "File path '%s' is not a valid file", filepath);
655
656 IDP_FreeProperty(op_props);
657 return OPERATOR_CANCELLED;
658 }
659 }
660
661 const Main *bmain = CTX_data_main(C);
662 BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
663
664 /* Ensure that any properties from when this operator was "last used" are cleared. Save them for
665 * restoration later. Otherwise properties from a regular File->Export may contaminate this
666 * collection export. */
667 IDProperty *last_properties = ot->last_properties;
668 ot->last_properties = nullptr;
669
670 RNA_string_set(&properties, "filepath", filepath);
671 RNA_string_set(&properties, "collection", collection_name);
673 C, ot, wm::OpCallContext::ExecDefault, &properties, nullptr);
674
675 /* Free the "last used" properties that were just set from the collection export and restore the
676 * original "last used" properties. */
677 if (ot->last_properties) {
678 IDP_FreeProperty(ot->last_properties);
679 }
680 ot->last_properties = last_properties;
681
682 IDP_FreeProperty(op_props);
683
684 if (report_success && op_result == OPERATOR_FINISHED) {
685 BKE_reportf(op->reports, RPT_INFO, "Exported '%s'", filepath);
686 }
687
688 return op_result;
689}
690
692{
693 Collection *collection = CTX_data_collection(C);
694 ListBase *exporters = &collection->exporters;
695
696 int index = RNA_int_get(op->ptr, "index");
697 CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
698 if (!data) {
699 return OPERATOR_CANCELLED;
700 }
701
702 return collection_exporter_export(C, op, data, collection, true);
703}
704
706{
707 /* identifiers */
708 ot->name = "Export";
709 ot->description = "Invoke the export operation";
710 ot->idname = "COLLECTION_OT_exporter_export";
711
712 /* API callbacks. */
715
716 /* flags */
717 ot->flag = 0;
718
719 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
720}
721
726
728 wmOperator *op,
729 Collection *collection,
731{
732 ListBase *exporters = &collection->exporters;
733 int files_num = 0;
734
735 LISTBASE_FOREACH (CollectionExport *, data, exporters) {
736 if (collection_exporter_export(C, op, data, collection, false) != OPERATOR_FINISHED) {
737 /* Do not continue calling exporters if we encounter one that fails. */
738 return OPERATOR_CANCELLED;
739 }
740 files_num++;
741 }
742
743 if (files_num) {
744 stats.successful_exports_num += files_num;
745 stats.collections_num++;
746 }
747 return OPERATOR_FINISHED;
748}
749
751{
752 Collection *collection = CTX_data_collection(C);
754 wmOperatorStatus result = collection_export(C, op, collection, stats);
755
756 /* Only report if nothing was cancelled along the way. We don't want this UI report to happen
757 * over-top any reports from the actual failures. */
758 if (result == OPERATOR_FINISHED && stats.successful_exports_num > 0) {
760 RPT_INFO,
761 "Exported %d files from collection '%s'",
763 collection->id.name + 2);
764 }
765
766 return result;
767}
768
770{
771 /* identifiers */
772 ot->name = "Export All";
773 ot->description = "Invoke all configured exporters on this collection";
774 ot->idname = "COLLECTION_OT_export_all";
775
776 /* API callbacks. */
779
780 /* flags */
781 ot->flag = 0;
782}
783
785 wmOperator *op,
786 LayerCollection *layer_collection,
788{
789 /* Skip collections which have been Excluded in the View Layer. */
790 if (layer_collection->flag & LAYER_COLLECTION_EXCLUDE) {
791 return OPERATOR_FINISHED;
792 }
793
794 if (!collection_exporter_common_check(layer_collection->collection)) {
795 return OPERATOR_FINISHED;
796 }
797
798 if (collection_export(C, op, layer_collection->collection, stats) != OPERATOR_FINISHED) {
799 return OPERATOR_CANCELLED;
800 }
801
802 LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
803 if (collection_export_recursive(C, op, child, stats) != OPERATOR_FINISHED) {
804 return OPERATOR_CANCELLED;
805 }
806 }
807
808 return OPERATOR_FINISHED;
809}
810
812{
813 ViewLayer *view_layer = CTX_data_view_layer(C);
814
816 LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
817 if (collection_export_recursive(C, op, layer_collection, stats) != OPERATOR_FINISHED) {
818 return OPERATOR_CANCELLED;
819 }
820 }
821
822 /* Only report if nothing was cancelled along the way. We don't want this UI report to happen
823 * over-top any reports from the actual failures. */
824 if (stats.successful_exports_num > 0) {
826 RPT_INFO,
827 "Exported %d files from %d collections",
829 stats.collections_num);
830 }
831
832 return OPERATOR_FINISHED;
833}
834
836{
837 /* identifiers */
838 ot->name = "Export All Collections";
839 ot->description = "Invoke all configured exporters for all collections";
840 ot->idname = "WM_OT_collection_export_all";
841
842 /* API callbacks. */
845
846 /* flags */
847 ot->flag = 0;
848}
849
850static void collection_exporter_menu_draw(const bContext * /*C*/, Menu *menu)
851{
852 using namespace blender;
853 uiLayout *layout = menu->layout;
854
855 /* Add all file handlers capable of being exported to the menu. */
856 bool at_least_one = false;
857 for (const auto &fh : bke::file_handlers()) {
858 if (WM_operatortype_find(fh->export_operator, true)) {
859 PointerRNA op_ptr = layout->op("COLLECTION_OT_exporter_add", fh->label, ICON_NONE);
860 RNA_string_set(&op_ptr, "name", fh->idname);
861 at_least_one = true;
862 }
863 }
864
865 if (!at_least_one) {
866 layout->label(IFACE_("No file handlers available"), ICON_NONE);
867 }
868}
869
885
886/****************** properties window operators *********************/
887
889{
890 Object *ob = context_object(C);
891 Main *bmain = CTX_data_main(C);
892
893 if (ob == nullptr) {
894 return OPERATOR_CANCELLED;
895 }
896
897 Collection *collection = BKE_collection_add(bmain, nullptr, "Collection");
898 id_fake_user_set(&collection->id);
899 BKE_collection_object_add(bmain, collection, ob);
900
903
905
906 return OPERATOR_FINISHED;
907}
908
910{
911 /* identifiers */
912 ot->name = "Add to Collection";
913 ot->idname = "OBJECT_OT_collection_add";
914 ot->description = "Add an object to a new collection";
915
916 /* API callbacks. */
917 ot->exec = collection_add_exec;
919
920 /* flags */
922}
923
925{
926 Main *bmain = CTX_data_main(C);
927 Object *ob = context_object(C);
928 Collection *collection = static_cast<Collection *>(
929 BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")));
930
931 if (ELEM(nullptr, ob, collection)) {
932 return OPERATOR_CANCELLED;
933 }
934
935 /* Early return check, if the object is already in collection
936 * we could skip all the dependency check and just consider
937 * operator is finished.
938 */
939 if (BKE_collection_has_object(collection, ob)) {
940 return OPERATOR_FINISHED;
941 }
942
943 /* Currently this should not be allowed (might be supported in the future though...). */
944 if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
945 BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is overridden");
946 return OPERATOR_CANCELLED;
947 }
948 /* Linked collections are already checked for by using RNA_collection_local_itemf
949 * but operator can be called without invoke */
950 if (!ID_IS_EDITABLE(&collection->id)) {
951 BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is linked");
952 return OPERATOR_CANCELLED;
953 }
954
955 /* Adding object to collection which is used as dupli-collection for self is bad idea.
956 *
957 * It is also bad idea to add object to collection which is in collection which
958 * contains our current object.
959 */
960 if (BKE_collection_object_cyclic_check(bmain, ob, collection)) {
962 RPT_ERROR,
963 "Could not add the collection because of dependency cycle detected");
964 return OPERATOR_CANCELLED;
965 }
966
967 BKE_collection_object_add(bmain, collection, ob);
968
971
973
974 return OPERATOR_FINISHED;
975}
976
978{
979 PropertyRNA *prop;
980
981 /* identifiers */
982 ot->name = "Link to Collection";
983 ot->idname = "OBJECT_OT_collection_link";
984 ot->description = "Add an object to an existing collection";
985
986 /* API callbacks. */
987 ot->exec = collection_link_exec;
988 ot->invoke = WM_enum_search_invoke;
990
991 /* flags */
993
994 /* properties */
995 prop = RNA_def_enum(ot->srna, "collection", rna_enum_dummy_NULL_items, 0, "Collection", "");
998 ot->prop = prop;
999}
1000
1002{
1003 Main *bmain = CTX_data_main(C);
1004 Object *ob = context_object(C);
1005 Collection *collection = static_cast<Collection *>(
1006 CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data);
1007
1008 if (!ob || !collection) {
1009 return OPERATOR_CANCELLED;
1010 }
1011 if (!ID_IS_EDITABLE(collection) || ID_IS_OVERRIDE_LIBRARY(collection)) {
1012 BKE_report(op->reports,
1013 RPT_ERROR,
1014 "Cannot remove an object from a linked or library override collection");
1015 return OPERATOR_CANCELLED;
1016 }
1017
1018 BKE_collection_object_remove(bmain, collection, ob, false);
1019
1022
1024
1025 return OPERATOR_FINISHED;
1026}
1027
1029{
1030 /* identifiers */
1031 ot->name = "Remove Collection";
1032 ot->idname = "OBJECT_OT_collection_remove";
1033 ot->description = "Remove the active object from this collection";
1034
1035 /* API callbacks. */
1036 ot->exec = collection_remove_exec;
1037 ot->poll = ED_operator_objectmode;
1038
1039 /* flags */
1040 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1041}
1042
1044{
1045 Main *bmain = CTX_data_main(C);
1046 Collection *collection = CTX_data_collection(C);
1047
1048 if (!collection) {
1049 return OPERATOR_CANCELLED;
1050 }
1051 if (collection->flag & COLLECTION_IS_MASTER) {
1052 return OPERATOR_CANCELLED;
1053 }
1054 BLI_assert((collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0);
1055 if (ID_IS_OVERRIDE_LIBRARY(collection) &&
1056 collection->id.override_library->hierarchy_root != &collection->id)
1057 {
1058 BKE_report(op->reports,
1059 RPT_ERROR,
1060 "Cannot unlink a library override collection which is not the root of its override "
1061 "hierarchy");
1062 return OPERATOR_CANCELLED;
1063 }
1064
1065 BKE_id_delete(bmain, collection);
1066
1068
1070
1071 return OPERATOR_FINISHED;
1072}
1073
1075{
1076 Collection *collection = CTX_data_collection(C);
1077
1078 if (!collection) {
1079 return false;
1080 }
1081 if (collection->flag & COLLECTION_IS_MASTER) {
1082 return false;
1083 }
1084 BLI_assert((collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0);
1085 if (ID_IS_OVERRIDE_LIBRARY(collection) &&
1086 collection->id.override_library->hierarchy_root != &collection->id)
1087 {
1088 return false;
1089 }
1090
1091 return ED_operator_objectmode(C);
1092}
1093
1095{
1096 /* identifiers */
1097 ot->name = "Unlink Collection";
1098 ot->idname = "OBJECT_OT_collection_unlink";
1099 ot->description = "Unlink the collection from all objects";
1100
1101 /* API callbacks. */
1102 ot->exec = collection_unlink_exec;
1103 ot->poll = collection_unlink_poll;
1104
1105 /* flags */
1106 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1107}
1108
1109/* Select objects in the same collection as the active */
1111{
1112 Scene *scene = CTX_data_scene(C);
1113 Collection *collection = static_cast<Collection *>(
1114 CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data);
1115
1116 if (!collection) {
1117 return OPERATOR_CANCELLED;
1118 }
1119
1120 CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
1121 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
1122 if (BKE_collection_has_object_recursive(collection, base->object)) {
1123 base_select(base, BA_SELECT);
1124 }
1125 }
1126 }
1128
1131
1132 return OPERATOR_FINISHED;
1133}
1134
1136{
1137 /* identifiers */
1138 ot->name = "Select Objects in Collection";
1139 ot->idname = "OBJECT_OT_collection_objects_select";
1140 ot->description = "Select all objects in collection";
1141
1142 /* API callbacks. */
1143 ot->exec = select_grouped_exec;
1144 ot->poll = ED_operator_objectmode;
1145
1146 /* flags */
1147 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1148}
1149
1150} // namespace blender::ed::object
bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, bool free_us)
void BKE_collection_exporter_remove(Collection *collection, CollectionExport *data)
bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
#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)
bool BKE_collection_exporter_move(Collection *collection, const int from, const int to)
CollectionExport * BKE_collection_exporter_add(Collection *collection, char *idname, char *label)
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:1251
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:863
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:396
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:887
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
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
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_UTF8(dst, src)
#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:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define MAX_ID_NAME
Definition DNA_ID.h:373
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:774
Object groups, one object can be in many groups at once.
@ COLLECTION_IS_MASTER
@ LAYER_COLLECTION_EXCLUDE
Object is a sort of wrapper for general info.
#define BASE_SELECTED(v3d, base)
#define BASE_SELECTABLE(v3d, base)
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
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:432
#define C
Definition RandGen.cpp:29
@ ALERT_ICON_NONE
int UI_icon_color_from_collection(const Collection *collection)
#define ND_DRAW
Definition WM_types.hh:461
#define ND_OB_SELECT
Definition WM_types.hh:442
#define ND_SPACE_PROPERTIES
Definition WM_types.hh:529
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_GROUP
Definition WM_types.hh:383
#define NA_EDITED
Definition WM_types.hh:584
#define NC_OBJECT
Definition WM_types.hh:379
#define NC_SPACE
Definition WM_types.hh:392
#define ND_SPACE_OUTLINER
Definition WM_types.hh:527
BMesh const char void * data
int count
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
FileHandlerType * file_handler_find(StringRef idname)
Span< std::unique_ptr< FileHandlerType > > file_handlers()
static wmOperatorStatus collection_objects_remove_exec(bContext *C, wmOperator *op)
static void COLLECTION_OT_exporter_add(wmOperatorType *ot)
static bool collection_unlink_poll(bContext *C)
static wmOperatorStatus collection_remove_exec(bContext *C, wmOperator *op)
static wmOperatorStatus objects_add_active_exec(bContext *C, wmOperator *op)
static void COLLECTION_OT_exporter_move(wmOperatorType *ot)
static wmOperatorStatus collection_export(bContext *C, wmOperator *op, Collection *collection, CollectionExportStats &stats)
static bool collection_export_all_poll(bContext *C)
static void COLLECTION_OT_exporter_remove(wmOperatorType *ot)
static wmOperatorStatus collection_exporter_remove_exec(bContext *C, wmOperator *op)
static wmOperatorStatus collection_unlink_exec(bContext *C, wmOperator *op)
static void COLLECTION_OT_export_all(wmOperatorType *ot)
static wmOperatorStatus collection_objects_remove_all_exec(bContext *C, wmOperator *)
static bool collection_exporter_common_check(const Collection *collection)
static wmOperatorStatus collection_add_exec(bContext *C, wmOperator *)
static wmOperatorStatus select_grouped_exec(bContext *C, wmOperator *)
static wmOperatorStatus collection_exporter_export_exec(bContext *C, wmOperator *op)
void OBJECT_OT_collection_objects_select(wmOperatorType *ot)
static wmOperatorStatus collection_exporter_remove_invoke(bContext *C, wmOperator *op, const wmEvent *)
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 Collection * collection_object_active_find_index(Main *bmain, Scene *scene, Object *ob, const int collection_object_index)
static void WM_OT_collection_export_all(wmOperatorType *ot)
void OBJECT_OT_collection_add(wmOperatorType *ot)
static const EnumPropertyItem * collection_object_active_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static wmOperatorStatus objects_remove_active_exec(bContext *C, wmOperator *op)
static wmOperatorStatus collection_exporter_export(bContext *C, wmOperator *op, CollectionExport *data, Collection *collection, const bool report_success)
static void collection_exporter_menu_draw(const bContext *, Menu *menu)
static wmOperatorStatus collection_io_export_all_exec(bContext *C, wmOperator *op)
static bool collection_exporter_remove_poll(bContext *C)
void COLLECTION_OT_create(wmOperatorType *ot)
static wmOperatorStatus collection_link_exec(bContext *C, wmOperator *op)
static wmOperatorStatus collection_exporter_add_exec(bContext *C, wmOperator *op)
void OBJECT_OT_collection_unlink(wmOperatorType *ot)
static wmOperatorStatus collection_exporter_move_exec(bContext *C, wmOperator *op)
void OBJECT_OT_collection_remove(wmOperatorType *ot)
static wmOperatorStatus collection_export_recursive(bContext *C, wmOperator *op, LayerCollection *layer_collection, CollectionExportStats &stats)
static wmOperatorStatus collection_create_exec(bContext *C, wmOperator *op)
void COLLECTION_OT_objects_add_active(wmOperatorType *ot)
void COLLECTION_OT_objects_remove_all(wmOperatorType *ot)
static wmOperatorStatus wm_collection_export_all_exec(bContext *C, wmOperator *op)
void COLLECTION_OT_objects_remove_active(wmOperatorType *ot)
static void COLLECTION_OT_exporter_export(wmOperatorType *ot)
void COLLECTION_OT_objects_remove(wmOperatorType *ot)
const char * name
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(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:26
struct ID * hierarchy_root
Definition DNA_ID.h:344
char name[258]
Definition DNA_ID.h:432
IDOverrideLibrary * override_library
Definition DNA_ID.h:494
short flag
Definition DNA_ID.h:438
ListBase layer_collections
struct Collection * collection
ListBase collections
Definition BKE_main.hh:298
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:53
ListBase layer_collections
std::string get_default_filename(StringRefNull name)
char export_operator[OP_MAX_TYPENAME]
void label(blender::StringRef name, int icon)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
#define N_(msgid)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
bool WM_menutype_add(MenuType *mt)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
wmOperatorStatus WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
wmOperatorStatus WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *)