Blender V5.0
outliner_tools.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "CLG_log.h"
12
13#include "DNA_anim_types.h"
14#include "DNA_armature_types.h"
17#include "DNA_curves_types.h"
19#include "DNA_light_types.h"
20#include "DNA_linestyle_types.h"
21#include "DNA_material_types.h"
22#include "DNA_mesh_types.h"
23#include "DNA_meta_types.h"
24#include "DNA_modifier_types.h"
25#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28#include "DNA_sequence_types.h"
29#include "DNA_volume_types.h"
30#include "DNA_world_types.h"
31
32#include "BLI_listbase.h"
33#include "BLI_map.hh"
34#include "BLI_set.hh"
35#include "BLI_string.h"
36#include "BLI_utildefines.h"
37#include "BLI_vector.hh"
38
39#include "BLT_translation.hh"
40
41#include "BKE_anim_data.hh"
42#include "BKE_animsys.h"
43#include "BKE_armature.hh"
44#include "BKE_collection.hh"
45#include "BKE_constraint.h"
46#include "BKE_context.hh"
47#include "BKE_fcurve.hh"
48#include "BKE_global.hh"
49#include "BKE_grease_pencil.hh"
50#include "BKE_idtype.hh"
51#include "BKE_layer.hh"
52#include "BKE_lib_id.hh"
53#include "BKE_lib_override.hh"
54#include "BKE_lib_query.hh"
55#include "BKE_lib_remap.hh"
56#include "BKE_library.hh"
57#include "BKE_main.hh"
59#include "BKE_object.hh"
60#include "BKE_report.hh"
61#include "BKE_scene.hh"
62#include "BKE_screen.hh"
63
64#include "DEG_depsgraph.hh"
66
67#include "ED_node.hh"
68#include "ED_object.hh"
69#include "ED_outliner.hh"
70#include "ED_scene.hh"
71#include "ED_screen.hh"
72#include "ED_sequencer.hh"
73#include "ED_undo.hh"
74
75#include "WM_api.hh"
76#include "WM_message.hh"
77#include "WM_types.hh"
78
79#include "UI_interface.hh"
81#include "UI_resources.hh"
82#include "UI_view2d.hh"
83
85
86#include "RNA_access.hh"
87#include "RNA_define.hh"
88#include "RNA_enum_types.hh"
89
90#include "ANIM_action_legacy.hh"
91
92#include "SEQ_relations.hh"
93#include "SEQ_sequencer.hh"
94
95#include "outliner_intern.hh"
99#include "tree/tree_iterator.hh"
100
101namespace blender::ed::outliner {
102
103static CLG_LogRef LOG = {"outliner.tools"};
104
105/* -------------------------------------------------------------------- */
108
110 const TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel)
111{
112 *scenelevel = *objectlevel = *idlevel = *datalevel = 0;
113
114 const TreeStoreElem *tselem = TREESTORE(te);
115 if ((tselem->flag & TSE_SELECTED) == 0) {
116 return;
117 }
118
119 /* Layer collection points to collection ID. */
120 if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
121 *datalevel = tselem->type;
122 }
123 else {
124 const int idcode = int(GS(tselem->id->name));
125 bool is_standard_id = false;
126 switch ((ID_Type)idcode) {
127 case ID_SCE:
128 *scenelevel = 1;
129 break;
130 case ID_OB:
131 *objectlevel = 1;
132 break;
133
134 case ID_ME:
135 case ID_CU_LEGACY:
136 case ID_MB:
137 case ID_LT:
138 case ID_LA:
139 case ID_AR:
140 case ID_CA:
141 case ID_SPK:
142 case ID_MA:
143 case ID_TE:
144 case ID_IM:
145 case ID_SO:
146 case ID_KE:
147 case ID_WO:
148 case ID_AC:
149 case ID_TXT:
150 case ID_GR:
151 case ID_LS:
152 case ID_LI:
153 case ID_VF:
154 case ID_NT:
155 case ID_BR:
156 case ID_PA:
157 case ID_GD_LEGACY:
158 case ID_MC:
159 case ID_MSK:
160 case ID_PAL:
161 case ID_PC:
162 case ID_CF:
163 case ID_WS:
164 case ID_LP:
165 case ID_CV:
166 case ID_PT:
167 case ID_VO:
168 case ID_GP:
169 is_standard_id = true;
170 break;
171 case ID_WM:
172 case ID_SCR:
173 /* Those are ignored here. */
174 /* NOTE: while Screens should be manageable here, deleting a screen used by a workspace
175 * will cause crashes when trying to use that workspace, so for now let's play minimal,
176 * safe change. */
177 break;
178 }
179 if (idcode == ID_NLA) {
180 /* Fake one, not an actual ID type... */
181 is_standard_id = true;
182 }
183
184 if (is_standard_id) {
185 *idlevel = idcode;
186 }
187 }
188
189 /* Return values are exclusive, only one may be non-null. */
190 BLI_assert(((*scenelevel != 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel == 0)) ||
191 ((*scenelevel == 0) && (*objectlevel != 0) && (*idlevel == 0) && (*datalevel == 0)) ||
192 ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel != 0) && (*datalevel == 0)) ||
193 ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel != 0)) ||
194 /* All null. */
195 ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel == 0)));
196}
197
198static TreeElement *get_target_element(const SpaceOutliner *space_outliner)
199{
201
202 return te;
203}
204
206{
208 return false;
209 }
210 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
211 TreeElement *te = get_target_element(space_outliner);
212 if (te == nullptr) {
213 return false;
214 }
215
216 return true;
217}
218
220 ReportList *reports,
221 Scene * /*scene*/,
222 TreeElement * /*te*/,
223 TreeStoreElem *tsep,
224 TreeStoreElem *tselem)
225{
226 if (!tsep || !TSE_IS_REAL_ID(tsep)) {
227 /* Valid case, no parent element of the action or it is not an ID (could be a #TSE_ID_BASE
228 * for example) so there's no data to unlink from. */
229 BKE_reportf(reports,
231 "Cannot unlink action '%s'. It's not clear which object or object-data it "
232 "should be unlinked from, there's no object or object-data as parent in the "
233 "Outliner tree",
234 tselem->id->name + 2);
235 return;
236 }
237
238 /* just set action to nullptr */
239 BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, nullptr);
241}
242
243static void unlink_material_fn(bContext * /*C*/,
244 ReportList *reports,
245 Scene * /*scene*/,
246 TreeElement *te,
247 TreeStoreElem *tsep,
248 TreeStoreElem *tselem)
249{
250 const bool te_is_material = TSE_IS_REAL_ID(tselem) && (GS(tselem->id->name) == ID_MA);
251
252 if (!te_is_material) {
253 /* Just fail silently. Another element may be selected that is a material, we don't want to
254 * confuse users with an error in that case. */
255 return;
256 }
257
258 if (!tsep || !TSE_IS_REAL_ID(tsep)) {
259 /* Valid case, no parent element of the material or it is not an ID (could be a #TSE_ID_BASE
260 * for example) so there's no data to unlink from. */
261 BKE_reportf(reports,
263 "Cannot unlink material '%s'. It's not clear which object or object-data it "
264 "should be unlinked from, there's no object or object-data as parent in the "
265 "Outliner tree",
266 tselem->id->name + 2);
267 return;
268 }
269
270 if (!ID_IS_EDITABLE(tsep->id) || ID_IS_OVERRIDE_LIBRARY(tsep->id)) {
271 BKE_reportf(reports,
273 "Cannot unlink the material '%s' from linked object data",
274 tselem->id->name + 2);
275 return;
276 }
277
278 Material **matar = nullptr;
279 int a, totcol = 0;
280
281 switch (GS(tsep->id->name)) {
282 case ID_OB: {
283 Object *ob = (Object *)tsep->id;
284 totcol = ob->totcol;
285 matar = ob->mat;
286 break;
287 }
288 case ID_ME: {
289 Mesh *mesh = (Mesh *)tsep->id;
290 totcol = mesh->totcol;
291 matar = mesh->mat;
292 break;
293 }
294 case ID_CU_LEGACY: {
295 Curve *cu = (Curve *)tsep->id;
296 totcol = cu->totcol;
297 matar = cu->mat;
298 break;
299 }
300 case ID_MB: {
301 MetaBall *mb = (MetaBall *)tsep->id;
302 totcol = mb->totcol;
303 matar = mb->mat;
304 break;
305 }
306 case ID_CV: {
307 Curves *curves = (Curves *)tsep->id;
308 totcol = curves->totcol;
309 matar = curves->mat;
310 break;
311 }
312 case ID_PT: {
314 totcol = pointcloud->totcol;
315 matar = pointcloud->mat;
316 break;
317 }
318 case ID_VO: {
319 Volume *volume = (Volume *)tsep->id;
320 totcol = volume->totcol;
321 matar = volume->mat;
322 break;
323 }
324 default:
326 }
327
328 if (LIKELY(matar != nullptr)) {
329 for (a = 0; a < totcol; a++) {
330 if (a == te->index && matar[a]) {
331 id_us_min(&matar[a]->id);
332 matar[a] = nullptr;
333 }
334 }
335 }
336}
337
338static void unlink_texture_fn(bContext * /*C*/,
339 ReportList *reports,
340 Scene * /*scene*/,
341 TreeElement *te,
342 TreeStoreElem *tsep,
343 TreeStoreElem *tselem)
344{
345 if (!tsep || !TSE_IS_REAL_ID(tsep)) {
346 /* Valid case, no parent element of the texture or it is not an ID (could be a #TSE_ID_BASE
347 * for example) so there's no data to unlink from. */
348 BKE_reportf(reports,
350 "Cannot unlink texture '%s'. It's not clear which Freestyle line style it should "
351 "be unlinked from, there's no Freestyle line style as parent in the Outliner tree",
352 tselem->id->name + 2);
353 return;
354 }
355
356 MTex **mtex = nullptr;
357 int a;
358
359 if (GS(tsep->id->name) == ID_LS) {
361 mtex = ls->mtex;
362 }
363 else {
364 return;
365 }
366
367 for (a = 0; a < MAX_MTEX; a++) {
368 if (a == te->index && mtex[a]) {
369 if (mtex[a]->tex) {
370 id_us_min(&mtex[a]->tex->id);
371 mtex[a]->tex = nullptr;
372 }
373 }
374 }
375}
376
378 ReportList *reports,
379 Scene * /*scene*/,
380 TreeElement * /*te*/,
381 TreeStoreElem *tsep,
382 TreeStoreElem *tselem)
383{
384 Main *bmain = CTX_data_main(C);
385 Collection *collection = (Collection *)tselem->id;
386
387 if (!tsep || !TSE_IS_REAL_ID(tsep)) {
388 /* Valid case, no parent element of the collection or it is not an ID (could be a #TSE_ID_BASE
389 * for example) so there's no data to unlink from. */
390 BKE_reportf(reports,
392 "Cannot unlink collection '%s'. It's not clear which scene, collection or "
393 "instance empties it should be unlinked from, there's no scene, collection or "
394 "instance empties as parent in the Outliner tree",
395 tselem->id->name + 2);
396 return;
397 }
398
399 if (tsep && (!ID_IS_EDITABLE(tsep->id) || ID_IS_OVERRIDE_LIBRARY(tsep->id))) {
400 BKE_reportf(reports,
402 "Cannot unlink collection '%s' parented to another linked collection '%s'",
403 collection->id.name + 2,
404 tsep->id->name + 2);
405 return;
406 }
407
408 if (tsep) {
409 if (GS(tsep->id->name) == ID_OB) {
410 Object *ob = (Object *)tsep->id;
411 ob->instance_collection = nullptr;
414 }
415 else if (GS(tsep->id->name) == ID_GR) {
416 Collection *parent = (Collection *)tsep->id;
417 id_fake_user_set(&collection->id);
418 BKE_collection_child_remove(bmain, parent, collection);
421 }
422 else if (GS(tsep->id->name) == ID_SCE) {
423 Scene *scene = (Scene *)tsep->id;
424 Collection *parent = scene->master_collection;
425 id_fake_user_set(&collection->id);
426 BKE_collection_child_remove(bmain, parent, collection);
429 }
430 }
431}
432
434 ReportList *reports,
435 Scene * /*scene*/,
436 TreeElement *te,
437 TreeStoreElem *tsep,
438 TreeStoreElem *tselem)
439{
440 if (tsep && tsep->id) {
441
442 if (!TSE_IS_REAL_ID(tsep)) {
443 return;
444 }
445 Main *bmain = CTX_data_main(C);
446 Object *ob = (Object *)tselem->id;
447 const eSpaceOutliner_Mode outliner_mode = eSpaceOutliner_Mode(
448 CTX_wm_space_outliner(C)->outlinevis);
449
450 if (GS(tsep->id->name) == ID_OB) {
451 /* Parented objects need to find which collection to unlink from. */
452 TreeElement *te_parent = te->parent;
453 while (tsep && GS(tsep->id->name) == ID_OB) {
454 if (!ID_IS_EDITABLE(tsep->id)) {
455 BKE_reportf(reports,
457 "Cannot unlink object '%s' parented to another linked object '%s'",
458 ob->id.name + 2,
459 tsep->id->name + 2);
460 return;
461 }
462 te_parent = te_parent->parent;
463 tsep = te_parent ? TREESTORE(te_parent) : nullptr;
464 }
465 }
466
467 if (tsep && tsep->id) {
468 if (!ID_IS_EDITABLE(tsep->id) || ID_IS_OVERRIDE_LIBRARY(tsep->id)) {
469 BKE_reportf(reports,
471 "Cannot unlink object '%s' from linked collection or scene '%s'",
472 ob->id.name + 2,
473 tsep->id->name + 2);
474 return;
475 }
476 switch (GS(tsep->id->name)) {
477 case ID_GR: {
478 Collection *parent = (Collection *)tsep->id;
479 BKE_collection_object_remove(bmain, parent, ob, true);
481 break;
482 }
483 case ID_SCE: {
484 Scene *scene = reinterpret_cast<Scene *>(tsep->id);
485 /* In Scene view, remove the object from all collections in the scene. */
486 if (outliner_mode == SO_SCENES) {
487 FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
488 if (BKE_collection_has_object(collection, ob)) {
489 BKE_collection_object_remove(bmain, collection, ob, true);
490 DEG_id_tag_update(&collection->id, ID_RECALC_HIERARCHY);
492 }
493 }
495 }
496 /* Otherwise, remove the object from the scene's main collection. */
497 else {
498 Collection *parent = scene->master_collection;
499 BKE_collection_object_remove(bmain, parent, ob, true);
501 }
502 break;
503 }
504 default: {
505 /* Un-handled case, should never be reached. */
507 return;
508 }
509 }
510 /* NOTE: Cannot risk tagging the object here, as it may have been deleted if its last usage
511 * was removed by above code. */
514 }
515 }
516}
517
518static void unlink_world_fn(bContext * /*C*/,
519 ReportList *reports,
520 Scene * /*scene*/,
521 TreeElement * /*te*/,
522 TreeStoreElem *tsep,
523 TreeStoreElem *tselem)
524{
525 if (!tsep || !TSE_IS_REAL_ID(tsep)) {
526 /* Valid case, no parent element of the world or it is not an ID (could be a #TSE_ID_BASE
527 * for example) so there's no data to unlink from. */
528 BKE_reportf(reports,
530 "Cannot unlink world '%s'. It's not clear which scene it should be unlinked from, "
531 "there's no scene as parent in the Outliner tree",
532 tselem->id->name + 2);
533 return;
534 }
535
536 Scene *parscene = (Scene *)tsep->id;
537 World *wo = (World *)tselem->id;
538
539 /* need to use parent scene not just scene, otherwise may end up getting wrong one */
540 id_us_min(&wo->id);
541 parscene->world = nullptr;
542}
543
545 ReportList *reports,
546 Scene *scene,
547 SpaceOutliner *space_outliner,
548 outliner_operation_fn operation_fn)
549{
550 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
551 TreeStoreElem *tselem = TREESTORE(te);
552 if (tselem->flag & TSE_SELECTED) {
553 if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) ||
554 tselem->type == TSE_LAYER_COLLECTION)
555 {
556 TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : nullptr;
557 operation_fn(C, reports, scene, te, tsep, tselem);
558 }
559 }
560 });
561}
562
564 /* Only selected items. */
566 /* Only content 'inside' selected items (their sub-tree). */
568 /* Combining both options above. */
570};
571
574 "SELECTED",
575 0,
576 "Selected",
577 "Apply the operation over selected data-blocks only"},
579 "CONTENT",
580 0,
581 "Content",
582 "Apply the operation over content of the selected items only (the data-blocks in their "
583 "sub-tree)"},
585 "SELECTED_AND_CONTENT",
586 0,
587 "Selected & Content",
588 "Apply the operation over selected data-blocks and all their dependencies"},
589 {0, nullptr, 0, nullptr, nullptr},
590};
591
593 bContext *C,
594 ReportList *reports,
595 Scene *scene,
597 TreeStoreElem *tselem,
598 const bool has_parent_selected,
599 outliner_operation_fn operation_fn,
600 eOutlinerLibOpSelectionSet selection_set)
601{
602 const bool do_selected = ELEM(selection_set,
605 const bool do_content = ELEM(selection_set,
608
609 const bool is_selected = tselem->flag & TSE_SELECTED;
610 if ((is_selected && do_selected) || (has_parent_selected && do_content)) {
611 if (((tselem->type == TSE_SOME_ID) && (element->idcode != 0)) ||
612 tselem->type == TSE_LAYER_COLLECTION)
613 {
614 TreeStoreElem *tsep = element->parent ? TREESTORE(element->parent) : nullptr;
615 operation_fn(C, reports, scene, element, tsep, tselem);
616 }
617 }
618 return is_selected;
619}
620
622 ReportList *reports,
623 Scene *scene,
624 SpaceOutliner *space_outliner,
625 const ListBase &subtree,
626 const bool has_parent_selected,
627 outliner_operation_fn operation_fn,
628 eOutlinerLibOpSelectionSet selection_set)
629{
631 /* Get needed data out in case element gets freed. */
633 const ListBase subtree = element->subtree;
634
636 C, reports, scene, element, tselem, has_parent_selected, operation_fn, selection_set);
637
638 /* Don't access element from now on, it may be freed. Note that the open/collapsed state may
639 * also have been changed in the visitor callback. */
641 reports,
642 scene,
643 space_outliner,
644 subtree,
645 is_selected || has_parent_selected,
646 operation_fn,
647 selection_set);
648 }
649}
650
652 ReportList *reports,
653 Scene *scene,
654 SpaceOutliner *space_outliner,
655 outliner_operation_fn operation_fn,
656 eOutlinerLibOpSelectionSet selection_set,
657 const bool do_active_element_first)
658{
659 if (do_active_element_first) {
660 TreeElement *active_element = outliner_find_element_with_flag(&space_outliner->tree,
661 TSE_ACTIVE);
662 if (active_element != nullptr) {
663 TreeStoreElem *tselem = TREESTORE(active_element);
664 const ListBase subtree = active_element->subtree;
665
667 C, reports, scene, active_element, tselem, false, operation_fn, selection_set);
668
669 /* Don't access element from now on, it may be freed. Note that the open/collapsed state may
670 * also have been changed in the visitor callback. */
672 C, reports, scene, space_outliner, subtree, is_selected, operation_fn, selection_set);
673 }
674 }
675
677 C, reports, scene, space_outliner, space_outliner->tree, false, operation_fn, selection_set);
678}
679
681
682/* -------------------------------------------------------------------- */
685
689
691 {OL_SCENE_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
692 {0, nullptr, 0, nullptr, nullptr},
693};
694
696 bContext *C,
697 SpaceOutliner *space_outliner,
699 bool (*operation_fn)(bContext *, eOutliner_PropSceneOps, TreeElement *, TreeStoreElem *))
700{
701 bool success = false;
702
703 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
704 TreeStoreElem *tselem = TREESTORE(te);
705 if (tselem->flag & TSE_SELECTED) {
706 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_SCE)) {
707 if (operation_fn(C, event, te, tselem)) {
708 success = true;
709 }
710 }
711 }
712 });
713
714 return success;
715}
716
717static bool scene_fn(bContext *C,
719 TreeElement * /*te*/,
720 TreeStoreElem *tselem)
721{
722 Scene *scene = (Scene *)tselem->id;
723
724 if (event == OL_SCENE_OP_DELETE) {
725 if (ED_scene_delete(C, CTX_data_main(C), scene)) {
727 }
728 else {
729 return false;
730 }
731 }
732
733 return true;
734}
735
737{
738 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
740
741 if (outliner_do_scene_operation(C, space_outliner, event, scene_fn) == false) {
742 return OPERATOR_CANCELLED;
743 }
744
745 if (event == OL_SCENE_OP_DELETE) {
746 outliner_cleanup_tree(space_outliner);
747 ED_undo_push(C, "Delete Scene(s)");
748 }
749 else {
751 return OPERATOR_CANCELLED;
752 }
753
754 return OPERATOR_FINISHED;
755}
756
758{
759 /* identifiers */
760 ot->name = "Outliner Scene Operation";
761 ot->idname = "OUTLINER_OT_scene_operation";
762 ot->description = "Context menu for scene operations";
763
764 /* callbacks */
765 ot->invoke = WM_menu_invoke;
768
769 ot->flag = 0;
770
771 ot->prop = RNA_def_enum(ot->srna, "type", prop_scene_op_types, 0, "Scene Operation", "");
772}
773
775
776/* -------------------------------------------------------------------- */
779
789
791 const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items)
792{
793 char name[64];
794 int iconid;
795
797 TreeStoreElem *tselem = TREESTORE(te);
798
799 if (tree_element_id_type_to_index(te) == type && tselem_type == tselem->type) {
800 if (BLI_strcasestr(te->name, str)) {
801 STRNCPY(name, te->name);
802
803 iconid = tree_element_get_icon(tselem, te).icon;
804
805 /* Don't allow duplicate named items */
806 if (UI_search_items_find_index(items, name) == -1) {
807 if (!UI_search_item_add(items, name, te, iconid, 0, 0)) {
808 break;
809 }
810 }
811 }
812 }
813
814 merged_element_search_fn_recursive(&te->subtree, tselem_type, type, str, items);
815 }
816}
817
818/* Get a list of elements that match the search string */
820 void *data,
821 const char *str,
822 uiSearchItems *items,
823 const bool /*is_first*/)
824{
825 MergedSearchData *search_data = (MergedSearchData *)data;
826 TreeElement *parent = search_data->parent_element;
827 TreeElement *te = search_data->select_element;
828
829 int type = tree_element_id_type_to_index(te);
830
831 merged_element_search_fn_recursive(&parent->subtree, TREESTORE(te)->type, type, str, items);
832}
833
834/* Activate an element from the merged element search menu */
835static void merged_element_search_exec_fn(bContext *C, void * /*arg1*/, void *element)
836{
837 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
839
841
843}
844
850{
851 static char search[64] = "";
852 uiBlock *block;
853 uiBut *but;
854
855 /* Clear search on each menu creation */
856 *search = '\0';
857
858 block = UI_block_begin(C, region, __func__, ui::EmbossType::Emboss);
861
862 short menu_width = 10 * UI_UNIT_X;
863 but = uiDefSearchBut(
864 block, search, 0, ICON_VIEWZOOM, sizeof(search), 0, 0, menu_width, UI_UNIT_Y, "");
866 nullptr,
868 data,
869 false,
870 nullptr,
872 nullptr);
874
875 /* Fake button to hold space for search items */
876 const int height = UI_searchbox_size_y() - UI_SEARCHBOX_BOUNDS;
877 uiDefBut(
878 block, ButType::Label, 0, "", 0, -height, menu_width, height, nullptr, 0, 0, std::nullopt);
879
880 /* Center the menu on the cursor */
881 const int offset[2] = {-(menu_width / 2), 0};
883
884 return block;
885}
886
888 TreeElement *parent_te,
889 TreeElement *activate_te)
890{
891 MergedSearchData *select_data = MEM_callocN<MergedSearchData>("merge_search_data");
892 select_data->parent_element = parent_te;
893 select_data->select_element = activate_te;
894
896}
897
899 ReportList * /*reports*/,
900 Scene * /*scene*/,
901 TreeElement * /*te*/,
902 TreeStoreElem * /*tsep*/,
903 TreeStoreElem *tselem)
904{
905 const Scene *scene = CTX_data_scene(C);
906 ViewLayer *view_layer = CTX_data_view_layer(C);
907 Object *ob = (Object *)tselem->id;
908 BKE_view_layer_synced_ensure(scene, view_layer);
909 Base *base = BKE_view_layer_base_find(view_layer, ob);
910
911 if (base) {
913 }
914}
915
917
918/* -------------------------------------------------------------------- */
921
923 ReportList * /*reports*/,
924 Scene * /*scene*/,
925 TreeElement *te,
926 TreeStoreElem * /*tsep*/,
927 TreeStoreElem * /*tselem*/)
928{
929 /* Don't extend because this toggles, which is nice for Ctrl-Click but not for a menu item.
930 * it's especially confusing when multiple items are selected since some toggle on/off. */
931 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
933 C, space_outliner, te, OL_ITEM_SELECT | OL_ITEM_ACTIVATE | OL_ITEM_RECURSIVE);
934}
935
937 ReportList * /*reports*/,
938 Scene * /*scene*/,
939 TreeElement * /*te*/,
940 TreeStoreElem * /*tsep*/,
941 TreeStoreElem *tselem)
942{
943 const Scene *scene = CTX_data_scene(C);
944 ViewLayer *view_layer = CTX_data_view_layer(C);
945 Object *ob = (Object *)tselem->id;
946 BKE_view_layer_synced_ensure(scene, view_layer);
947 Base *base = BKE_view_layer_base_find(view_layer, ob);
948
949 if (base) {
950 base->flag &= ~BASE_SELECTED;
951 }
952}
953
954static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *scene, Object *ob)
955{
956 if (ob) {
957 Main *bmain = CTX_data_main(C);
958 if (ob->id.tag & ID_TAG_INDIRECT) {
960 reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2);
961 return;
962 }
963 if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
965 {
966 BKE_reportf(reports,
968 "Cannot delete object '%s' from scene '%s', indirectly used objects need at "
969 "least one user",
970 ob->id.name + 2,
971 scene->id.name + 2);
972 return;
973 }
974
975 /* Check also library later. */
976 if ((ob->mode & OB_MODE_EDIT) && BKE_object_is_in_editmode(ob)) {
978 }
979 BKE_id_delete(bmain, ob);
980 }
981}
982
983static void id_local_fn(bContext *C,
984 ReportList * /*reports*/,
985 Scene * /*scene*/,
986 TreeElement * /*te*/,
987 TreeStoreElem * /*tsep*/,
988 TreeStoreElem *tselem)
989{
990 if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & ID_TAG_EXTERN)) {
991 Main *bmain = CTX_data_main(C);
994 }
995 }
996 else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) {
998 }
999}
1000
1019
1022
1025
1031
1035
1042
1045
1046 void id_root_add(ID *id_hierarchy_root_reference,
1047 ID *id_root_reference,
1048 ID *id_instance_hint,
1049 const bool is_override_instancing_object)
1050 {
1051 OutlinerLiboverrideDataIDRoot id_root_data;
1052 id_root_data.id_root_reference = id_root_reference;
1053 id_root_data.id_hierarchy_root_override = nullptr;
1054 id_root_data.id_instance_hint = id_instance_hint;
1055 id_root_data.is_override_instancing_object = is_override_instancing_object;
1056
1057 Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
1058 id_hierarchy_root_reference);
1059 value.append(id_root_data);
1060 }
1061 void id_root_set(ID *id_hierarchy_root_reference)
1062 {
1063 OutlinerLiboverrideDataIDRoot id_root_data;
1064 id_root_data.id_root_reference = nullptr;
1065 id_root_data.id_hierarchy_root_override = nullptr;
1066 id_root_data.id_instance_hint = nullptr;
1067 id_root_data.is_override_instancing_object = false;
1068
1069 Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
1070 id_hierarchy_root_reference);
1071 if (value.is_empty()) {
1072 value.append(id_root_data);
1073 }
1074 }
1075};
1076
1077/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
1078 * hierarchy. */
1081 ReportList *reports,
1082 TreeElement *te,
1083 TreeStoreElem *tsep,
1084 TreeStoreElem *tselem)
1085{
1086 BLI_assert(TSE_IS_REAL_ID(tselem));
1087
1088 const bool do_hierarchy = data->do_hierarchy;
1089 ID *id_root_reference = tselem->id;
1090
1091 if (!BKE_idtype_idcode_is_linkable(GS(id_root_reference->name)) ||
1093 0)
1094 {
1095 return;
1096 }
1097
1098 /* Only process a given ID once. Otherwise, all kind of weird things can happen if e.g. a
1099 * selected sub-collection is part of more than one override hierarchies. */
1100 if (data->selected_id_uid.contains(id_root_reference->session_uid)) {
1101 return;
1102 }
1103 data->selected_id_uid.add(id_root_reference->session_uid);
1104
1105 if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root_reference) && !ID_IS_LINKED(id_root_reference)) {
1107 return;
1108 }
1109
1110 if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_root_reference)) {
1111 if (ID_IS_LINKED(id_root_reference)) {
1113 reports,
1115 "Could not create library override from data-block '%s', as it is not overridable",
1116 id_root_reference->name);
1117 }
1118 /* Else it's a local ID, do not bother reporting this, as it gets annoyingly noisy then when
1119 * operated e.g. on a hierarchy of liboverrides. */
1120 return;
1121 }
1122
1123 BLI_assert(do_hierarchy);
1124 UNUSED_VARS_NDEBUG(do_hierarchy);
1125
1126 if (GS(id_root_reference->name) == ID_GR && (tselem->flag & TSE_CLOSED) != 0) {
1127 /* If selected element is a (closed) collection, check all of its objects recursively, and also
1128 * consider the armature ones as 'selected' (i.e. to not become system overrides). */
1129 Collection *root_collection = reinterpret_cast<Collection *>(id_root_reference);
1130 FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (root_collection, object_iter) {
1131 if (id_root_reference->lib == object_iter->id.lib && object_iter->type == OB_ARMATURE) {
1132 data->selected_id_uid.add(object_iter->id.session_uid);
1133 }
1134 }
1136 }
1137
1138 ID *id_instance_hint = nullptr;
1139 bool is_override_instancing_object = false;
1140 if (tsep != nullptr && tsep->type == TSE_SOME_ID && tsep->id != nullptr &&
1141 GS(tsep->id->name) == ID_OB && !ID_IS_OVERRIDE_LIBRARY(tsep->id))
1142 {
1143 Object *ob = reinterpret_cast<Object *>(tsep->id);
1144 if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root_reference) {
1145 BLI_assert(GS(id_root_reference->name) == ID_GR);
1146 /* Empty instantiating the collection we override, we need to pass it to BKE overriding code
1147 * for proper handling. */
1148 id_instance_hint = tsep->id;
1149 is_override_instancing_object = true;
1150 }
1151 }
1152
1153 if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) &&
1154 !(ID_IS_LINKED(id_root_reference) && do_hierarchy))
1155 {
1156 return;
1157 }
1158
1159 Main *bmain = CTX_data_main(C);
1160
1161 if (do_hierarchy) {
1162 /* Tag all linked parents in tree hierarchy to be also overridden. */
1163 ID *id_hierarchy_root_reference = id_root_reference;
1164 while ((te = te->parent) != nullptr) {
1165 if (!TSE_IS_REAL_ID(te->store_elem)) {
1166 continue;
1167 }
1168
1169 /* Tentative hierarchy root. */
1170 ID *id_current_hierarchy_root = te->store_elem->id;
1171
1172 /* If the parent ID is from a different library than the reference root one, we are done
1173 * with upwards tree processing in any case. */
1174 if (id_current_hierarchy_root->lib != id_root_reference->lib) {
1175 if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
1176 /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
1177 * get an actual real override. */
1178 continue;
1179 }
1180
1181 /* If the parent ID is already an override, and is valid (i.e. local override), we can
1182 * access its hierarchy root directly. */
1183 if (!ID_IS_LINKED(id_current_hierarchy_root) &&
1184 ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
1185 id_current_hierarchy_root->override_library->reference->lib == id_root_reference->lib)
1186 {
1187 id_hierarchy_root_reference =
1188 id_current_hierarchy_root->override_library->hierarchy_root;
1189 BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
1190 break;
1191 }
1192
1193 if (ID_IS_LINKED(id_current_hierarchy_root)) {
1194 /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
1195 * would most likely generate invisible/confusing/hard to use and manage overrides. */
1196 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
1197 BKE_reportf(reports,
1199 "Invalid anchor ('%s') found, needed to create library override from "
1200 "data-block '%s'",
1201 id_current_hierarchy_root->name,
1202 id_root_reference->name);
1203 return;
1204 }
1205
1206 /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
1207 * current `id_hierarchy_root_reference` is our best candidate. */
1208
1209 break;
1210 }
1211
1212 /* If some element in the tree needs to be overridden, but its ID is not overridable,
1213 * abort. */
1214 if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
1215 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
1216 BKE_reportf(reports,
1218 "Could not create library override from data-block '%s', one of its parents "
1219 "is not overridable ('%s')",
1220 id_root_reference->name,
1221 id_current_hierarchy_root->name);
1222 return;
1223 }
1224 id_current_hierarchy_root->tag |= ID_TAG_DOIT;
1225 id_hierarchy_root_reference = id_current_hierarchy_root;
1226 }
1227
1228 /* That case can happen when linked data is a complex mix involving several libraries and/or
1229 * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
1230 * from another library. Do not try to support such cases for now. */
1231 if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
1232 (!ID_IS_LINKED(id_hierarchy_root_reference) &&
1233 ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
1234 id_hierarchy_root_reference->override_library->reference->lib ==
1235 id_root_reference->lib)))
1236 {
1237 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
1238 BKE_reportf(reports,
1240 "Invalid hierarchy root ('%s') found, needed to create library override from "
1241 "data-block '%s'",
1242 id_hierarchy_root_reference->name,
1243 id_root_reference->name);
1244 return;
1245 }
1246
1247 /* While ideally this should not be needed, in practice user almost _never_ wants to actually
1248 * create liboverrides for all data under a selected hierarchy node, and this has currently a
1249 * dreadful consequences over performances (since it would call
1250 * #BKE_lib_override_library_create over _all_ items in the hierarchy). So only the clearing of
1251 * the system override flag is supported for non-selected items for now.
1252 */
1253 const bool is_selected = tselem->flag & TSE_SELECTED;
1254 if (!is_selected && data->id_hierarchy_roots.contains(id_hierarchy_root_reference)) {
1255 return;
1256 }
1257
1258 data->id_root_add(id_hierarchy_root_reference,
1259 id_root_reference,
1260 id_instance_hint,
1261 is_override_instancing_object);
1262 }
1263 else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
1264 data->id_root_add(
1265 id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object);
1266 }
1267}
1268
1270 Main &bmain,
1271 Scene *scene,
1272 ViewLayer *view_layer,
1274 ID *id_hierarchy_root_reference,
1276 bool &r_aggregated_success)
1277{
1278 BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) ||
1279 ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
1280
1281 const bool do_hierarchy = data.do_hierarchy;
1282
1283 /* NOTE: This process is not the most efficient, but allows to re-use existing code.
1284 * If this becomes a bottle-neck at some point, we need to implement a new
1285 * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of
1286 * a same hierarchy in a single call. */
1287 for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) {
1288 /* For now, remap all local usages of linked ID to local override one here. */
1289 ID *id_iter;
1290 FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) {
1291 if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
1292 id_iter->tag &= ~ID_TAG_DOIT;
1293 }
1294 else {
1295 id_iter->tag |= ID_TAG_DOIT;
1296 }
1297 }
1299
1300 bool success = false;
1301 if (do_hierarchy) {
1302 ID *id_root_override = nullptr;
1303 success = BKE_lib_override_library_create(&bmain,
1304 scene,
1305 view_layer,
1306 nullptr,
1307 data_idroot.id_root_reference,
1308 id_hierarchy_root_reference,
1309 data_idroot.id_instance_hint,
1310 &id_root_override,
1311 data.do_fully_editable);
1312
1313 if (success) {
1314 BLI_assert(id_root_override != nullptr);
1315 BLI_assert(!ID_IS_LINKED(id_root_override));
1316 BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
1317
1318 ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
1319 BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override));
1320 if (ID_IS_LINKED(id_hierarchy_root_reference)) {
1321 BLI_assert(id_hierarchy_root_override->override_library->reference ==
1322 id_hierarchy_root_reference);
1323 /* If the hierarchy root reference was a linked data, after the first iteration there is
1324 * now a matching override, which shall be used for all further partial overrides with
1325 * this same hierarchy. */
1326 id_hierarchy_root_reference = id_hierarchy_root_override;
1327 }
1328 else {
1329 BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference);
1330 }
1331 data_idroot.id_hierarchy_root_override = id_hierarchy_root_override;
1332 data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uid);
1333 }
1334 }
1335 else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) {
1336 ID *id_root_override = BKE_lib_override_library_create_from_id(
1337 &bmain, data_idroot.id_root_reference, true);
1338
1339 success = id_root_override != nullptr;
1340 if (success) {
1341 BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
1343 }
1344 /* Cleanup. */
1346 BKE_main_id_tag_all(&bmain, ID_TAG_DOIT, false);
1347 }
1348 else {
1350 }
1351
1352 /* Remove the instance empty from this scene, the items now have an overridden collection
1353 * instead. */
1354 if (success && data_idroot.is_override_instancing_object) {
1355 BLI_assert(GS(data_idroot.id_instance_hint->name) == ID_OB);
1357 &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint));
1358 }
1359
1360 r_aggregated_success = r_aggregated_success && success;
1361 }
1362}
1363
1364/* Clear system override flag from newly created overrides which linked reference were previously
1365 * selected in the Outliner tree. */
1367 ReportList *reports,
1369{
1370 Main *bmain = CTX_data_main(C);
1371 Scene *scene = CTX_data_scene(C);
1372 ViewLayer *view_layer = CTX_data_view_layer(C);
1373 const bool do_hierarchy = data.do_hierarchy;
1374
1375 bool success = true;
1376 for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) {
1378 *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success);
1379 }
1380
1381 if (!success) {
1382 BKE_reportf(reports,
1384 "Could not create library override from one or more of the selected data-blocks");
1385 }
1386
1387 if (!do_hierarchy) {
1388 return;
1389 }
1390
1391 ID *id_iter;
1392 FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
1393 if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
1394 continue;
1395 }
1396 if (id_iter->override_library->hierarchy_root != nullptr &&
1397 !data.id_hierarchy_roots_uid.contains(
1399 {
1400 continue;
1401 }
1402 if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uid) ||
1403 data.selected_id_uid.contains(id_iter->session_uid))
1404 {
1406 }
1407 }
1409}
1410
1413 TreeStoreElem *tselem)
1414{
1415 BLI_assert(TSE_IS_REAL_ID(tselem));
1416 ID *id_root = tselem->id;
1417 const bool do_hierarchy = data->do_hierarchy;
1418
1419 if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
1420 CLOG_WARN(&LOG, "Could not reset library override of data block '%s'", id_root->name);
1421 return;
1422 }
1423
1424 Main *bmain = CTX_data_main(C);
1425
1426 if (do_hierarchy) {
1427 BKE_lib_override_library_id_hierarchy_reset(bmain, id_root, false);
1428 }
1429 else {
1430 BKE_lib_override_library_id_reset(bmain, id_root, false);
1431 }
1432}
1433
1435 ReportList * /*reports*/,
1437{
1438 Main *bmain = CTX_data_main(C);
1439 ViewLayer *view_layer = CTX_data_view_layer(C);
1440 Scene *scene = CTX_data_scene(C);
1441
1442 /* TODO: At some point this likely needs to be re-written as a BKE function instead, with better
1443 * handling of hierarchies among other things. */
1444
1445 /* Try to process all potential leaves first (deleting some liboverride leaves will turn other
1446 * liboverrides into leaves as well). */
1447 bool do_process_leaves = true;
1448 while (!data.id_hierarchy_roots.is_empty()) {
1449 bool has_found_leaves = false;
1450
1451 for (auto &&id : data.id_hierarchy_roots.keys()) {
1452 if (do_process_leaves) {
1454 /* If given ID is not using any other override (it's a 'leaf' in the override hierarchy),
1455 * delete it and remap its usages to its linked reference. Otherwise, keep it as a reset
1456 * system override. */
1457 bool do_remap_active = false;
1458 BKE_view_layer_synced_ensure(scene, view_layer);
1459 if (BKE_view_layer_active_object_get(view_layer) == reinterpret_cast<Object *>(id)) {
1460 BLI_assert(GS(id->name) == ID_OB);
1461 do_remap_active = true;
1462 }
1464 bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE);
1465 if (do_remap_active) {
1466 BKE_view_layer_synced_ensure(scene, view_layer);
1467 Object *ref_object = reinterpret_cast<Object *>(id->override_library->reference);
1468 Base *basact = BKE_view_layer_base_find(view_layer, ref_object);
1469 if (basact != nullptr) {
1470 view_layer->basact = basact;
1471 }
1473 }
1474
1475 BKE_id_delete(bmain, id);
1476 data.id_hierarchy_roots.remove(id);
1477 has_found_leaves = true;
1478 }
1479 }
1480 else {
1482 BKE_lib_override_library_id_reset(bmain, id, true);
1483 data.id_hierarchy_roots.remove(id);
1484 }
1485 }
1486
1487 do_process_leaves = has_found_leaves;
1488 }
1490}
1491
1493 ReportList *reports,
1494 TreeStoreElem *tselem)
1495{
1496 BLI_assert(TSE_IS_REAL_ID(tselem));
1497 ID *id = tselem->id;
1498
1499 if (!ID_IS_OVERRIDE_LIBRARY(id)) {
1500 return;
1501 }
1502 if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
1503 BKE_reportf(reports,
1505 "Cannot clear embedded library override '%s', only overrides of real data-blocks "
1506 "can be directly cleared",
1507 id->name);
1508 return;
1509 }
1510 if (ID_IS_LINKED(id)) {
1512 reports,
1514 "Cannot clear linked library override '%s', only local overrides can be directly cleared",
1515 id->name);
1516 return;
1517 }
1518
1519 data->id_root_set(id);
1520}
1521
1523{
1524 BLI_assert(TSE_IS_REAL_ID(tselem));
1525 ID *id_root = tselem->id;
1526
1527 if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
1528 CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
1529 return;
1530 }
1531
1532 if (id_root->override_library->hierarchy_root != nullptr) {
1533 id_root = id_root->override_library->hierarchy_root;
1534 }
1535
1536 data->id_root_set(id_root);
1537}
1538
1539/* Resync a hierarchy of library overrides. */
1541 ReportList *reports,
1543{
1544 Main *bmain = CTX_data_main(C);
1545 Scene *scene = CTX_data_scene(C);
1546 const bool do_hierarchy_enforce = data.do_resync_hierarchy_enforce;
1547
1548 BlendFileReadReport report{};
1549 report.reports = reports;
1550
1551 for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) {
1553 scene,
1555 id_hierarchy_root,
1556 nullptr,
1557 do_hierarchy_enforce,
1558 &report);
1559 }
1560
1562}
1563
1565 TreeStoreElem *tselem)
1566{
1567 BLI_assert(TSE_IS_REAL_ID(tselem));
1568 ID *id_root = tselem->id;
1569
1570 if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
1571 CLOG_WARN(&LOG, "Could not delete library override of data block '%s'", id_root->name);
1572 return;
1573 }
1574
1575 if (id_root->override_library->hierarchy_root != nullptr) {
1576 id_root = id_root->override_library->hierarchy_root;
1577 }
1578
1579 data->id_root_set(id_root);
1580}
1581
1582/* Clear (delete) a hierarchy of library overrides. */
1584 ReportList * /*reports*/,
1586{
1587 Main *bmain = CTX_data_main(C);
1588
1589 for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) {
1590 BKE_lib_override_library_delete(bmain, id_hierarchy_root);
1591 }
1592}
1593
1594static void id_fake_user_set_fn(bContext * /*C*/,
1595 ReportList * /*reports*/,
1596 Scene * /*scene*/,
1597 TreeElement * /*te*/,
1598 TreeStoreElem * /*tsep*/,
1599 TreeStoreElem *tselem)
1600{
1601 ID *id = tselem->id;
1602
1603 id_fake_user_set(id);
1604}
1605
1607 ReportList * /*reports*/,
1608 Scene * /*scene*/,
1609 TreeElement * /*te*/,
1610 TreeStoreElem * /*tsep*/,
1611 TreeStoreElem *tselem)
1612{
1613 ID *id = tselem->id;
1614
1616}
1617
1619 ReportList * /*reports*/,
1620 Scene * /*scene*/,
1621 TreeElement * /*te*/,
1622 TreeStoreElem * /*tsep*/,
1623 TreeStoreElem *tselem)
1624{
1625 ID *id = tselem->id;
1626
1628}
1629
1631 ReportList * /*reports*/,
1632 Scene * /*scene*/,
1633 TreeElement *te,
1634 TreeStoreElem *tsep,
1635 TreeStoreElem *tselem)
1636{
1637 /* This callback runs for all selected elements, some of which may not be actions which results
1638 * in a crash. */
1639 if (te->idcode != ID_AC) {
1640 return;
1641 }
1642
1643 ID *id = tselem->id;
1644
1645 if (id) {
1646 IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
1647 PropertyRNA *prop;
1648
1649 PointerRNA ptr = RNA_pointer_create_discrete(&iat->id, &RNA_AnimData, iat->adt);
1650 prop = RNA_struct_find_property(&ptr, "action");
1651
1652 id_single_user(C, id, &ptr, prop);
1653 }
1654}
1655
1657 ReportList * /*reports*/,
1658 Scene * /*scene*/,
1659 TreeElement * /*te*/,
1660 TreeStoreElem *tsep,
1661 TreeStoreElem *tselem)
1662{
1663 ID *id = tselem->id;
1664
1665 /* need to use parent scene not just scene, otherwise may end up getting wrong one */
1666 if (id) {
1667 Scene *parscene = (Scene *)tsep->id;
1668 PropertyRNA *prop;
1669
1670 PointerRNA ptr = RNA_id_pointer_create(&parscene->id);
1671 prop = RNA_struct_find_property(&ptr, "world");
1672
1673 id_single_user(C, id, &ptr, prop);
1674 }
1675}
1676
1678 ReportList *reports,
1679 Scene *scene_act,
1680 SpaceOutliner *space_outliner,
1681 ListBase *lb,
1682 outliner_operation_fn operation_fn,
1683 bool recurse_selected)
1684{
1685 LISTBASE_FOREACH (TreeElement *, te, lb) {
1686 TreeStoreElem *tselem = TREESTORE(te);
1687 bool select_handled = false;
1688 if (tselem->flag & TSE_SELECTED) {
1689 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
1690 /* When objects selected in other scenes, don't know if that should be allowed. */
1691 Scene *scene_owner = (Scene *)outliner_search_back(te, ID_SCE);
1692 if (scene_owner && scene_act != scene_owner) {
1694 }
1695 /* Important to use 'scene_owner' not scene_act else deleting objects can crash.
1696 * only use 'scene_act' when 'scene_owner' is nullptr, which can happen when the
1697 * outliner isn't showing scenes: Visible Layer draw mode for eg. */
1698 operation_fn(C, reports, scene_owner ? scene_owner : scene_act, te, nullptr, tselem);
1699 select_handled = true;
1700 }
1701 }
1702 if (TSELEM_OPEN(tselem, space_outliner)) {
1703 if ((select_handled == false) || recurse_selected) {
1705 C, reports, scene_act, space_outliner, &te->subtree, operation_fn, recurse_selected);
1706 }
1707 }
1708 }
1709}
1710
1712 ReportList *reports,
1713 Scene *scene_act,
1714 SpaceOutliner *space_outliner,
1715 ListBase *lb,
1716 outliner_operation_fn operation_fn)
1717{
1718 outliner_do_object_operation_ex(C, reports, scene_act, space_outliner, lb, operation_fn, true);
1719}
1720
1722
1723/* -------------------------------------------------------------------- */
1726
1727static void clear_animdata_fn(int /*event*/,
1728 TreeElement * /*te*/,
1729 TreeStoreElem *tselem,
1730 void * /*arg*/)
1731{
1732 BKE_animdata_free(tselem->id, true);
1734}
1735
1736static void unlinkact_animdata_fn(int /*event*/,
1737 TreeElement * /*te*/,
1738 TreeStoreElem *tselem,
1739 void * /*arg*/)
1740{
1741 /* just set action to nullptr */
1742 BKE_animdata_set_action(nullptr, tselem->id, nullptr);
1744}
1745
1746static void cleardrivers_animdata_fn(int /*event*/,
1747 TreeElement * /*te*/,
1748 TreeStoreElem *tselem,
1749 void * /*arg*/)
1750{
1751 IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
1752
1753 /* just free drivers - stored as a list of F-Curves */
1756}
1757
1758static void refreshdrivers_animdata_fn(int /*event*/,
1759 TreeElement * /*te*/,
1760 TreeStoreElem *tselem,
1761 void * /*arg*/)
1762{
1763 IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
1764
1765 /* Loop over drivers, performing refresh
1766 * (i.e. check `graph_buttons.cc` and `rna_fcurve.cc` for details). */
1767 LISTBASE_FOREACH (FCurve *, fcu, &iat->adt->drivers) {
1768 fcu->flag &= ~FCURVE_DISABLED;
1769
1770 if (fcu->driver) {
1771 fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
1772 }
1773 }
1774}
1775
1777
1778/* -------------------------------------------------------------------- */
1781
1793
1796 "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
1797 0,
1798 "Make",
1799 "Create a local override of the selected linked data-blocks, and their hierarchy of "
1800 "dependencies"},
1802 "OVERRIDE_LIBRARY_RESET",
1803 0,
1804 "Reset",
1805 "Reset the selected local overrides to their linked references values"},
1807 "OVERRIDE_LIBRARY_CLEAR_SINGLE",
1808 0,
1809 "Clear",
1810 "Delete the selected local overrides and relink their usages to the linked data-blocks if "
1811 "possible, else reset them and mark them as non editable"},
1812 {0, nullptr, 0, nullptr, nullptr},
1813};
1814
1817 "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
1818 0,
1819 "Resync",
1820 "Rebuild the selected local overrides from their linked references, as well as their "
1821 "hierarchies of dependencies"},
1823 "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE",
1824 0,
1825 "Resync Enforce",
1826 "Rebuild the selected local overrides from their linked references, as well as their "
1827 "hierarchies of dependencies, enforcing these hierarchies to match the linked data (i.e. "
1828 "ignoring existing overrides on data-blocks pointer properties)"},
1831 "OVERRIDE_LIBRARY_DELETE_HIERARCHY",
1832 0,
1833 "Delete",
1834 "Delete the selected local overrides (including their hierarchies of override dependencies) "
1835 "and relink their usages to the linked data-blocks"},
1836 {0, nullptr, 0, nullptr, nullptr},
1837};
1838
1840{
1842 return false;
1843 }
1844 return true;
1845}
1846
1848{
1849 Scene *scene = CTX_data_scene(C);
1850 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1851
1852 /* check for invalid states */
1853 if (space_outliner == nullptr) {
1854 return OPERATOR_CANCELLED;
1855 }
1856
1857 const eOutlinerLibOpSelectionSet selection_set = static_cast<eOutlinerLibOpSelectionSet>(
1858 RNA_enum_get(op->ptr, "selection_set"));
1860 RNA_enum_get(op->ptr, "type"));
1861 switch (event) {
1863 OutlinerLibOverrideData override_data{};
1864 override_data.do_hierarchy = true;
1865 override_data.do_fully_editable = false;
1866
1868 C,
1869 op->reports,
1870 scene,
1871 space_outliner,
1872 [&](bContext *C,
1873 ReportList *reports,
1874 Scene * /*scene*/,
1875 TreeElement *te,
1876 TreeStoreElem *tsep,
1877 TreeStoreElem *tselem) {
1878 id_override_library_create_hierarchy_pre_process(
1879 C, &override_data, reports, te, tsep, tselem);
1880 },
1881 selection_set,
1882 true);
1883
1885
1886 ED_undo_push(C, "Overridden Data Hierarchy");
1887 break;
1888 }
1890 OutlinerLibOverrideData override_data{};
1892 C,
1893 op->reports,
1894 scene,
1895 space_outliner,
1896 [&](bContext *C,
1897 ReportList * /*reports*/,
1898 Scene * /*scene*/,
1899 TreeElement * /*te*/,
1900 TreeStoreElem * /*tsep*/,
1901 TreeStoreElem *tselem) { id_override_library_reset(C, &override_data, tselem); },
1902 selection_set,
1903 false);
1904 ED_undo_push(C, "Reset Overridden Data");
1905 break;
1906 }
1908 OutlinerLibOverrideData override_data{};
1909 override_data.do_hierarchy = false;
1910 override_data.do_fully_editable = false;
1911
1913 C,
1914 op->reports,
1915 scene,
1916 space_outliner,
1917 [&](bContext * /*C*/,
1918 ReportList *reports,
1919 Scene * /*scene*/,
1920 TreeElement * /*te*/,
1921 TreeStoreElem * /*tsep*/,
1922 TreeStoreElem *tselem) {
1923 id_override_library_clear_single(&override_data, reports, tselem);
1924 },
1925 selection_set,
1926 false);
1927
1929
1930 ED_undo_push(C, "Clear Overridden Data");
1931 break;
1932 }
1933
1935 OutlinerLibOverrideData override_data{};
1936 override_data.do_hierarchy = true;
1938 C,
1939 op->reports,
1940 scene,
1941 space_outliner,
1942 [&](bContext * /*C*/,
1943 ReportList * /*reports*/,
1944 Scene * /*scene*/,
1945 TreeElement * /*te*/,
1946 TreeStoreElem * /*tsep*/,
1947 TreeStoreElem *tselem) { id_override_library_resync(&override_data, tselem); },
1949 false);
1950
1952
1953 ED_undo_push(C, "Resync Overridden Data Hierarchy");
1954 break;
1955 }
1957 OutlinerLibOverrideData override_data{};
1958 override_data.do_hierarchy = true;
1959 override_data.do_resync_hierarchy_enforce = true;
1961 C,
1962 op->reports,
1963 scene,
1964 space_outliner,
1965 [&](bContext * /*C*/,
1966 ReportList * /*reports*/,
1967 Scene * /*scene*/,
1968 TreeElement * /*te*/,
1969 TreeStoreElem * /*tsep*/,
1970 TreeStoreElem *tselem) { id_override_library_resync(&override_data, tselem); },
1972 false);
1973
1975
1976 ED_undo_push(C, "Resync Overridden Data Hierarchy Enforce");
1977 break;
1978 }
1980 OutlinerLibOverrideData override_data{};
1981 override_data.do_hierarchy = true;
1983 C,
1984 op->reports,
1985 scene,
1986 space_outliner,
1987 [&](bContext * /*C*/,
1988 ReportList * /*reports*/,
1989 Scene * /*scene*/,
1990 TreeElement * /*te*/,
1991 TreeStoreElem * /*tsep*/,
1992 TreeStoreElem *tselem) {
1993 id_override_library_delete_hierarchy(&override_data, tselem);
1994 },
1996 false);
1997
1999
2000 ED_undo_push(C, "Delete Overridden Data Hierarchy");
2001 break;
2002 }
2003 default:
2004 /* Invalid - unhandled. */
2005 break;
2006 }
2007
2011
2012 return OPERATOR_FINISHED;
2013}
2014
2016{
2017 /* identifiers */
2018 ot->name = "Outliner Library Override Operation";
2019 ot->idname = "OUTLINER_OT_liboverride_operation";
2020 ot->description = "Create, reset or clear library override hierarchies";
2021
2022 /* callbacks */
2023 ot->invoke = WM_menu_invoke;
2026
2027 ot->flag = 0;
2028
2029 RNA_def_enum(ot->srna, "type", prop_liboverride_op_types, 0, "Library Override Operation", "");
2030 ot->prop = RNA_def_enum(ot->srna,
2031 "selection_set",
2033 0,
2034 "Selection Set",
2035 "Over which part of the tree items to apply the operation");
2036}
2037
2039{
2040 /* identifiers */
2041 ot->name = "Outliner Library Override Troubleshoot Operation";
2042 ot->idname = "OUTLINER_OT_liboverride_troubleshoot_operation";
2043 ot->description = "Advanced operations over library override to help fix broken hierarchies";
2044
2045 /* callbacks */
2046 ot->invoke = WM_menu_invoke;
2049
2050 ot->flag = 0;
2051
2052 ot->prop = RNA_def_enum(ot->srna,
2053 "type",
2055 0,
2056 "Library Override Troubleshoot Operation",
2057 "");
2058 RNA_def_enum(ot->srna,
2059 "selection_set",
2061 0,
2062 "Selection Set",
2063 "Over which part of the tree items to apply the operation");
2064}
2065
2067
2068/* -------------------------------------------------------------------- */
2071
2079
2085
2092
2093static void pchan_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, void * /*arg*/)
2094{
2095 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
2096
2097 if (event == OL_DOP_SELECT) {
2098 pchan->flag |= POSE_SELECTED;
2099 }
2100 else if (event == OL_DOP_DESELECT) {
2101 pchan->flag &= ~POSE_SELECTED;
2102 }
2103 else if (event == OL_DOP_HIDE) {
2104 pchan->drawflag |= PCHAN_DRAW_HIDDEN;
2105 pchan->flag &= ~POSE_SELECTED;
2106 }
2107 else if (event == OL_DOP_UNHIDE) {
2108 pchan->drawflag &= ~PCHAN_DRAW_HIDDEN;
2109 }
2110}
2111
2112static void bone_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, void * /*arg*/)
2113{
2114 Bone *bone = (Bone *)te->directdata;
2115
2116 if (event == OL_DOP_SELECT) {
2117 bone->flag |= BONE_SELECTED;
2118 }
2119 else if (event == OL_DOP_DESELECT) {
2120 bone->flag &= ~BONE_SELECTED;
2121 }
2122 else if (event == OL_DOP_HIDE) {
2123 bone->flag |= BONE_HIDDEN_P;
2124 bone->flag &= ~BONE_SELECTED;
2125 }
2126 else if (event == OL_DOP_UNHIDE) {
2127 bone->flag &= ~BONE_HIDDEN_P;
2128 }
2129}
2130
2131static void ebone_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, void * /*arg*/)
2132{
2133 EditBone *ebone = (EditBone *)te->directdata;
2134
2135 if (event == OL_DOP_SELECT) {
2136 ebone->flag |= BONE_SELECTED;
2137 }
2138 else if (event == OL_DOP_DESELECT) {
2139 ebone->flag &= ~BONE_SELECTED;
2140 }
2141 else if (event == OL_DOP_HIDE) {
2142 ebone->flag |= BONE_HIDDEN_A;
2144 }
2145 else if (event == OL_DOP_UNHIDE) {
2146 ebone->flag &= ~BONE_HIDDEN_A;
2147 }
2148}
2149
2150static void sequence_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, void *scene_ptr)
2151{
2153 Strip *strip = &te_strip->get_strip();
2154 Scene *scene = (Scene *)scene_ptr;
2155 Editing *ed = seq::editing_get(scene);
2156 if (BLI_findindex(ed->current_strips(), strip) != -1) {
2157 if (event == OL_DOP_SELECT) {
2158 vse::select_strip_single(scene, strip, true);
2159 }
2160 else if (event == OL_DOP_DESELECT) {
2161 strip->flag &= ~SELECT;
2162 }
2163 else if (event == OL_DOP_HIDE) {
2164 if (!(strip->flag & SEQ_MUTE)) {
2165 strip->flag |= SEQ_MUTE;
2166 seq::relations_invalidate_cache(scene, strip);
2167 }
2168 }
2169 else if (event == OL_DOP_UNHIDE) {
2170 if (strip->flag & SEQ_MUTE) {
2171 strip->flag &= ~SEQ_MUTE;
2172 seq::relations_invalidate_cache(scene, strip);
2173 }
2174 }
2175 }
2176}
2177
2178static void gpencil_layer_fn(int event,
2179 TreeElement *te,
2180 TreeStoreElem * /*tselem*/,
2181 void * /*arg*/)
2182{
2183 bGPDlayer *gpl = (bGPDlayer *)te->directdata;
2184
2185 if (event == OL_DOP_SELECT) {
2186 gpl->flag |= GP_LAYER_SELECT;
2187 }
2188 else if (event == OL_DOP_DESELECT) {
2189 gpl->flag &= ~GP_LAYER_SELECT;
2190 }
2191 else if (event == OL_DOP_HIDE) {
2192 gpl->flag |= GP_LAYER_HIDE;
2193 }
2194 else if (event == OL_DOP_UNHIDE) {
2195 gpl->flag &= ~GP_LAYER_HIDE;
2196 }
2197}
2198
2199static void grease_pencil_node_fn(int event,
2200 TreeElement *te,
2201 TreeStoreElem * /*tselem*/,
2202 void * /*arg*/)
2203{
2205
2206 if (event == OL_DOP_SELECT) {
2207 node.set_selected(true);
2208 }
2209 else if (event == OL_DOP_DESELECT) {
2210 node.set_selected(false);
2211 }
2212 else if (event == OL_DOP_HIDE) {
2213 node.set_visible(false);
2214 }
2215 else if (event == OL_DOP_UNHIDE) {
2216 node.set_visible(true);
2217 }
2218}
2219
2220static void data_select_linked_fn(int event,
2221 TreeElement *te,
2222 TreeStoreElem * /*tselem*/,
2223 void *C_v)
2224{
2226 if (!te_rna_struct) {
2227 return;
2228 }
2229
2230 if (event == OL_DOP_SELECT_LINKED) {
2231 const PointerRNA &ptr = te_rna_struct->get_pointer_rna();
2232 if (RNA_struct_is_ID(ptr.type)) {
2233 bContext *C = (bContext *)C_v;
2234 ID *id = static_cast<ID *>(ptr.data);
2235
2237 }
2238 }
2239}
2240
2241static void constraint_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, void *C_v)
2242{
2243 bContext *C = static_cast<bContext *>(C_v);
2244 Main *bmain = CTX_data_main(C);
2245 bConstraint *constraint = (bConstraint *)te->directdata;
2247
2248 if (event == OL_CONSTRAINTOP_ENABLE) {
2249 constraint->flag &= ~CONSTRAINT_OFF;
2250 object::constraint_update(bmain, ob);
2252 }
2253 else if (event == OL_CONSTRAINTOP_DISABLE) {
2254 constraint->flag |= CONSTRAINT_OFF;
2255 object::constraint_update(bmain, ob);
2257 }
2258 else if (event == OL_CONSTRAINTOP_DELETE) {
2259 ListBase *lb = nullptr;
2260
2261 if (TREESTORE(te->parent->parent)->type == TSE_POSE_CHANNEL) {
2262 lb = &((bPoseChannel *)te->parent->parent->directdata)->constraints;
2263 }
2264 else {
2265 lb = &ob->constraints;
2266 }
2267
2268 if (BKE_constraint_remove_ex(lb, ob, constraint)) {
2269 /* there's no active constraint now, so make sure this is the case */
2271
2272 /* Needed to set the flags on pose-bones correctly. */
2273 object::constraint_update(bmain, ob);
2274
2277 }
2278 }
2279}
2280
2285
2286static void modifier_fn(int event, TreeElement *te, TreeStoreElem * /*tselem*/, void *arg)
2287{
2288 ModifierFnArgs *data = static_cast<ModifierFnArgs *>(arg);
2289 bContext *C = data->C;
2290 Main *bmain = CTX_data_main(C);
2291 Scene *scene = CTX_data_scene(C);
2295
2296 if (event == OL_MODIFIER_OP_TOGVIS) {
2300 }
2301 else if (event == OL_MODIFIER_OP_TOGREN) {
2305 }
2306 else if (event == OL_MODIFIER_OP_DELETE) {
2307 object::modifier_remove(data->reports, bmain, scene, ob, md);
2310 }
2311 else if (event == OL_MODIFIER_OP_APPLY) {
2313 bmain, data->reports, depsgraph, scene, ob, md, object::MODIFIER_APPLY_DATA, false, false);
2318 }
2319}
2320
2322 SpaceOutliner *space_outliner,
2323 int type,
2324 int event,
2325 void (*operation_fn)(int, TreeElement *, TreeStoreElem *, void *),
2326 void *arg)
2327{
2328 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
2329 TreeStoreElem *tselem = TREESTORE(te);
2330 if (tselem->flag & TSE_SELECTED) {
2331 if (tselem->type == type) {
2332 operation_fn(event, te, tselem, arg);
2333 }
2334 }
2335 });
2336}
2337
2339 Main *bmain,
2340 Scene *scene,
2341 Object *object)
2342{
2343 if (object->id.tag & ID_TAG_INDIRECT) {
2345 reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", object->id.name + 2);
2346 BLI_assert((object->id.tag & ID_TAG_DOIT) == 0);
2347 }
2348 /* FIXME: This code checking object user-count won't work as expected if a same object belongs to
2349 * more than one collection in the scene. */
2350 if (ID_REAL_USERS(object) <= 1 && ID_EXTRA_USERS(object) == 0 &&
2351 BKE_library_ID_is_indirectly_used(bmain, object))
2352 {
2353 BKE_reportf(reports,
2355 "Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
2356 "one user",
2357 object->id.name + 2,
2358 scene->id.name + 2);
2359 BLI_assert((object->id.tag & ID_TAG_DOIT) == 0);
2360 }
2361
2362 object->id.tag |= ID_TAG_DOIT;
2363}
2364
2366 ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base)
2367{
2368 Object *object = base->object;
2369 BLI_assert(object != nullptr);
2370
2371 outliner_batch_delete_object_tag(reports, bmain, scene, object);
2372
2373 /* Even though the object itself may not be deletable, some of its children may still be
2374 * deletable. */
2375 for (Base *base_iter = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2376 base_iter != nullptr;
2377 base_iter = base_iter->next)
2378 {
2379 Object *parent_ob_iter;
2380 for (parent_ob_iter = base_iter->object->parent;
2381 (parent_ob_iter != nullptr && parent_ob_iter != object &&
2382 (parent_ob_iter->id.tag & ID_TAG_DOIT) == 0);
2383 parent_ob_iter = parent_ob_iter->parent)
2384 {
2385 /* pass */
2386 }
2387 if (parent_ob_iter != nullptr) {
2388 /* There is one or more parents to current iterated object that also need to be deleted,
2389 * process the parenting chain again to tag them as such.
2390 *
2391 * NOTE: Since objects that cannot be deleted are not tagged, the relevant 'parenting'
2392 * branches may be looped over more than once. Would not expect this to be a real issue in
2393 * practice though. */
2394 for (parent_ob_iter = base_iter->object;
2395 (parent_ob_iter != nullptr && parent_ob_iter != object &&
2396 (parent_ob_iter->id.tag & ID_TAG_DOIT) == 0);
2397 parent_ob_iter = parent_ob_iter->parent)
2398 {
2399 outliner_batch_delete_object_tag(reports, bmain, scene, parent_ob_iter);
2400 }
2401 }
2402 }
2403}
2404
2406 ReportList *reports,
2407 Scene *scene,
2408 Object *ob)
2409{
2410 if (ob->id.tag & ID_TAG_DOIT) {
2411 /* Object has already been processed and tagged for removal as part of another parenting
2412 * hierarchy. */
2413#ifndef NDEBUG
2414 ViewLayer *view_layer = CTX_data_view_layer(C);
2415 BKE_view_layer_synced_ensure(scene, view_layer);
2416 BLI_assert(BKE_view_layer_base_find(view_layer, ob) == nullptr);
2417#endif
2418 return;
2419 }
2420
2421 ViewLayer *view_layer = CTX_data_view_layer(C);
2422 Object *obedit = CTX_data_edit_object(C);
2423
2424 Base *base = BKE_view_layer_base_find(view_layer, ob);
2425
2426 if (base == nullptr) {
2427 return;
2428 }
2429
2430 /* Exit Edit mode if the active object or one of its children are being edited. */
2431 for (; obedit && (obedit != base->object); obedit = obedit->parent) {
2432 /* pass */
2433 }
2434 if (obedit == base->object) {
2436 }
2437
2438 Main *bmain = CTX_data_main(C);
2439 outliner_batch_delete_object_hierarchy_tag(reports, bmain, view_layer, scene, base);
2440}
2441
2443{
2444 LISTBASE_FOREACH (Object *, ob_iter, &bmain->objects) {
2445 if ((ob_iter->id.tag & ID_TAG_DOIT) == 0) {
2446 continue;
2447 }
2448
2449 BKE_scene_collections_object_remove(bmain, scene, ob_iter, false);
2450
2451 /* Check on all objects tagged for deletion, these that are still in use (e.g. in collections
2452 * from another scene) should not be deleted. They also need to be tagged for depsgraph update.
2453 */
2454 if (ob_iter->id.us != 0) {
2455 ob_iter->id.tag &= ~ID_TAG_DOIT;
2456 DEG_id_tag_update_ex(bmain, &ob_iter->id, ID_RECALC_BASE_FLAGS);
2457 }
2458 }
2459
2461}
2462
2464
2465/* -------------------------------------------------------------------- */
2468
2469enum {
2475};
2476
2478 {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""},
2479 {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""},
2480 {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""},
2481 {OL_OP_REMAP,
2482 "REMAP",
2483 0,
2484 "Remap Users",
2485 "Make all users of selected data-blocks to use instead a new chosen one"},
2486 {OL_OP_RENAME, "RENAME", 0, "Rename", ""},
2487 {0, nullptr, 0, nullptr, nullptr},
2488};
2489
2491{
2492 Main *bmain = CTX_data_main(C);
2493 Scene *scene = CTX_data_scene(C);
2494 wmWindow *win = CTX_wm_window(C);
2495 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2496 int event;
2497 const char *str = nullptr;
2498 bool selection_changed = false;
2499
2500 /* check for invalid states */
2501 if (space_outliner == nullptr) {
2502 return OPERATOR_CANCELLED;
2503 }
2504
2505 event = RNA_enum_get(op->ptr, "type");
2506
2507 switch (event) {
2508 case OL_OP_SELECT: {
2509 Scene *sce = scene; /* To be able to delete, scenes are set... */
2511 C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn);
2512 /* FIXME: This is most certainly broken, maybe check should rather be
2513 * `if (CTX_data_scene(C) != scene)` ? */
2514 if (scene != sce) {
2515 WM_window_set_active_scene(bmain, C, win, sce);
2516 }
2517
2518 str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select Objects");
2519 selection_changed = true;
2520 break;
2521 }
2523 Scene *sce = scene; /* To be able to delete, scenes are set... */
2525 op->reports,
2526 scene,
2527 space_outliner,
2528 &space_outliner->tree,
2530 false);
2531 /* FIXME: This is most certainly broken, maybe check should rather be
2532 * `if (CTX_data_scene(C) != scene)` ? */
2533 if (scene != sce) {
2534 WM_window_set_active_scene(bmain, C, win, sce);
2535 }
2536 str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select Object Hierarchy");
2537 selection_changed = true;
2538 break;
2539 }
2540 case OL_OP_DESELECT:
2542 C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn);
2543 str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Deselect Objects");
2544 selection_changed = true;
2545 break;
2546 case OL_OP_REMAP:
2547 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn);
2548 /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
2549 * trick does not work here). */
2550 break;
2551 case OL_OP_RENAME:
2553 C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn);
2554 str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Object");
2555 break;
2556 default:
2558 return OPERATOR_CANCELLED;
2559 }
2560
2561 if (selection_changed) {
2565 }
2566
2567 if (str != nullptr) {
2568 ED_undo_push(C, str);
2569 }
2570
2571 return OPERATOR_FINISHED;
2572}
2573
2575{
2576 /* identifiers */
2577 ot->name = "Outliner Object Operation";
2578 ot->idname = "OUTLINER_OT_object_operation";
2579
2580 /* callbacks */
2581 ot->invoke = WM_menu_invoke;
2584
2585 ot->flag = 0;
2586
2587 ot->prop = RNA_def_enum(ot->srna, "type", prop_object_op_types, 0, "Object Operation", "");
2588}
2589
2591
2592/* -------------------------------------------------------------------- */
2595
2596using OutlinerDeleteFn = void (*)(bContext *C, ReportList *reports, Scene *scene, Object *ob);
2597
2603
2605 ReportList *reports,
2606 Scene *scene,
2607 const Set<Object *> &objects_to_delete,
2608 OutlinerDeleteFn delete_fn)
2609{
2610 for (Object *ob : objects_to_delete) {
2611 delete_fn(C, reports, scene, ob);
2612 }
2613}
2614
2616{
2617 ObjectEditData *data = static_cast<ObjectEditData *>(customdata);
2618 TreeStoreElem *tselem = TREESTORE(te);
2619
2621 return TRAVERSE_CONTINUE;
2622 }
2623
2624 if ((tselem->type != TSE_SOME_ID) || (tselem->id == nullptr) || (GS(tselem->id->name) != ID_OB))
2625 {
2626 return TRAVERSE_SKIP_CHILDS;
2627 }
2628
2629 /* Do not allow to delete children objects of an override collection. */
2630 TreeElement *te_parent = te->parent;
2631 if (te_parent != nullptr && outliner_is_collection_tree_element(te_parent)) {
2632 TreeStoreElem *tselem_parent = TREESTORE(te_parent);
2633 ID *id_parent = tselem_parent->id;
2634 /* It's not possible to remove an object from an overridden collection (and potentially scene,
2635 * through the master collection). */
2636 if (ELEM(GS(id_parent->name), ID_GR, ID_SCE)) {
2637 if (ID_IS_OVERRIDE_LIBRARY_REAL(id_parent)) {
2638 return TRAVERSE_SKIP_CHILDS;
2639 }
2640 }
2641 }
2642
2643 ID *id = tselem->id;
2644
2647 if (!(data->is_liboverride_hierarchy_root_allowed || data->is_liboverride_allowed)) {
2648 return TRAVERSE_SKIP_CHILDS;
2649 }
2650 }
2651 else {
2652 if (!data->is_liboverride_allowed) {
2653 return TRAVERSE_SKIP_CHILDS;
2654 }
2655 }
2656 }
2657
2658 data->objects_set.add(reinterpret_cast<Object *>(id));
2659
2660 return TRAVERSE_CONTINUE;
2661}
2662
2664{
2665 Main *bmain = CTX_data_main(C);
2666 Scene *scene = CTX_data_scene(C);
2667 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2668 wmMsgBus *mbus = CTX_wm_message_bus(C);
2669 ViewLayer *view_layer = CTX_data_view_layer(C);
2670 BKE_view_layer_synced_ensure(scene, view_layer);
2671 const Base *basact_prev = BKE_view_layer_active_base_get(view_layer);
2672
2673 const bool delete_hierarchy = RNA_boolean_get(op->ptr, "hierarchy");
2674
2675 /* Get selected objects skipping duplicates to prevent deleting objects linked to multiple
2676 * collections twice */
2677 ObjectEditData object_delete_data = {};
2678 object_delete_data.is_liboverride_allowed = false;
2679 object_delete_data.is_liboverride_hierarchy_root_allowed = delete_hierarchy;
2680 outliner_tree_traverse(space_outliner,
2681 &space_outliner->tree,
2682 0,
2685 &object_delete_data);
2686
2687 if (delete_hierarchy) {
2688 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
2689
2690 BKE_view_layer_synced_ensure(scene, view_layer);
2691
2692 /* #object_batch_delete_hierarchy_fn callback will only remove objects from collections and tag
2693 * them for deletion. */
2695 op->reports,
2696 scene,
2697 object_delete_data.objects_set,
2699
2701 }
2702 else {
2704 C, op->reports, scene, object_delete_data.objects_set, outliner_object_delete_fn);
2705 }
2706
2707 outliner_collection_delete(C, bmain, scene, op->reports, delete_hierarchy);
2708
2709 /* Tree management normally happens from draw_outliner(), but when
2710 * you're clicking too fast on Delete object from context menu in
2711 * outliner several mouse events can be handled in one cycle without
2712 * handling notifiers/redraw which leads to deleting the same object twice.
2713 * cleanup tree here to prevent such cases. */
2714 outliner_cleanup_tree(space_outliner);
2715
2718
2719 BKE_view_layer_synced_ensure(scene, view_layer);
2720 if (basact_prev != BKE_view_layer_active_base_get(view_layer)) {
2722 WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
2723 }
2724
2726
2731
2732 return OPERATOR_FINISHED;
2733}
2734
2736{
2737 /* identifiers */
2738 ot->name = "Delete";
2739 ot->idname = "OUTLINER_OT_delete";
2740 ot->description = "Delete selected objects and collections";
2741
2742 /* callbacks */
2743 ot->exec = outliner_delete_exec;
2745
2746 /* flags */
2747 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2748
2749 /* properties */
2751 ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections");
2753}
2754
2756
2757/* -------------------------------------------------------------------- */
2760
2779
2780/* TODO: implement support for changing the ID-block used. */
2782 {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
2783 {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
2784 {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
2785 {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
2787 "REMAP",
2788 0,
2789 "Remap Users",
2790 "Make all users of selected data-blocks to use instead current (clicked) one"},
2792 {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
2793 {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
2796 "ADD_FAKE",
2797 0,
2798 "Add Fake User",
2799 "Ensure data-block gets saved even if it isn't in use (e.g. for motion and material "
2800 "libraries)"},
2801 {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""},
2802 {OUTLINER_IDOP_RENAME, "RENAME", 0, "Rename", ""},
2803 {OUTLINER_IDOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
2804 {0, nullptr, 0, nullptr, nullptr},
2805};
2806
2808 PointerRNA * /*ptr*/,
2809 PropertyRNA * /*prop*/,
2810 const int enum_value)
2811{
2813 return false;
2814 }
2815
2816 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2817 TreeElement *te = get_target_element(space_outliner);
2818 TreeStoreElem *tselem = TREESTORE(te);
2819 if (!TSE_IS_REAL_ID(tselem)) {
2820 return false;
2821 }
2822
2823 switch (enum_value) {
2825 if (ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
2826 return true;
2827 }
2828 /* TODO(dalai): enable in the few cases where this can be supported
2829 * (i.e., when we have a valid parent for the tselem). */
2830 return false;
2831 }
2832
2833 return true;
2834}
2835
2837 PointerRNA *ptr,
2838 PropertyRNA *prop,
2839 bool *r_free)
2840{
2841 EnumPropertyItem *items = nullptr;
2842 int totitem = 0;
2843
2844 if ((C == nullptr) || (ED_operator_outliner_active(C) == false)) {
2845 return prop_id_op_types;
2846 }
2847 for (const EnumPropertyItem *it = prop_id_op_types; it->identifier != nullptr; it++) {
2848 if (!outliner_id_operation_item_poll(C, ptr, prop, it->value)) {
2849 continue;
2850 }
2851 RNA_enum_item_add(&items, &totitem, it);
2852 }
2853 RNA_enum_item_end(&items, &totitem);
2854 *r_free = true;
2855
2856 return items;
2857}
2858
2860{
2861 Main *bmain = CTX_data_main(C);
2863 Scene *scene = CTX_data_scene(C);
2864 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2865 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2866
2867 /* check for invalid states */
2868 if (space_outliner == nullptr) {
2869 return OPERATOR_CANCELLED;
2870 }
2871
2872 TreeElement *te = get_target_element(space_outliner);
2873 get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
2874
2876 switch (event) {
2877 case OUTLINER_IDOP_UNLINK: {
2878 /* unlink datablock from its parent */
2879 if (objectlevel) {
2880 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_object_fn);
2881
2883 ED_undo_push(C, "Unlink Object");
2884 break;
2885 }
2886
2887 switch (idlevel) {
2888 case ID_AC:
2889 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_action_fn);
2890
2892 ED_undo_push(C, "Unlink action");
2893 break;
2894 case ID_MA:
2895 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_material_fn);
2896
2898 ED_undo_push(C, "Unlink material");
2899 break;
2900 case ID_TE:
2901 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_texture_fn);
2902
2904 ED_undo_push(C, "Unlink texture");
2905 break;
2906 case ID_WO:
2907 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, unlink_world_fn);
2908
2910 ED_undo_push(C, "Unlink world");
2911 break;
2912 case ID_GR:
2914 C, op->reports, scene, space_outliner, unlink_collection_fn);
2915
2917 ED_undo_push(C, "Unlink Collection");
2918 break;
2919 default:
2920 BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
2921 break;
2922 }
2923 break;
2924 }
2925 case OUTLINER_IDOP_LOCAL: {
2926 /* make local */
2927 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_local_fn);
2928 ED_undo_push(C, "Localized Data");
2929 break;
2930 }
2931 case OUTLINER_IDOP_SINGLE: {
2932 /* make single user */
2933 switch (idlevel) {
2934 case ID_AC:
2936 C, op->reports, scene, space_outliner, singleuser_action_fn);
2937
2939 ED_undo_push(C, "Single-User Action");
2940 break;
2941
2942 case ID_WO:
2944 C, op->reports, scene, space_outliner, singleuser_world_fn);
2945
2947 ED_undo_push(C, "Single-User World");
2948 break;
2949
2950 default:
2951 BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
2952 break;
2953 }
2954 break;
2955 }
2956 case OUTLINER_IDOP_DELETE: {
2957 if (idlevel > 0) {
2958 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
2959 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_tag_fn);
2962 ED_undo_push(C, "Delete");
2963 }
2964 break;
2965 }
2966 case OUTLINER_IDOP_REMAP: {
2967 if (idlevel > 0 || objectlevel) {
2968 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_remap_fn);
2969 /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
2970 * trick does not work here). */
2971 }
2972 break;
2973 }
2974 case OUTLINER_IDOP_COPY: {
2975 wm->op_undo_depth++;
2977 C, "OUTLINER_OT_id_copy", wm::OpCallContext::InvokeDefault, nullptr, nullptr);
2978 wm->op_undo_depth--;
2979 /* No need for undo, this operation does not change anything... */
2980 break;
2981 }
2982 case OUTLINER_IDOP_PASTE: {
2983 wm->op_undo_depth++;
2985 C, "OUTLINER_OT_id_paste", wm::OpCallContext::InvokeDefault, nullptr, nullptr);
2986 wm->op_undo_depth--;
2988 ED_undo_push(C, "Paste");
2989 break;
2990 }
2992 /* set fake user */
2993 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_fake_user_set_fn);
2994
2996 ED_undo_push(C, "Add Fake User");
2997 break;
2998 }
3000 /* clear fake user */
3001 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_fake_user_clear_fn);
3002
3004 ED_undo_push(C, "Clear Fake User");
3005 break;
3006 }
3007 case OUTLINER_IDOP_RENAME: {
3008 /* rename */
3009 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, item_rename_fn);
3010
3012 ED_undo_push(C, "Rename");
3013 break;
3014 }
3016 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_select_linked_fn);
3018 ED_undo_push(C, "Select");
3019 break;
3020
3021 default:
3022 /* Invalid - unhandled. */
3023 break;
3024 }
3025
3027
3028 /* wrong notifier still... */
3030
3031 /* XXX: this is just so that outliner is always up to date. */
3033
3034 return OPERATOR_FINISHED;
3035}
3036
3038{
3039 /* identifiers */
3040 ot->name = "Outliner ID Data Operation";
3041 ot->idname = "OUTLINER_OT_id_operation";
3042 ot->description = "General data-block management operations";
3043
3044 /* callbacks */
3045 ot->invoke = WM_menu_invoke;
3048
3049 ot->flag = 0;
3050
3051 ot->prop = RNA_def_enum(ot->srna, "type", prop_id_op_types, 0, "ID Data Operation", "");
3053}
3054
3056
3057/* -------------------------------------------------------------------- */
3060
3068
3070 {OL_LIB_DELETE, "DELETE", ICON_X, "Delete", "Delete this library and all its items"},
3072 "RELOCATE",
3073 0,
3074 "Relocate",
3075 "Select a new path for this library, and reload all its data"},
3076 {OL_LIB_RELOAD, "RELOAD", ICON_FILE_REFRESH, "Reload", "Reload all data from this library"},
3077 {0, nullptr, 0, nullptr, nullptr},
3078};
3079
3081{
3082 Main *bmain = CTX_data_main(C);
3083 Scene *scene = CTX_data_scene(C);
3084 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3085
3086 /* check for invalid states */
3087 if (space_outliner == nullptr) {
3088 return OPERATOR_CANCELLED;
3089 }
3090
3092 switch (event) {
3093 case OL_LIB_DELETE: {
3094 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
3095 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, id_delete_tag_fn);
3097 ED_undo_push(C, "Delete Library");
3098 break;
3099 }
3100 case OL_LIB_RELOCATE: {
3101 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_relocate_fn);
3102 /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
3103 * trick does not work here). */
3104 break;
3105 }
3106 case OL_LIB_RELOAD: {
3107 outliner_do_libdata_operation(C, op->reports, scene, space_outliner, lib_reload_fn);
3108 /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
3109 * trick does not work here). */
3110 break;
3111 }
3112 default:
3113 /* invalid - unhandled */
3114 break;
3115 }
3116
3118
3119 /* wrong notifier still... */
3121
3122 /* XXX: this is just so that outliner is always up to date */
3124
3125 return OPERATOR_FINISHED;
3126}
3127
3129{
3130 /* identifiers */
3131 ot->name = "Outliner Library Operation";
3132 ot->idname = "OUTLINER_OT_lib_operation";
3133
3134 /* callbacks */
3135 ot->invoke = WM_menu_invoke;
3138
3139 ot->prop = RNA_def_enum(
3140 ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", "");
3141}
3142
3144
3145/* -------------------------------------------------------------------- */
3148
3150 SpaceOutliner *space_outliner,
3151 int type,
3152 ID *newid,
3153 void (*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
3154{
3155 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
3156 TreeStoreElem *tselem = TREESTORE(te);
3157 if (tselem->flag & TSE_SELECTED) {
3158 if (tselem->type == type) {
3159 TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : nullptr;
3160 operation_fn(te, tselem, tsep, newid);
3161 }
3162 }
3163 });
3164}
3165
3166static void actionset_id_fn(TreeElement * /*te*/,
3167 TreeStoreElem *tselem,
3168 TreeStoreElem *tsep,
3169 ID *actId)
3170{
3171 bAction *act = (bAction *)actId;
3172
3173 if (tselem->type == TSE_ANIM_DATA) {
3174 /* "animation" entries - action is child of this */
3175 BKE_animdata_set_action(nullptr, tselem->id, act);
3176 }
3177 /* TODO: if any other "expander" channels which own actions need to support this menu,
3178 * add: tselem->type = ...
3179 */
3180 else if (tsep && (tsep->type == TSE_ANIM_DATA)) {
3181 /* "animation" entries case again */
3182 BKE_animdata_set_action(nullptr, tsep->id, act);
3183 }
3184 /* TODO: other cases not supported yet. */
3185}
3186
3188{
3189 Main *bmain = CTX_data_main(C);
3190 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3191 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
3192 bAction *act;
3193
3194 TreeElement *te = get_target_element(space_outliner);
3195 get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
3196
3197 /* get action to use */
3198 act = static_cast<bAction *>(BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action")));
3199
3200 if (act == nullptr) {
3201 BKE_report(op->reports, RPT_ERROR, "No valid action to add");
3202 return OPERATOR_CANCELLED;
3203 }
3205 /* Hopefully in this case (i.e. library of userless actions),
3206 * the user knows what they're doing. */
3207 BKE_reportf(op->reports,
3209 "Action '%s' does not specify what data-blocks it can be used on "
3210 "(try setting the 'ID Root Type' setting from the data-blocks editor "
3211 "for this action to avoid future problems)",
3212 act->id.name + 2);
3213 }
3214
3215 /* perform action if valid channel */
3216 if (datalevel == TSE_ANIM_DATA) {
3217 outliner_do_id_set_operation(space_outliner, datalevel, (ID *)act, actionset_id_fn);
3218 }
3219 else if (idlevel == ID_AC) {
3220 outliner_do_id_set_operation(space_outliner, idlevel, (ID *)act, actionset_id_fn);
3221 }
3222 else {
3223 return OPERATOR_CANCELLED;
3224 }
3225
3226 /* set notifier that things have changed */
3229 ED_undo_push(C, "Set action");
3230
3231 /* done */
3232 return OPERATOR_FINISHED;
3233}
3234
3236{
3237 PropertyRNA *prop;
3238
3239 /* identifiers */
3240 ot->name = "Outliner Set Action";
3241 ot->idname = "OUTLINER_OT_action_set";
3242 ot->description = "Change the active action used";
3243
3244 /* API callbacks. */
3245 ot->invoke = WM_enum_search_invoke;
3248
3249 /* flags */
3250 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3251
3252 /* props */
3253 /* TODO: this would be nicer as an ID-pointer... */
3254 prop = RNA_def_enum(ot->srna, "action", rna_enum_dummy_NULL_items, 0, "Action", "");
3257 ot->prop = prop;
3258}
3259
3261
3262/* -------------------------------------------------------------------- */
3265
3277
3280 "CLEAR_ANIMDATA",
3281 0,
3282 "Clear Animation Data",
3283 "Remove this animation data container"},
3284 {OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
3285 {OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
3286 {OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
3287 {OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
3288 {0, nullptr, 0, nullptr, nullptr},
3289};
3290
3292{
3294 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3295 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
3296 TreeElement *te = get_target_element(space_outliner);
3297 get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
3298
3299 if (datalevel != TSE_ANIM_DATA) {
3300 return OPERATOR_CANCELLED;
3301 }
3302
3303 /* perform the core operation */
3305 switch (event) {
3307 /* Remove Animation Data - this may remove the active action, in some cases... */
3308 outliner_do_data_operation(space_outliner, datalevel, event, clear_animdata_fn, nullptr);
3309
3311 ED_undo_push(C, "Clear Animation Data");
3312 break;
3313
3315 /* delegate once again... */
3316 wm->op_undo_depth++;
3318 C, "OUTLINER_OT_action_set", wm::OpCallContext::InvokeRegionWin, nullptr, nullptr);
3319 wm->op_undo_depth--;
3320 ED_undo_push(C, "Set active action");
3321 break;
3322
3324 /* clear active action - using standard rules */
3325 outliner_do_data_operation(space_outliner, datalevel, event, unlinkact_animdata_fn, nullptr);
3326
3328 ED_undo_push(C, "Unlink action");
3329 break;
3330
3333 space_outliner, datalevel, event, refreshdrivers_animdata_fn, nullptr);
3334
3336 // ED_undo_push(C, "Refresh Drivers"); /* No undo needed - shouldn't have any impact? */
3337 break;
3338
3341 space_outliner, datalevel, event, cleardrivers_animdata_fn, nullptr);
3342
3344 ED_undo_push(C, "Clear Drivers");
3345 break;
3346
3347 default: /* Invalid. */
3348 break;
3349 }
3350
3351 /* update dependencies */
3353
3354 return OPERATOR_FINISHED;
3355}
3356
3358{
3359 /* identifiers */
3360 ot->name = "Outliner Animation Data Operation";
3361 ot->idname = "OUTLINER_OT_animdata_operation";
3362
3363 /* callbacks */
3364 ot->invoke = WM_menu_invoke;
3367
3368 ot->flag = 0;
3369
3370 ot->prop = RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", "");
3371}
3372
3374
3375/* -------------------------------------------------------------------- */
3378
3380 {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_HIDE_OFF, "Enable", ""},
3381 {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_HIDE_ON, "Disable", ""},
3382 {OL_CONSTRAINTOP_DELETE, "DELETE", ICON_X, "Delete", ""},
3383 {0, nullptr, 0, nullptr, nullptr},
3384};
3385
3387{
3388 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3390
3391 outliner_do_data_operation(space_outliner, TSE_CONSTRAINT, event, constraint_fn, C);
3392
3393 if (event == OL_CONSTRAINTOP_DELETE) {
3394 outliner_cleanup_tree(space_outliner);
3395 }
3396
3397 ED_undo_push(C, "Constraint operation");
3398
3399 return OPERATOR_FINISHED;
3400}
3401
3403{
3404 /* identifiers */
3405 ot->name = "Outliner Constraint Operation";
3406 ot->idname = "OUTLINER_OT_constraint_operation";
3407
3408 /* callbacks */
3409 ot->invoke = WM_menu_invoke;
3412
3413 ot->flag = 0;
3414
3415 ot->prop = RNA_def_enum(
3416 ot->srna, "type", prop_constraint_op_types, 0, "Constraint Operation", "");
3417}
3418
3420
3421/* -------------------------------------------------------------------- */
3424
3426 {OL_MODIFIER_OP_APPLY, "APPLY", ICON_CHECKMARK, "Apply", ""},
3427 {OL_MODIFIER_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
3429 {OL_MODIFIER_OP_TOGVIS, "TOGVIS", ICON_RESTRICT_VIEW_OFF, "Toggle Viewport Use", ""},
3430 {OL_MODIFIER_OP_TOGREN, "TOGREN", ICON_RESTRICT_RENDER_OFF, "Toggle Render Use", ""},
3431 {0, nullptr, 0, nullptr, nullptr},
3432};
3433
3435{
3436 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3438
3439 ModifierFnArgs args{};
3440 args.C = C;
3441 args.reports = op->reports;
3442
3443 outliner_do_data_operation(space_outliner, TSE_MODIFIER, event, modifier_fn, &args);
3444
3446 outliner_cleanup_tree(space_outliner);
3447 }
3448
3449 ED_undo_push(C, "Modifier operation");
3450
3451 return OPERATOR_FINISHED;
3452}
3453
3455{
3456 /* identifiers */
3457 ot->name = "Outliner Modifier Operation";
3458 ot->idname = "OUTLINER_OT_modifier_operation";
3459
3460 /* callbacks */
3461 ot->invoke = WM_menu_invoke;
3464
3465 ot->flag = 0;
3466
3467 ot->prop = RNA_def_enum(ot->srna, "type", prop_modifier_op_types, 0, "Modifier Operation", "");
3468}
3469
3471
3472/* -------------------------------------------------------------------- */
3475
3477{
3479 return false;
3480 }
3481 const SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3482 const TreeElement *te = get_target_element(space_outliner);
3483
3484 if (te == nullptr) {
3485 return false;
3486 }
3487
3488 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
3489 get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
3490 return ELEM(
3492}
3493
3495{
3496 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3497 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
3498 TreeElement *te = get_target_element(space_outliner);
3499 get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
3500
3502 switch (datalevel) {
3503 case TSE_POSE_CHANNEL: {
3504 outliner_do_data_operation(space_outliner, datalevel, event, pchan_fn, nullptr);
3506 ED_undo_push(C, "PoseChannel operation");
3507
3508 break;
3509 }
3510 case TSE_BONE: {
3511 outliner_do_data_operation(space_outliner, datalevel, event, bone_fn, nullptr);
3513 ED_undo_push(C, "Bone operation");
3514
3515 break;
3516 }
3517 case TSE_EBONE: {
3518 outliner_do_data_operation(space_outliner, datalevel, event, ebone_fn, nullptr);
3520 ED_undo_push(C, "EditBone operation");
3521
3522 break;
3523 }
3524 case TSE_STRIP: {
3525 Scene *sequencer_scene = CTX_data_sequencer_scene(C);
3526 outliner_do_data_operation(space_outliner, datalevel, event, sequence_fn, sequencer_scene);
3528 ED_undo_push(C, "Sequencer operation");
3529
3530 break;
3531 }
3532 case TSE_GP_LAYER: {
3533 outliner_do_data_operation(space_outliner, datalevel, event, gpencil_layer_fn, nullptr);
3535 ED_undo_push(C, "Grease Pencil Layer operation");
3536
3537 break;
3538 }
3540 outliner_do_data_operation(space_outliner, datalevel, event, grease_pencil_node_fn, nullptr);
3542 ED_undo_push(C, "Grease Pencil Node operation");
3543 break;
3544 }
3545 case TSE_RNA_STRUCT:
3546 if (event == OL_DOP_SELECT_LINKED) {
3547 outliner_do_data_operation(space_outliner, datalevel, event, data_select_linked_fn, C);
3548 }
3549
3550 break;
3551
3552 default:
3553 BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
3554 break;
3555 }
3556
3557 return OPERATOR_FINISHED;
3558}
3559
3560/* Dynamically populate an enum of Keying Sets */
3562 PointerRNA * /*ptr*/,
3563 PropertyRNA * /*prop*/,
3564 bool * /*r_free*/)
3565{
3566 /* Check for invalid states. */
3567 if (C == nullptr) {
3569 }
3570
3571 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3572 if (space_outliner == nullptr) {
3574 }
3575
3576 TreeElement *te = get_target_element(space_outliner);
3577 if (te == nullptr) {
3579 }
3580
3581 TreeStoreElem *tselem = TREESTORE(te);
3582
3583 static const EnumPropertyItem optype_sel_and_hide[] = {
3584 {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
3585 {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
3586 {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
3587 {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
3588 {0, nullptr, 0, nullptr, nullptr}};
3589
3590 static const EnumPropertyItem optype_sel_linked[] = {
3591 {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
3592 {0, nullptr, 0, nullptr, nullptr}};
3593
3594 if (tselem->type == TSE_RNA_STRUCT) {
3595 return optype_sel_linked;
3596 }
3597
3598 return optype_sel_and_hide;
3599}
3600
3602{
3603 /* identifiers */
3604 ot->name = "Outliner Data Operation";
3605 ot->idname = "OUTLINER_OT_data_operation";
3606
3607 /* callbacks */
3608 ot->invoke = WM_menu_invoke;
3611
3612 ot->flag = 0;
3613
3614 ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_dummy_DEFAULT_items, 0, "Data Operation", "");
3616}
3617
3619
3620/* -------------------------------------------------------------------- */
3623
3625{
3626 wmOperatorType *ot = WM_operatortype_find(opname, false);
3627 uiPopupMenu *pup = UI_popup_menu_begin(C, WM_operatortype_name(ot, nullptr).c_str(), ICON_NONE);
3628 uiLayout *layout = UI_popup_menu_layout(pup);
3629
3630 /* Set this so the default execution context is the same as sub-menus. */
3632
3633 if (WM_operator_poll(C, ot)) {
3634 layout->op_enum(ot->idname, RNA_property_identifier(ot->prop));
3635
3636 layout->separator();
3637 }
3638
3639 layout->menu_contents("OUTLINER_MT_context_menu");
3640
3641 UI_popup_menu_end(C, pup);
3642
3643 return OPERATOR_INTERFACE;
3644}
3645
3647 ARegion *region,
3648 SpaceOutliner *space_outliner,
3649 TreeElement *te)
3650{
3651 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
3652 TreeStoreElem *tselem = TREESTORE(te);
3653
3654 int select_flag = OL_ITEM_ACTIVATE | OL_ITEM_SELECT;
3655 if (tselem->flag & TSE_SELECTED) {
3656 select_flag |= OL_ITEM_EXTEND;
3657 }
3658
3659 outliner_item_select(C, space_outliner, te, select_flag);
3660
3661 /* Only redraw, don't rebuild here because TreeElement pointers will
3662 * become invalid and operations will crash. */
3665
3666 get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
3667
3668 if (scenelevel) {
3669 return outliner_operator_menu(C, "OUTLINER_OT_scene_operation");
3670 }
3671 if (objectlevel) {
3673 return OPERATOR_FINISHED;
3674 }
3675 if (idlevel) {
3676 switch (idlevel) {
3677 case ID_GR:
3678 WM_menu_name_call(C, "OUTLINER_MT_collection", wm::OpCallContext::InvokeRegionWin);
3679 return OPERATOR_FINISHED;
3680 break;
3681 case ID_LI:
3682 return outliner_operator_menu(C, "OUTLINER_OT_lib_operation");
3683 break;
3684 default:
3685 return outliner_operator_menu(C, "OUTLINER_OT_id_operation");
3686 break;
3687 }
3688 }
3689 else if (datalevel) {
3690 if (datalevel == TSE_ANIM_DATA) {
3691 return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation");
3692 }
3693 if (datalevel == TSE_DRIVER_BASE) {
3694 /* do nothing... no special ops needed yet */
3695 return OPERATOR_CANCELLED;
3696 }
3697 if (datalevel == TSE_LAYER_COLLECTION) {
3698 WM_menu_name_call(C, "OUTLINER_MT_collection", wm::OpCallContext::InvokeRegionWin);
3699 return OPERATOR_FINISHED;
3700 }
3702 WM_menu_name_call(C, "OUTLINER_MT_collection_new", wm::OpCallContext::InvokeRegionWin);
3703 return OPERATOR_FINISHED;
3704 }
3705 if (datalevel == TSE_ID_BASE) {
3706 /* do nothing... there are no ops needed here yet */
3707 return OPERATOR_CANCELLED;
3708 }
3709 if (datalevel == TSE_CONSTRAINT) {
3710 return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
3711 }
3712 if (datalevel == TSE_MODIFIER) {
3713 return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation");
3714 }
3715 return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
3716 }
3717
3718 return OPERATOR_CANCELLED;
3719}
3720
3722 wmOperator * /*op*/,
3723 const wmEvent *event)
3724{
3725 ARegion *region = CTX_wm_region(C);
3726 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
3728 float view_mval[2];
3729
3730 if (but) {
3732 }
3733
3735 &region->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
3736
3738 space_outliner, &space_outliner->tree, view_mval[1]);
3739 if (!hovered_te) {
3740 /* Let this fall through to 'OUTLINER_MT_context_menu'. */
3741 return OPERATOR_PASS_THROUGH;
3742 }
3743
3744 return do_outliner_operation_event(C, region, space_outliner, hovered_te);
3745}
3746
3748{
3749 ot->name = "Context Menu";
3750 ot->idname = "OUTLINER_OT_operation";
3751 ot->description = "Context menu for item operations";
3752
3753 ot->invoke = outliner_operation_invoke;
3754
3756}
3757
3759
3760} // namespace blender::ed::outliner
Functions for backward compatibility with the legacy Action API.
bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
Definition anim_data.cc:125
void BKE_animdata_free(ID *id, bool do_id_user)
Definition anim_data.cc:188
bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, bool free_us)
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_END
#define FOREACH_SCENE_COLLECTION_END
bool BKE_scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, bool free_us)
bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child)
bool BKE_collection_has_object(Collection *collection, const Object *ob)
#define FOREACH_SCENE_COLLECTION_BEGIN(scene, _instance)
#define FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(_collection, _object)
bool BKE_constraint_remove_ex(ListBase *list, struct Object *ob, struct bConstraint *con)
void BKE_constraints_active_set(ListBase *list, struct bConstraint *con)
ReportList * CTX_wm_reports(const bContext *C)
SpaceOutliner * CTX_wm_space_outliner(const bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
Scene * CTX_data_sequencer_scene(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_fcurves_free(ListBase *list)
Low-level operations for grease pencil.
bool BKE_idtype_idcode_is_linkable(short idcode)
Definition idtype.cc:197
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
void BKE_id_newptr_and_tag_clear(ID *id)
Definition lib_id.cc:412
void BKE_id_delete(Main *bmain, void *idv) ATTR_NONNULL()
@ LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR
bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop)
Definition lib_id.cc:1068
void size_t BKE_id_multi_tagged_delete(Main *bmain) ATTR_NONNULL()
void id_fake_user_set(ID *id)
Definition lib_id.cc:396
void BKE_main_id_newptr_and_tag_clear(Main *bmain)
Definition lib_id.cc:2001
bool BKE_lib_id_make_local(Main *bmain, ID *id, int flags)
Definition lib_id.cc:598
void id_fake_user_clear(ID *id)
Definition lib_id.cc:404
void id_us_min(ID *id)
Definition lib_id.cc:366
void BKE_main_id_tag_all(Main *mainvar, int tag, bool value)
Definition lib_id.cc:1224
ID * BKE_lib_override_library_create_from_id(Main *bmain, ID *reference_id, bool do_tagged_remap)
void BKE_lib_override_library_id_reset(Main *bmain, ID *id_root, bool do_reset_system_override)
bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, Collection *override_resync_residual_storage, bool do_hierarchy_enforce, BlendFileReadReport *reports)
bool BKE_lib_override_library_create(Main *bmain, Scene *scene, ViewLayer *view_layer, Library *owner_library, ID *id_root_reference, ID *id_hierarchy_root_reference, ID *id_instance_hint, ID **r_id_root_override, const bool do_fully_editable)
void BKE_lib_override_library_id_hierarchy_reset(Main *bmain, ID *id_root, bool do_reset_system_override)
void BKE_lib_override_library_make_local(Main *bmain, ID *id)
void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
bool BKE_lib_override_library_is_hierarchy_leaf(Main *bmain, ID *id)
bool BKE_library_ID_is_indirectly_used(Main *bmain, void *idv)
Definition lib_query.cc:628
void void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(1
@ ID_REMAP_SKIP_INDIRECT_USAGE
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:583
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:577
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ 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_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
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)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define LIKELY(x)
external readfile function prototypes.
#define CTX_N_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
#define ID_IS_OVERRIDE_LIBRARY_VIRTUAL(_id)
Definition DNA_ID.h:727
#define ID_EXTRA_USERS(id)
Definition DNA_ID.h:662
@ ID_TAG_INDIRECT
Definition DNA_ID.h:848
@ ID_TAG_EXTERN
Definition DNA_ID.h:842
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_HIERARCHY
Definition DNA_ID.h:1158
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1104
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition DNA_ID.h:723
#define ID_IS_OVERRIDABLE_LIBRARY(_id)
Definition DNA_ID.h:716
#define ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(_id)
Definition DNA_ID.h:712
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_REAL_USERS(id)
Definition DNA_ID.h:676
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
#define ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(_id)
Definition DNA_ID.h:733
@ LIBOVERRIDE_FLAG_SYSTEM_DEFINED
Definition DNA_ID.h:363
@ ID_FLAG_EMBEDDED_DATA_LIB_OVERRIDE
Definition DNA_ID.h:785
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:774
ID_Type
@ ID_WM
@ ID_CA
@ ID_AR
@ ID_MC
@ ID_CF
@ ID_LI
@ ID_TE
@ ID_IM
@ ID_VO
@ ID_WS
@ ID_NT
@ ID_LA
@ ID_KE
@ ID_TXT
@ ID_SO
@ ID_SCE
@ ID_LS
@ ID_MSK
@ ID_CV
@ ID_PAL
@ ID_BR
@ ID_LP
@ ID_WO
@ ID_MA
@ ID_AC
@ ID_SCR
@ ID_CU_LEGACY
@ ID_GD_LEGACY
@ ID_VF
@ ID_ME
@ ID_GR
@ ID_SPK
@ ID_MB
@ ID_LT
@ ID_OB
@ ID_GP
@ ID_PA
@ ID_PT
@ ID_PC
#define ID_NLA
@ PCHAN_DRAW_HIDDEN
@ POSE_SELECTED
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_HIDDEN_A
@ BONE_HIDDEN_P
@ BONE_TIPSEL
Object groups, one object can be in many groups at once.
@ CONSTRAINT_OFF
@ eModifierMode_Render
@ eModifierMode_Realtime
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ OB_ARMATURE
#define TSE_IS_REAL_ID(_tse)
@ TSE_POSE_CHANNEL
@ TSE_STRIP
@ TSE_GP_LAYER
@ TSE_VIEW_COLLECTION_BASE
@ TSE_ANIM_DATA
@ TSE_EBONE
@ TSE_BONE
@ TSE_CONSTRAINT
@ TSE_SCENE_COLLECTION_BASE
@ TSE_LAYER_COLLECTION
@ TSE_GREASE_PENCIL_NODE
@ TSE_ID_BASE
@ TSE_SOME_ID
@ TSE_DRIVER_BASE
@ TSE_MODIFIER
@ TSE_RNA_STRUCT
@ TSE_SELECTED
@ TSE_CLOSED
@ TSE_ACTIVE
#define BASE_SELECTED(v3d, base)
eSpaceOutliner_Mode
@ SO_VIEW_LAYER
@ SO_SCENES
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
void ED_outliner_select_sync_from_all_tag(bContext *C)
bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene) ATTR_NONNULL()
Definition scene_edit.cc:93
bool ED_operator_region_outliner_active(bContext *C)
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:638
bool ED_operator_outliner_active(bContext *C)
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:98
Read Guarded memory(de)allocation.
const EnumPropertyItem * RNA_action_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool *r_free)
#define RNA_ENUM_ITEM_SEPR
Definition RNA_types.hh:676
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:432
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
#define MAX_MTEX
Definition Stroke.h:31
#define UI_UNIT_Y
@ UI_BLOCK_SEARCH_MENU
@ UI_BLOCK_LOOP
@ UI_BLOCK_MOVEMOUSE_QUIT
void UI_block_theme_style_set(uiBlock *block, char theme_style)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
@ UI_BUT_ACTIVATE_ON_INIT
void UI_block_bounds_set_popup(uiBlock *block, int addval, const int bounds_offset[2])
Definition interface.cc:653
void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free)
void UI_but_func_search_set(uiBut *but, uiButSearchCreateFn search_create_fn, uiButSearchUpdateFn search_update_fn, void *arg, bool free_arg, uiFreeArgFunc search_arg_free_fn, uiButHandleFunc search_exec_fn, void *active)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiBut * uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxncpy, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
void UI_but_tooltip_timer_remove(bContext *C, uiBut *but)
uiBut * UI_context_active_but_get(const bContext *C)
bool UI_search_item_add(uiSearchItems *items, blender::StringRef name, void *poin, int iconid, int but_flag, uint8_t name_prefix_offset)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
#define UI_SEARCHBOX_BOUNDS
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
int UI_search_items_find_index(const uiSearchItems *items, const char *name)
int UI_searchbox_size_y()
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_enable(uiBut *but, int flag)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
#define ND_SEQUENCER
Definition WM_types.hh:437
#define ND_WORLD
Definition WM_types.hh:452
#define NC_WINDOW
Definition WM_types.hh:375
#define NC_ID
Definition WM_types.hh:395
#define ND_NLA_ACTCHANGE
Definition WM_types.hh:498
#define ND_OB_ACTIVE
Definition WM_types.hh:440
#define NC_WM
Definition WM_types.hh:374
#define ND_DATA
Definition WM_types.hh:509
#define NC_ANIMATION
Definition WM_types.hh:388
#define ND_LIB_OVERRIDE_CHANGED
Definition WM_types.hh:419
#define ND_OB_SELECT
Definition WM_types.hh:442
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_LAYER_CONTENT
Definition WM_types.hh:453
#define ND_MODIFIER
Definition WM_types.hh:462
#define ND_POSE
Definition WM_types.hh:458
#define NA_EDITED
Definition WM_types.hh:584
#define ND_CONSTRAINT
Definition WM_types.hh:464
#define NA_REMOVED
Definition WM_types.hh:587
#define NC_GPENCIL
Definition WM_types.hh:399
#define ND_LAYER
Definition WM_types.hh:450
#define ND_OB_SHADING
Definition WM_types.hh:457
#define ND_SPACE_VIEW3D
Definition WM_types.hh:528
#define NC_OBJECT
Definition WM_types.hh:379
#define ND_ANIMCHAN
Definition WM_types.hh:496
#define NC_SPACE
Definition WM_types.hh:392
#define NA_SELECTED
Definition WM_types.hh:589
#define ND_SPACE_OUTLINER
Definition WM_types.hh:527
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const void * element
BPy_StructRNA * depsgraph
void append(const T &value)
bool is_empty() const
#define SELECT
KDTree_3d * tree
#define str(s)
#define GS(x)
#define active
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool action_treat_as_legacy(const bAction &action)
bool modifier_apply(Main *bmain, ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md, int mode, bool keep_modifier, bool do_all_keyframes)
void constraint_update(Main *bmain, Object *ob)
void base_select(Base *base, eObjectSelect_Mode mode)
bool editmode_exit(bContext *C, int flag)
void select_linked_by_id(bContext *C, ID *id)
void base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
bool editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int flag)
bool modifier_remove(ReportList *reports, Main *bmain, Scene *scene, Object *ob, ModifierData *md)
void all_open(const SpaceOutliner &space_outliner, const VisitorFn visitor)
static bool outliner_liboverride_operation_poll(bContext *C)
void OUTLINER_OT_delete(wmOperatorType *ot)
static void unlink_texture_fn(bContext *, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
static void ebone_fn(int event, TreeElement *te, TreeStoreElem *, void *)
static const EnumPropertyItem outliner_lib_op_type_items[]
static void object_deselect_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
static bool outliner_do_libdata_operation_selection_set_element(bContext *C, ReportList *reports, Scene *scene, TreeElement *element, TreeStoreElem *tselem, const bool has_parent_selected, outliner_operation_fn operation_fn, eOutlinerLibOpSelectionSet selection_set)
static void id_override_library_create_hierarchy_process(bContext *C, ReportList *reports, OutlinerLibOverrideData &data)
static wmOperatorStatus outliner_scene_operation_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_scene_operation(wmOperatorType *ot)
static const EnumPropertyItem * outliner_data_op_sets_enum_item_fn(bContext *C, PointerRNA *, PropertyRNA *, bool *)
static void object_select_hierarchy_fn(bContext *C, ReportList *, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *)
static void unlink_action_fn(bContext *C, ReportList *reports, Scene *, TreeElement *, TreeStoreElem *tsep, TreeStoreElem *tselem)
static void unlink_object_fn(bContext *C, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
static TreeTraversalAction outliner_collect_objects_to_delete(TreeElement *te, void *customdata)
static bool scene_fn(bContext *C, eOutliner_PropSceneOps event, TreeElement *, TreeStoreElem *tselem)
static wmOperatorStatus outliner_animdata_operation_exec(bContext *C, wmOperator *op)
bool outliner_is_collection_tree_element(const TreeElement *te)
static void get_element_operation_type(const TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel)
static void clear_animdata_fn(int, TreeElement *, TreeStoreElem *tselem, void *)
void OUTLINER_OT_data_operation(wmOperatorType *ot)
void OUTLINER_OT_lib_operation(wmOperatorType *ot)
static CLG_LogRef LOG
void OUTLINER_OT_constraint_operation(wmOperatorType *ot)
static const EnumPropertyItem prop_constraint_op_types[]
static const EnumPropertyItem prop_lib_op_selection_set[]
static void id_fake_user_set_fn(bContext *, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
blender::FunctionRef< void(bContext *C, ReportList *reports, Scene *scene, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)> outliner_operation_fn
static void modifier_fn(int event, TreeElement *te, TreeStoreElem *, void *arg)
static void object_select_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
static void id_override_library_clear_single_process(bContext *C, ReportList *, OutlinerLibOverrideData &data)
static void outliner_batch_delete_object_hierarchy(Main *bmain, Scene *scene)
static void refreshdrivers_animdata_fn(int, TreeElement *, TreeStoreElem *tselem, void *)
static wmOperatorStatus outliner_object_operation_exec(bContext *C, wmOperator *op)
void id_delete_tag_fn(bContext *C, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
static void id_override_library_reset(bContext *C, OutlinerLibOverrideData *data, TreeStoreElem *tselem)
static void merged_element_search_fn_recursive(const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items)
static wmOperatorStatus outliner_constraint_operation_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_scene_op_types[]
static void pchan_fn(int event, TreeElement *te, TreeStoreElem *, void *)
static wmOperatorStatus outliner_id_operation_exec(bContext *C, wmOperator *op)
static void outliner_batch_delete_object_hierarchy_tag(ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base)
void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
static void unlinkact_animdata_fn(int, TreeElement *, TreeStoreElem *tselem, void *)
void outliner_collection_delete(bContext *C, Main *bmain, Scene *scene, ReportList *reports, bool do_hierarchy)
TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
static void object_batch_delete_hierarchy_tag_fn(bContext *C, ReportList *reports, Scene *scene, Object *ob)
TreeElement * outliner_find_item_at_y(const SpaceOutliner *space_outliner, const ListBase *tree, float view_co_y)
static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *scene, Object *ob)
static const EnumPropertyItem prop_animdata_op_types[]
static void merged_element_search_update_fn(const bContext *, void *data, const char *str, uiSearchItems *items, const bool)
static void data_select_linked_fn(int event, TreeElement *te, TreeStoreElem *, void *C_v)
static void merged_element_search_exec_fn(bContext *C, void *, void *element)
static bool outliner_data_operation_poll(bContext *C)
static void id_override_library_resync(OutlinerLibOverrideData *data, TreeStoreElem *tselem)
void outliner_do_object_operation_ex(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, ListBase *lb, outliner_operation_fn operation_fn, bool recurse_selected)
static void singleuser_action_fn(bContext *C, ReportList *, Scene *, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
void OUTLINER_OT_object_operation(wmOperatorType *ot)
static void id_fake_user_clear_fn(bContext *, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
static void id_select_linked_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
static void id_local_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
static void bone_fn(int event, TreeElement *te, TreeStoreElem *, void *)
void OUTLINER_OT_animdata_operation(wmOperatorType *ot)
static void unlink_world_fn(bContext *, ReportList *reports, Scene *, TreeElement *, TreeStoreElem *tsep, TreeStoreElem *tselem)
void lib_relocate_fn(bContext *C, ReportList *, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
int tree_element_id_type_to_index(TreeElement *te)
static void id_override_library_clear_single(OutlinerLibOverrideData *data, ReportList *reports, TreeStoreElem *tselem)
static void id_override_library_create_hierarchy(Main &bmain, Scene *scene, ViewLayer *view_layer, OutlinerLibOverrideData &data, ID *id_hierarchy_root_reference, Vector< OutlinerLiboverrideDataIDRoot > &data_idroots, bool &r_aggregated_success)
static void unlink_collection_fn(bContext *C, ReportList *reports, Scene *, TreeElement *, TreeStoreElem *tsep, TreeStoreElem *tselem)
static wmOperatorStatus outliner_lib_operation_exec(bContext *C, wmOperator *op)
static void outliner_do_data_operation(SpaceOutliner *space_outliner, int type, int event, void(*operation_fn)(int, TreeElement *, TreeStoreElem *, void *), void *arg)
static const EnumPropertyItem prop_liboverride_op_types[]
static void id_override_library_delete_hierarchy(OutlinerLibOverrideData *data, TreeStoreElem *tselem)
void OUTLINER_OT_action_set(wmOperatorType *ot)
static bool outliner_id_operation_item_poll(bContext *C, PointerRNA *, PropertyRNA *, const int enum_value)
bool outliner_tree_traverse(const SpaceOutliner *space_outliner, ListBase *tree, int filter_te_flag, int filter_tselem_flag, TreeTraversalFunc func, void *customdata)
static const EnumPropertyItem prop_id_op_types[]
static void id_override_library_resync_hierarchy_process(bContext *C, ReportList *reports, OutlinerLibOverrideData &data)
static wmOperatorStatus outliner_data_operation_exec(bContext *C, wmOperator *op)
TreeElementT * tree_element_cast(const TreeElement *te)
ID * outliner_search_back(TreeElement *te, short idcode)
static void cleardrivers_animdata_fn(int, TreeElement *, TreeStoreElem *tselem, void *)
static wmOperatorStatus outliner_operator_menu(bContext *C, const char *opname)
void merged_element_search_menu_invoke(bContext *C, TreeElement *parent_te, TreeElement *activate_te)
static uiBlock * merged_element_search_menu(bContext *C, ARegion *region, void *data)
static TreeElement * get_target_element(const SpaceOutliner *space_outliner)
static void unlink_material_fn(bContext *, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
static void constraint_fn(int event, TreeElement *te, TreeStoreElem *, void *C_v)
static void actionset_id_fn(TreeElement *, TreeStoreElem *tselem, TreeStoreElem *tsep, ID *actId)
@ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT
static void outliner_batch_delete_object_tag(ReportList *reports, Main *bmain, Scene *scene, Object *object)
void id_remap_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
void lib_reload_fn(bContext *C, ReportList *, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
void outliner_cleanup_tree(SpaceOutliner *space_outliner)
static void singleuser_world_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *tsep, TreeStoreElem *tselem)
void outliner_do_object_operation(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, ListBase *lb, outliner_operation_fn operation_fn)
static wmOperatorStatus outliner_operation_invoke(bContext *C, wmOperator *, const wmEvent *event)
TreeElement * outliner_find_element_with_flag(const ListBase *lb, short flag)
static void outliner_do_libdata_operation(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, outliner_operation_fn operation_fn)
static wmOperatorStatus outliner_delete_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_liboverride_troubleshoot_operation(wmOperatorType *ot)
static wmOperatorStatus outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
static wmOperatorStatus outliner_modifier_operation_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_operation(wmOperatorType *ot)
static const EnumPropertyItem * outliner_id_operation_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool *r_free)
static const EnumPropertyItem prop_modifier_op_types[]
void item_rename_fn(bContext *C, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
static bool outliner_do_scene_operation(bContext *C, SpaceOutliner *space_outliner, eOutliner_PropSceneOps event, bool(*operation_fn)(bContext *, eOutliner_PropSceneOps, TreeElement *, TreeStoreElem *))
static void sequence_fn(int event, TreeElement *te, TreeStoreElem *, void *scene_ptr)
static void outliner_do_id_set_operation(SpaceOutliner *space_outliner, int type, ID *newid, void(*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
static void gpencil_layer_fn(int event, TreeElement *te, TreeStoreElem *, void *)
static wmOperatorStatus do_outliner_operation_event(bContext *C, ARegion *region, SpaceOutliner *space_outliner, TreeElement *te)
void(*)(bContext *C, ReportList *reports, Scene *scene, Object *ob) OutlinerDeleteFn
static const EnumPropertyItem prop_liboverride_troubleshoot_op_types[]
static wmOperatorStatus outliner_action_set_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_id_operation(wmOperatorType *ot)
void OUTLINER_OT_liboverride_operation(wmOperatorType *ot)
static void id_override_library_delete_hierarchy_process(bContext *C, ReportList *, OutlinerLibOverrideData &data)
void outliner_item_select(bContext *C, SpaceOutliner *space_outliner, TreeElement *te, short select_flag)
static void id_override_library_create_hierarchy_pre_process(bContext *C, OutlinerLibOverrideData *data, ReportList *reports, TreeElement *te, TreeStoreElem *tsep, TreeStoreElem *tselem)
static void outliner_do_libdata_operation_selection_set(bContext *C, ReportList *reports, Scene *scene, SpaceOutliner *space_outliner, const ListBase &subtree, const bool has_parent_selected, outliner_operation_fn operation_fn, eOutlinerLibOpSelectionSet selection_set)
static void outliner_do_object_delete(bContext *C, ReportList *reports, Scene *scene, const Set< Object * > &objects_to_delete, OutlinerDeleteFn delete_fn)
static const EnumPropertyItem prop_object_op_types[]
static void grease_pencil_node_fn(int event, TreeElement *te, TreeStoreElem *, void *)
static bool outliner_operation_tree_element_poll(bContext *C)
void select_strip_single(Scene *scene, Strip *strip, bool deselect_all)
void relations_invalidate_cache(Scene *scene, Strip *strip)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
#define TREESTORE(a)
#define TSELEM_OPEN(telm, sv)
const char * name
bool RNA_struct_is_ID(const StructRNA *type)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_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)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
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)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:26
const EnumPropertyItem rna_enum_dummy_DEFAULT_items[]
Definition rna_rna.cc:32
ListBase drivers
struct Base * next
short flag
struct Object * object
struct Material ** mat
short totcol
struct MTex * mtex[18]
unsigned int flag
Definition DNA_ID.h:348
struct ID * hierarchy_root
Definition DNA_ID.h:344
struct ID * reference
Definition DNA_ID.h:334
Definition DNA_ID.h:414
int tag
Definition DNA_ID.h:442
struct Library * lib
Definition DNA_ID.h:420
char name[258]
Definition DNA_ID.h:432
IDOverrideLibrary * override_library
Definition DNA_ID.h:494
short flag
Definition DNA_ID.h:438
unsigned int session_uid
Definition DNA_ID.h:462
ID id
Definition DNA_ID.h:550
struct Tex * tex
ListBase actions
Definition BKE_main.hh:300
ListBase objects
Definition BKE_main.hh:280
struct Material ** mat
ListBase constraints
struct Collection * instance_collection
struct Material ** mat
struct Object * parent
struct Collection * master_collection
struct World * world
struct Base * basact
struct Material ** mat
Map< ID *, Vector< OutlinerLiboverrideDataIDRoot > > id_hierarchy_roots
void id_root_add(ID *id_hierarchy_root_reference, ID *id_root_reference, ID *id_instance_hint, const bool is_override_instancing_object)
void id_root_set(ID *id_hierarchy_root_reference)
void op_enum(blender::StringRefNull opname, blender::StringRefNull propname, IDProperty *properties, blender::wm::OpCallContext context, eUI_Item_Flag flag, const int active=-1)
void operator_context_set(blender::wm::OpCallContext opcontext)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void menu_contents(blender::StringRef menuname)
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
bool WM_operator_poll(bContext *C, wmOperatorType *ot)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_menu_name_call(bContext *C, const char *menu_name, blender::wm::OpCallContext context)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
wmOperatorStatus WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *)
void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene)