Blender V4.5
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.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"
50
51#include "object_intern.hh"
52
53namespace blender::ed::object {
54
55/********************* 3d view operators ***********************/
56
57/* can be called with C == nullptr */
59 PointerRNA * /*ptr*/,
60 PropertyRNA * /*prop*/,
61 bool *r_free)
62{
63 Main *bmain = CTX_data_main(C);
64 Scene *scene = CTX_data_scene(C);
65 Object *ob;
66 EnumPropertyItem *item = nullptr, item_tmp = {0};
67 int totitem = 0;
68
69 if (C == nullptr) {
71 }
72
73 ob = context_object(C);
74
75 /* check that the object exists */
76 if (ob) {
77 Collection *collection;
78 int i = 0, count = 0;
79
80 /* if 2 or more collections, add option to add to all collections */
81 collection = nullptr;
82 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
83 count++;
84 }
85
86 if (count >= 2) {
87 item_tmp.identifier = item_tmp.name = "All Collections";
88 item_tmp.value = INT_MAX; /* this will give nullptr on lookup */
89 RNA_enum_item_add(&item, &totitem, &item_tmp);
90 RNA_enum_item_add_separator(&item, &totitem);
91 }
92
93 /* add collections */
94 collection = nullptr;
95 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
96 item_tmp.identifier = item_tmp.name = collection->id.name + 2;
97 item_tmp.icon = UI_icon_color_from_collection(collection);
98 item_tmp.value = i;
99 RNA_enum_item_add(&item, &totitem, &item_tmp);
100 i++;
101 }
102 }
103
104 RNA_enum_item_end(&item, &totitem);
105 *r_free = true;
106
107 return item;
108}
109
110/* get the collection back from the enum index, quite awkward and UI specific */
112 Scene *scene,
113 Object *ob,
114 const int collection_object_index)
115{
116 Collection *collection = nullptr;
117 int i = 0;
118 while ((collection = BKE_collection_object_find(bmain, scene, collection, ob))) {
119 if (i == collection_object_index) {
120 break;
121 }
122 i++;
123 }
124
125 return collection;
126}
127
129{
130 Object *ob = context_object(C);
131 Main *bmain = CTX_data_main(C);
132 Scene *scene = CTX_data_scene(C);
133 int single_collection_index = RNA_enum_get(op->ptr, "collection");
135 bmain, scene, ob, single_collection_index);
136 bool is_cycle = false;
137 bool changed_multi = false;
138
139 if (ob == nullptr) {
140 return OPERATOR_CANCELLED;
141 }
142
143 /* now add all selected objects to the collection(s) */
144 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
145 if (single_collection && collection != single_collection) {
146 continue;
147 }
148 if (!BKE_collection_has_object(collection, ob)) {
149 continue;
150 }
151
152 bool changed = false;
153 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
154 if (BKE_collection_has_object(collection, base->object)) {
155 continue;
156 }
157
158 if (!BKE_collection_object_cyclic_check(bmain, base->object, collection)) {
159 BKE_collection_object_add(bmain, collection, base->object);
160 changed = true;
161 }
162 else {
163 is_cycle = true;
164 }
165 }
167
168 if (changed) {
170 changed_multi = true;
171 }
172 }
174
175 if (is_cycle) {
176 BKE_report(op->reports, RPT_WARNING, "Skipped some collections because of cycle detected");
177 }
178
179 if (!changed_multi) {
180 return OPERATOR_CANCELLED;
181 }
182
185
186 return OPERATOR_FINISHED;
187}
188
190{
191 PropertyRNA *prop;
192
193 /* identifiers */
194 ot->name = "Add Selected to Active Object's Collection";
195 ot->description =
196 "Add selected objects to one of the collections the active-object is part of. "
197 "Optionally add to \"All Collections\" to ensure selected objects are included in "
198 "the same collections as the active object";
199 ot->idname = "COLLECTION_OT_objects_add_active";
200
201 /* API callbacks. */
203 ot->invoke = WM_menu_invoke;
205
206 /* flags */
208
209 /* properties */
210 prop = RNA_def_enum(ot->srna,
211 "collection",
213 0,
214 "Collection",
215 "The collection to add other selected objects to");
218 ot->prop = prop;
219}
220
222{
223 Main *bmain = CTX_data_main(C);
224 Scene *scene = CTX_data_scene(C);
225 ViewLayer *view_layer = CTX_data_view_layer(C);
226 BKE_view_layer_synced_ensure(scene, view_layer);
228 int single_collection_index = RNA_enum_get(op->ptr, "collection");
230 bmain, scene, ob, single_collection_index);
231 bool changed_multi = false;
232
233 if (ob == nullptr) {
234 return OPERATOR_CANCELLED;
235 }
236
237 /* Linking to same collection requires its own loop so we can avoid
238 * looking up the active objects collections each time. */
239 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
240 if (single_collection && collection != single_collection) {
241 continue;
242 }
243
244 if (BKE_collection_has_object(collection, ob)) {
245 /* Remove collections from selected objects */
246 bool changed = false;
247 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
248 BKE_collection_object_remove(bmain, collection, base->object, false);
249 changed = true;
250 }
252
253 if (changed) {
255 changed_multi = true;
256 }
257 }
258 }
260
261 if (!changed_multi) {
262 BKE_report(op->reports, RPT_ERROR, "Active object contains no collections");
263 }
264
267
268 return OPERATOR_FINISHED;
269}
270
272{
273 PropertyRNA *prop;
274
275 /* identifiers */
276 ot->name = "Remove Selected from Active Collection";
277 ot->description = "Remove the object from an object collection that contains the active object";
278 ot->idname = "COLLECTION_OT_objects_remove_active";
279
280 /* API callbacks. */
282 ot->invoke = WM_menu_invoke;
284
285 /* flags */
287
288 /* properties */
289 prop = RNA_def_enum(ot->srna,
290 "collection",
292 0,
293 "Collection",
294 "The collection to remove other selected objects from");
297 ot->prop = prop;
298}
299
301{
302 Main *bmain = CTX_data_main(C);
303 Scene *scene = CTX_data_scene(C);
304
305 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
306 BKE_object_groups_clear(bmain, scene, base->object);
307 }
309
312
313 return OPERATOR_FINISHED;
314}
315
317{
318 /* identifiers */
319 ot->name = "Remove from All Collections";
320 ot->description = "Remove selected objects from all collections";
321 ot->idname = "COLLECTION_OT_objects_remove_all";
322
323 /* API callbacks. */
326
327 /* flags */
329}
330
332{
333 Object *ob = context_object(C);
334 Main *bmain = CTX_data_main(C);
335 Scene *scene = CTX_data_scene(C);
336 int single_collection_index = RNA_enum_get(op->ptr, "collection");
338 bmain, scene, ob, single_collection_index);
339 bool changed_multi = false;
340
341 if (ob == nullptr) {
342 return OPERATOR_CANCELLED;
343 }
344
345 FOREACH_COLLECTION_BEGIN (bmain, scene, Collection *, collection) {
346 if (single_collection && collection != single_collection) {
347 continue;
348 }
349 if (!BKE_collection_has_object(collection, ob)) {
350 continue;
351 }
352
353 /* now remove all selected objects from the collection */
354 bool changed = false;
355 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
356 BKE_collection_object_remove(bmain, collection, base->object, false);
357 changed = true;
358 }
360
361 if (changed) {
363 changed_multi = true;
364 }
365 }
367
368 if (!changed_multi) {
369 return OPERATOR_CANCELLED;
370 }
371
374
375 return OPERATOR_FINISHED;
376}
377
379{
380 PropertyRNA *prop;
381
382 /* identifiers */
383 ot->name = "Remove from Collection";
384 ot->description = "Remove selected objects from a collection";
385 ot->idname = "COLLECTION_OT_objects_remove";
386
387 /* API callbacks. */
389 ot->invoke = WM_menu_invoke;
391
392 /* flags */
394
395 /* properties */
396 prop = RNA_def_enum(ot->srna,
397 "collection",
399 0,
400 "Collection",
401 "The collection to remove this object from");
404 ot->prop = prop;
405}
406
408{
409 Main *bmain = CTX_data_main(C);
410 char name[MAX_ID_NAME - 2]; /* id name */
411 bool changed = false;
412
413 RNA_string_get(op->ptr, "name", name);
414
415 Collection *collection = BKE_collection_add(bmain, nullptr, name);
416 id_fake_user_set(&collection->id);
417
418 CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
419 BKE_collection_object_add(bmain, collection, base->object);
420 changed = true;
421 }
423
424 if (changed) {
426 }
427
430
431 return OPERATOR_FINISHED;
432}
433
435{
436 /* identifiers */
437 ot->name = "Create New Collection";
438 ot->description = "Create an object collection from selected objects";
439 ot->idname = "COLLECTION_OT_create";
440
441 /* API callbacks. */
444
445 /* flags */
447
449 ot->srna, "name", "Collection", 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 ListBase *exporters = &collection->exporters;
481
482 char name[MAX_ID_NAME - 2]; /* id name */
483 RNA_string_get(op->ptr, "name", name);
484
486 if (!fh) {
487 BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", name);
488 return OPERATOR_CANCELLED;
489 }
490
491 if (!WM_operatortype_find(fh->export_operator, true)) {
493 op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
494 return OPERATOR_CANCELLED;
495 }
496
497 /* Add a new #CollectionExport item to our handler list and fill it with #FileHandlerType
498 * information. Also load in the operator's properties now as well. */
500 STRNCPY(data->fh_idname, fh->idname);
501
503
504 IDPropertyTemplate val{};
505 data->export_properties = IDP_New(IDP_GROUP, &val, "export_properties");
507
508 BLI_addtail(exporters, data);
509 collection->active_exporter_index = BLI_listbase_count(exporters) - 1;
510
513
516
517 return OPERATOR_FINISHED;
518}
519
521{
522 /* identifiers */
523 ot->name = "Add Exporter";
524 ot->description = "Add Exporter";
525 ot->idname = "COLLECTION_OT_exporter_add";
526
527 /* API callbacks. */
530
531 /* flags */
533
534 RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "FileHandler idname");
535}
536
538{
539 Collection *collection = CTX_data_collection(C);
540 ListBase *exporters = &collection->exporters;
541
542 int index = RNA_int_get(op->ptr, "index");
543 CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
544 if (!data) {
545 return OPERATOR_CANCELLED;
546 }
547
548 BLI_remlink(exporters, data);
550
552
553 const int count = BLI_listbase_count(exporters);
554 const int new_index = count == 0 ? 0 : std::min(collection->active_exporter_index, count - 1);
555 collection->active_exporter_index = new_index;
556
559
562
563 return OPERATOR_FINISHED;
564}
565
567 wmOperator *op,
568 const wmEvent * /*event*/)
569{
571 C, op, IFACE_("Remove exporter?"), nullptr, IFACE_("Delete"), ALERT_ICON_NONE, false);
572}
573
575{
576 /* identifiers */
577 ot->name = "Remove Exporter";
578 ot->description = "Remove Exporter";
579 ot->idname = "COLLECTION_OT_exporter_remove";
580
581 /* API callbacks. */
585
586 /* flags */
588
589 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
590}
591
593 wmOperator *op,
595 Collection *collection,
596 const bool report_success)
597{
598 using namespace blender;
600 if (!fh) {
601 BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", data->fh_idname);
602 return OPERATOR_CANCELLED;
603 }
604
606 if (!ot) {
608 op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
609 return OPERATOR_CANCELLED;
610 }
611
612 /* Execute operator with our stored properties. */
613 /* TODO: Cascade settings down from parent collections(?) */
614 IDProperty *op_props = IDP_CopyProperty(data->export_properties);
615 PointerRNA properties = RNA_pointer_create_discrete(nullptr, ot->srna, op_props);
616 const char *collection_name = collection->id.name + 2;
617
618 /* Ensure we have a valid filepath set. Create one if the user has not specified anything yet. */
619 char filepath[FILE_MAX];
620 RNA_string_get(&properties, "filepath", filepath);
621 if (!filepath[0]) {
623 filepath, sizeof(filepath), "//", fh->get_default_filename(collection_name).c_str());
624 }
625 else {
626 const char *filename = BLI_path_basename(filepath);
627 if (!filename[0] || !BLI_path_extension(filename)) {
628 BKE_reportf(op->reports, RPT_ERROR, "File path '%s' is not a valid file", filepath);
629
630 IDP_FreeProperty(op_props);
631 return OPERATOR_CANCELLED;
632 }
633 }
634
635 const Main *bmain = CTX_data_main(C);
636 BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
637
638 /* Ensure that any properties from when this operator was "last used" are cleared. Save them for
639 * restoration later. Otherwise properties from a regular File->Export may contaminate this
640 * collection export. */
641 IDProperty *last_properties = ot->last_properties;
642 ot->last_properties = nullptr;
643
644 RNA_string_set(&properties, "filepath", filepath);
645 RNA_string_set(&properties, "collection", collection_name);
647 C, ot, WM_OP_EXEC_DEFAULT, &properties, nullptr);
648
649 /* Free the "last used" properties that were just set from the collection export and restore the
650 * original "last used" properties. */
651 if (ot->last_properties) {
652 IDP_FreeProperty(ot->last_properties);
653 }
654 ot->last_properties = last_properties;
655
656 IDP_FreeProperty(op_props);
657
658 if (report_success && op_result == OPERATOR_FINISHED) {
659 BKE_reportf(op->reports, RPT_INFO, "Exported '%s'", filepath);
660 }
661
662 return op_result;
663}
664
666{
667 Collection *collection = CTX_data_collection(C);
668 ListBase *exporters = &collection->exporters;
669
670 int index = RNA_int_get(op->ptr, "index");
671 CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
672 if (!data) {
673 return OPERATOR_CANCELLED;
674 }
675
676 return collection_exporter_export(C, op, data, collection, true);
677}
678
680{
681 /* identifiers */
682 ot->name = "Export";
683 ot->description = "Invoke the export operation";
684 ot->idname = "COLLECTION_OT_exporter_export";
685
686 /* API callbacks. */
689
690 /* flags */
691 ot->flag = 0;
692
693 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
694}
695
700
702 wmOperator *op,
703 Collection *collection,
705{
706 ListBase *exporters = &collection->exporters;
707 int files_num = 0;
708
709 LISTBASE_FOREACH (CollectionExport *, data, exporters) {
710 if (collection_exporter_export(C, op, data, collection, false) != OPERATOR_FINISHED) {
711 /* Do not continue calling exporters if we encounter one that fails. */
712 return OPERATOR_CANCELLED;
713 }
714 files_num++;
715 }
716
717 if (files_num) {
718 stats.successful_exports_num += files_num;
719 stats.collections_num++;
720 }
721 return OPERATOR_FINISHED;
722}
723
725{
726 Collection *collection = CTX_data_collection(C);
728 wmOperatorStatus result = collection_export(C, op, collection, stats);
729
730 /* Only report if nothing was cancelled along the way. We don't want this UI report to happen
731 * over-top any reports from the actual failures. */
732 if (result == OPERATOR_FINISHED && stats.successful_exports_num > 0) {
734 RPT_INFO,
735 "Exported %d files from collection '%s'",
737 collection->id.name + 2);
738 }
739
740 return result;
741}
742
744{
745 /* identifiers */
746 ot->name = "Export All";
747 ot->description = "Invoke all configured exporters on this collection";
748 ot->idname = "COLLECTION_OT_export_all";
749
750 /* API callbacks. */
753
754 /* flags */
755 ot->flag = 0;
756}
757
759 wmOperator *op,
760 LayerCollection *layer_collection,
762{
763 /* Skip collections which have been Excluded in the View Layer. */
764 if (layer_collection->flag & LAYER_COLLECTION_EXCLUDE) {
765 return OPERATOR_FINISHED;
766 }
767
768 if (!collection_exporter_common_check(layer_collection->collection)) {
769 return OPERATOR_FINISHED;
770 }
771
772 if (collection_export(C, op, layer_collection->collection, stats) != OPERATOR_FINISHED) {
773 return OPERATOR_CANCELLED;
774 }
775
776 LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
777 if (collection_export_recursive(C, op, child, stats) != OPERATOR_FINISHED) {
778 return OPERATOR_CANCELLED;
779 }
780 }
781
782 return OPERATOR_FINISHED;
783}
784
786{
787 ViewLayer *view_layer = CTX_data_view_layer(C);
788
790 LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
791 if (collection_export_recursive(C, op, layer_collection, stats) != OPERATOR_FINISHED) {
792 return OPERATOR_CANCELLED;
793 }
794 }
795
796 /* Only report if nothing was cancelled along the way. We don't want this UI report to happen
797 * over-top any reports from the actual failures. */
798 if (stats.successful_exports_num > 0) {
800 RPT_INFO,
801 "Exported %d files from %d collections",
803 stats.collections_num);
804 }
805
806 return OPERATOR_FINISHED;
807}
808
810{
811 /* identifiers */
812 ot->name = "Export All Collections";
813 ot->description = "Invoke all configured exporters for all collections";
814 ot->idname = "WM_OT_collection_export_all";
815
816 /* API callbacks. */
819
820 /* flags */
821 ot->flag = 0;
822}
823
824static void collection_exporter_menu_draw(const bContext * /*C*/, Menu *menu)
825{
826 using namespace blender;
827 uiLayout *layout = menu->layout;
828
829 /* Add all file handlers capable of being exported to the menu. */
830 bool at_least_one = false;
831 for (const auto &fh : bke::file_handlers()) {
832 if (WM_operatortype_find(fh->export_operator, true)) {
833 PointerRNA op_ptr = layout->op("COLLECTION_OT_exporter_add", fh->label, ICON_NONE);
834 RNA_string_set(&op_ptr, "name", fh->idname);
835 at_least_one = true;
836 }
837 }
838
839 if (!at_least_one) {
840 layout->label(IFACE_("No file handlers available"), ICON_NONE);
841 }
842}
843
858
859/****************** properties window operators *********************/
860
862{
863 Object *ob = context_object(C);
864 Main *bmain = CTX_data_main(C);
865
866 if (ob == nullptr) {
867 return OPERATOR_CANCELLED;
868 }
869
870 Collection *collection = BKE_collection_add(bmain, nullptr, "Collection");
871 id_fake_user_set(&collection->id);
872 BKE_collection_object_add(bmain, collection, ob);
873
876
878
879 return OPERATOR_FINISHED;
880}
881
883{
884 /* identifiers */
885 ot->name = "Add to Collection";
886 ot->idname = "OBJECT_OT_collection_add";
887 ot->description = "Add an object to a new collection";
888
889 /* API callbacks. */
890 ot->exec = collection_add_exec;
892
893 /* flags */
895}
896
898{
899 Main *bmain = CTX_data_main(C);
900 Object *ob = context_object(C);
901 Collection *collection = static_cast<Collection *>(
902 BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")));
903
904 if (ELEM(nullptr, ob, collection)) {
905 return OPERATOR_CANCELLED;
906 }
907
908 /* Early return check, if the object is already in collection
909 * we could skip all the dependency check and just consider
910 * operator is finished.
911 */
912 if (BKE_collection_has_object(collection, ob)) {
913 return OPERATOR_FINISHED;
914 }
915
916 /* Currently this should not be allowed (might be supported in the future though...). */
917 if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
918 BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is overridden");
919 return OPERATOR_CANCELLED;
920 }
921 /* Linked collections are already checked for by using RNA_collection_local_itemf
922 * but operator can be called without invoke */
923 if (!ID_IS_EDITABLE(&collection->id)) {
924 BKE_report(op->reports, RPT_ERROR, "Could not add the collection because it is linked");
925 return OPERATOR_CANCELLED;
926 }
927
928 /* Adding object to collection which is used as dupli-collection for self is bad idea.
929 *
930 * It is also bad idea to add object to collection which is in collection which
931 * contains our current object.
932 */
933 if (BKE_collection_object_cyclic_check(bmain, ob, collection)) {
935 RPT_ERROR,
936 "Could not add the collection because of dependency cycle detected");
937 return OPERATOR_CANCELLED;
938 }
939
940 BKE_collection_object_add(bmain, collection, ob);
941
944
946
947 return OPERATOR_FINISHED;
948}
949
951{
952 PropertyRNA *prop;
953
954 /* identifiers */
955 ot->name = "Link to Collection";
956 ot->idname = "OBJECT_OT_collection_link";
957 ot->description = "Add an object to an existing collection";
958
959 /* API callbacks. */
960 ot->exec = collection_link_exec;
961 ot->invoke = WM_enum_search_invoke;
963
964 /* flags */
966
967 /* properties */
968 prop = RNA_def_enum(ot->srna, "collection", rna_enum_dummy_NULL_items, 0, "Collection", "");
971 ot->prop = prop;
972}
973
975{
976 Main *bmain = CTX_data_main(C);
977 Object *ob = context_object(C);
978 Collection *collection = static_cast<Collection *>(
979 CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data);
980
981 if (!ob || !collection) {
982 return OPERATOR_CANCELLED;
983 }
984 if (!ID_IS_EDITABLE(collection) || ID_IS_OVERRIDE_LIBRARY(collection)) {
986 RPT_ERROR,
987 "Cannot remove an object from a linked or library override collection");
988 return OPERATOR_CANCELLED;
989 }
990
991 BKE_collection_object_remove(bmain, collection, ob, false);
992
995
997
998 return OPERATOR_FINISHED;
999}
1000
1002{
1003 /* identifiers */
1004 ot->name = "Remove Collection";
1005 ot->idname = "OBJECT_OT_collection_remove";
1006 ot->description = "Remove the active object from this collection";
1007
1008 /* API callbacks. */
1009 ot->exec = collection_remove_exec;
1010 ot->poll = ED_operator_objectmode;
1011
1012 /* flags */
1013 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1014}
1015
1017{
1018 Main *bmain = CTX_data_main(C);
1019 Collection *collection = CTX_data_collection(C);
1020
1021 if (!collection) {
1022 return OPERATOR_CANCELLED;
1023 }
1024 if (collection->flag & COLLECTION_IS_MASTER) {
1025 return OPERATOR_CANCELLED;
1026 }
1027 BLI_assert((collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0);
1028 if (ID_IS_OVERRIDE_LIBRARY(collection) &&
1029 collection->id.override_library->hierarchy_root != &collection->id)
1030 {
1031 BKE_report(op->reports,
1032 RPT_ERROR,
1033 "Cannot unlink a library override collection which is not the root of its override "
1034 "hierarchy");
1035 return OPERATOR_CANCELLED;
1036 }
1037
1038 BKE_id_delete(bmain, collection);
1039
1041
1043
1044 return OPERATOR_FINISHED;
1045}
1046
1048{
1049 Collection *collection = CTX_data_collection(C);
1050
1051 if (!collection) {
1052 return false;
1053 }
1054 if (collection->flag & COLLECTION_IS_MASTER) {
1055 return false;
1056 }
1057 BLI_assert((collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0);
1058 if (ID_IS_OVERRIDE_LIBRARY(collection) &&
1059 collection->id.override_library->hierarchy_root != &collection->id)
1060 {
1061 return false;
1062 }
1063
1064 return ED_operator_objectmode(C);
1065}
1066
1068{
1069 /* identifiers */
1070 ot->name = "Unlink Collection";
1071 ot->idname = "OBJECT_OT_collection_unlink";
1072 ot->description = "Unlink the collection from all objects";
1073
1074 /* API callbacks. */
1075 ot->exec = collection_unlink_exec;
1076 ot->poll = collection_unlink_poll;
1077
1078 /* flags */
1079 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1080}
1081
1082/* Select objects in the same collection as the active */
1084{
1085 Scene *scene = CTX_data_scene(C);
1086 Collection *collection = static_cast<Collection *>(
1087 CTX_data_pointer_get_type(C, "collection", &RNA_Collection).data);
1088
1089 if (!collection) {
1090 return OPERATOR_CANCELLED;
1091 }
1092
1093 CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
1094 if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLE) != 0)) {
1095 if (BKE_collection_has_object_recursive(collection, base->object)) {
1096 base_select(base, BA_SELECT);
1097 }
1098 }
1099 }
1101
1104
1105 return OPERATOR_FINISHED;
1106}
1107
1109{
1110 /* identifiers */
1111 ot->name = "Select Objects in Collection";
1112 ot->idname = "OBJECT_OT_collection_objects_select";
1113 ot->description = "Select all objects in collection";
1114
1115 /* API callbacks. */
1116 ot->exec = select_grouped_exec;
1117 ot->poll = ED_operator_objectmode;
1118
1119 /* flags */
1120 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1121}
1122
1123} // 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:1243
IDProperty * IDP_New(char type, const IDPropertyTemplate *val, blender::StringRef name, eIDPropertyFlag flags={}) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:1001
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:873
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:391
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:872
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:126
#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)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
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
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define ELEM(...)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:687
@ ID_RECALC_SELECT
Definition DNA_ID.h:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ 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)
@ 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:406
#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:458
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_OB_SELECT
Definition WM_types.hh:439
#define ND_SPACE_PROPERTIES
Definition WM_types.hh:526
#define NC_SCENE
Definition WM_types.hh:375
#define NC_GROUP
Definition WM_types.hh:380
#define NA_EDITED
Definition WM_types.hh:581
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define NC_OBJECT
Definition WM_types.hh:376
#define NC_SPACE
Definition WM_types.hh:389
#define ND_SPACE_OUTLINER
Definition WM_types.hh:524
BMesh const char void * data
#define ID_IS_LINKED(_id)
#define MAX_ID_NAME
#define ID_IS_EDITABLE(_id)
#define ID_IS_OVERRIDE_LIBRARY(_id)
int count
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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 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)
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)
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_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:334
IDOverrideLibrary * override_library
Definition DNA_ID.h:459
short flag
Definition DNA_ID.h:420
char name[66]
Definition DNA_ID.h:415
ListBase layer_collections
struct Collection * collection
ListBase collections
Definition BKE_main.hh:267
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]
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
void label(blender::StringRef name, int icon)
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
#define N_(msgid)
void WM_main_add_notifier(uint type, void *reference)
wmOperatorStatus 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:4225
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 *)