Blender V5.0
tree_element_overrides.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_lib_override.hh"
10
11#include "BLI_function_ref.hh"
13#include "BLI_map.hh"
14#include "BLI_utildefines.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_space_types.h"
19
20#include "RNA_access.hh"
21#include "RNA_path.hh"
22
23#include "../outliner_intern.hh"
24
25#include "tree_display.hh"
26#include "tree_element_label.hh"
28
29#include <stdexcept>
30
31namespace blender::ed::outliner {
32
34 SpaceOutliner &space_outliner_;
36
37 public:
39 void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index);
40
41 private:
42 TreeElement &ensure_label_element_for_prop(
43 TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index);
44 TreeElement &ensure_label_element_for_ptr(TreeElement &parent,
45 StringRef elem_path,
47 short &index);
48 void ensure_entire_collection(TreeElement &te_to_expand,
49 const TreeElementOverridesData &override_data,
50 const char *coll_prop_path,
51 short &index);
52};
53
54/* -------------------------------------------------------------------- */
61
63 : AbstractTreeElement(legacy_te), id(id)
64{
66 if (legacy_te.parent != nullptr &&
68 {
69 legacy_te.name = IFACE_("Library Overrides");
70 }
71 else {
72 legacy_te.name = id.name + 2;
73 }
74}
75
77{
79 return RPT_("This override data-block is not needed anymore, but was detected as user-edited");
80 }
81
82 if (ID_IS_OVERRIDE_LIBRARY_REAL(&id) && ID_REAL_USERS(&id) == 0) {
83 return RPT_("This override data-block is unused");
84 }
85
86 return {};
87}
88
90 const bool show_system_overrides,
92{
93 PointerRNA override_rna_ptr;
94 PropertyRNA *override_rna_prop;
95
97
98 for (IDOverrideLibraryProperty *override_prop :
99 ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties))
100 {
101 int rnaprop_index = 0;
102 const bool is_rna_path_valid = BKE_lib_override_rna_property_find(
103 &idpoin, override_prop, &override_rna_ptr, &override_rna_prop, &rnaprop_index);
104
105 /* Check for conditions where the liboverride property should be considered as a system
106 * override, if needed. */
107 if (is_rna_path_valid && !show_system_overrides) {
108 bool do_skip = true;
109 bool is_system_override = false;
110
111 /* Matching ID pointers are considered as system overrides. */
112 if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
113 RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop)))
114 {
115 for (IDOverrideLibraryPropertyOperation *override_prop_op :
117 {
118 if ((override_prop_op->flag & LIBOVERRIDE_OP_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
119 do_skip = false;
120 break;
121 }
122 is_system_override = true;
123 }
124 }
125
126 /* Animated/driven properties are considered as system overrides. */
127 if (!is_system_override && !BKE_lib_override_library_property_is_animated(
128 &id, override_prop, override_rna_prop, rnaprop_index))
129 {
130 do_skip = false;
131 }
132
133 if (do_skip) {
134 continue;
135 }
136 }
137
139 id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid};
140
141 fn(data);
142 }
143}
144
146{
147 BLI_assert(id.override_library != nullptr);
148
149 const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) &&
150 (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) !=
151 0);
152
153 OverrideRNAPathTreeBuilder path_builder(space_outliner);
154 short index = 0;
155
156 iterate_properties_to_display(id, show_system_overrides, [&](TreeElementOverridesData &data) {
157 path_builder.build_path(legacy_te_, data, index);
158 });
159}
160
162
163/* -------------------------------------------------------------------- */
169
171 TreeElementOverridesData &override_data)
172 : AbstractTreeElement(legacy_te),
173 override_rna_ptr(override_data.override_rna_ptr),
175 rna_path(override_data.override_property.rna_path),
177{
180
181 legacy_te.name = RNA_property_ui_name(&override_data.override_rna_prop);
182}
183
185{
186 if (!is_rna_path_valid) {
187 return RPT_(
188 "This override property does not exist in current data, it will be removed on "
189 "next .blend file save");
190 }
191
192 return {};
193}
194
196
197/* -------------------------------------------------------------------- */
202
204 TreeElement &legacy_te, TreeElementOverridesData &override_data)
205 : TreeElementOverridesProperty(legacy_te, override_data)
206{
209 "Override operations are only supported for collections right now");
210 /* Quiet Clang Static Analyzer warning by throwing instead of asserting (possible
211 * null-dereference). */
212 if (!override_data.operation) {
213 throw std::invalid_argument("missing operation");
214 }
215
216 operation_ = std::make_unique<IDOverrideLibraryPropertyOperation>(*override_data.operation);
217 /* Just for extra sanity. */
218 operation_->next = operation_->prev = nullptr;
219
220 if (std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) {
221 const char *dyn_name = RNA_struct_name_get_alloc(&*col_item_ptr, nullptr, 0, nullptr);
222 if (dyn_name) {
223 legacy_te.name = dyn_name;
224 legacy_te.flag |= TE_FREE_NAME;
225 }
226 else {
227 legacy_te.name = RNA_struct_ui_name(col_item_ptr->type);
228 }
229 }
230}
231
233{
234 switch (operation_->operation) {
237 return RPT_("Added through override");
239 /* Returning nothing so that drawing code shows actual RNA button instead. */
240 return {};
241 /* Following cases are not expected in regular situation, but could be found in experimental
242 * files. */
244 return RPT_("Protected from override");
246 return RPT_("Additive override");
248 return RPT_("Subtractive override");
250 return RPT_("Multiplicative override");
251 default:
253 return {};
254 }
255}
256
258{
259 if (const std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) {
260 return RNA_struct_ui_icon(col_item_ptr->type);
261 }
262
263 return {};
264}
265
266std::optional<PointerRNA> TreeElementOverridesPropertyOperation::get_collection_ptr() const
267{
268 PointerRNA col_item_ptr;
271 operation_->subitem_local_index,
272 &col_item_ptr))
273 {
274 return col_item_ptr;
275 }
276
277 return {};
278}
279
281
282/* -------------------------------------------------------------------- */
304
306 : space_outliner_(space_outliner)
307{
308}
309
311 TreeElementOverridesData &override_data,
312 short &index)
313{
314 PointerRNA idpoin = RNA_id_pointer_create(&override_data.id);
315
316 ListBase path_elems = {nullptr};
317 if (!RNA_path_resolve_elements(&idpoin, override_data.override_property.rna_path, &path_elems)) {
318 return;
319 }
320
321 const char *elem_path = nullptr;
322 TreeElement *te_to_expand = &parent;
323 char name_buf[128], *name;
324
325 LISTBASE_FOREACH (PropertyElemRNA *, elem, &path_elems) {
326 if (!elem->next) {
327 /* The last element is added as #TSE_LIBRARY_OVERRIDE below. */
328 break;
329 }
330 const char *previous_path = elem_path;
331 const char *new_path = RNA_path_append(previous_path, &elem->ptr, elem->prop, -1, nullptr);
332
333 te_to_expand = &ensure_label_element_for_prop(
334 *te_to_expand, new_path, elem->ptr, *elem->prop, index);
335
336 /* Above the collection property was added (e.g. "Modifiers"), to get the actual collection
337 * item the path refers to, we have to peek at the following path element and add a tree
338 * element for its pointer (e.g. "My Subdiv Modifier"). */
339 if (RNA_property_type(elem->prop) == PROP_COLLECTION) {
340 const int coll_item_idx = RNA_property_collection_lookup_index(
341 &elem->ptr, elem->prop, &elem->next->ptr);
342 name = RNA_struct_name_get_alloc(&elem->next->ptr, name_buf, sizeof(name_buf), nullptr);
343 const char *coll_item_path = RNA_path_append(
344 previous_path, &elem->ptr, elem->prop, coll_item_idx, name);
345 if (name && (name != name_buf)) {
347 }
348
349 te_to_expand = &ensure_label_element_for_ptr(
350 *te_to_expand, coll_item_path, elem->next->ptr, index);
351
352 MEM_delete(new_path);
353 new_path = coll_item_path;
354 }
355
356 if (new_path) {
357 MEM_delete(elem_path);
358 elem_path = new_path;
359 }
360 }
361 LISTBASE_FOREACH_MUTABLE (PropertyElemRNA *, elem, &path_elems) {
362 MEM_delete(elem);
363 }
364 BLI_listbase_clear(&path_elems);
365
366 /* Special case: Overriding collections, e.g. adding or removing items. In this case we add
367 * elements for all collection items to show full context, and indicate which ones were
368 * added/removed (currently added only). Note that a single collection override may add/remove
369 * multiple items. */
370 if (RNA_property_type(&override_data.override_rna_prop) == PROP_COLLECTION) {
371 /* Tree element for the actual collection item (e.g. "Modifiers"). Can just use the override
372 * ptr & prop here, since they point to the collection property (e.g. `modifiers`). */
373 te_to_expand = &ensure_label_element_for_prop(*te_to_expand,
374 override_data.override_property.rna_path,
375 override_data.override_rna_ptr,
376 override_data.override_rna_prop,
377 index);
378
379 ensure_entire_collection(*te_to_expand, override_data, elem_path, index);
380 }
381 /* Some properties have multiple operations (e.g. an array property with multiple changed
382 * values), so the element may already be present. At this point they are displayed as a single
383 * property in the tree, so don't add it multiple times here. */
384 else if (!path_te_map.contains(override_data.override_property.rna_path)) {
385 AbstractTreeDisplay::add_element(&space_outliner_,
386 &te_to_expand->subtree,
387 &override_data.id,
388 &override_data,
389 te_to_expand,
391 index++);
392 }
393
394 MEM_delete(elem_path);
395}
396
397void OverrideRNAPathTreeBuilder::ensure_entire_collection(
398 TreeElement &te_to_expand,
399 const TreeElementOverridesData &override_data,
400 /* The path of the owning collection property. */
401 const char *coll_prop_path,
402 short &index)
403{
404 BLI_assert(tree_element_cast<AbstractTreeElement>(&te_to_expand) != nullptr);
405
406 TreeElement *previous_te = nullptr;
407 int item_idx = 0;
408 char name_buf[128], *name;
409 RNA_PROP_BEGIN (&override_data.override_rna_ptr, itemptr, &override_data.override_rna_prop) {
410 name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), nullptr);
411 const char *coll_item_path = RNA_path_append(coll_prop_path,
412 &override_data.override_rna_ptr,
413 &override_data.override_rna_prop,
414 item_idx,
415 name);
416 if (name && (name != name_buf)) {
418 }
419 IDOverrideLibraryPropertyOperation *item_operation =
421 nullptr,
422 nullptr,
423 {},
424 {},
425 -1,
426 item_idx,
427 false,
428 nullptr);
429 TreeElement *current_te = nullptr;
430
431 TreeElement *existing_te = path_te_map.lookup_default(coll_item_path, nullptr);
432
433 if (existing_te) {
434 /* Reinsert the element to make sure the order is right. It may have been inserted by a
435 * previous override. */
436 BLI_remlink(&te_to_expand.subtree, existing_te);
437 BLI_insertlinkafter(&te_to_expand.subtree, previous_te, existing_te);
438 current_te = existing_te;
439 }
440 /* Is there an operation for this item (added or removed the item to/from the collection)? If
441 * so indicate it as override using #TSE_LIBRARY_OVERRIDE_OPERATION. Otherwise it's just a
442 * regular collection we display for context. */
443 else if (item_operation) {
444 TreeElementOverridesData override_op_data = override_data;
445 override_op_data.operation = item_operation;
446
447 current_te = AbstractTreeDisplay::add_element(&space_outliner_,
448 &te_to_expand.subtree,
449 &override_op_data.id,
450 /* Element will store a copy. */
451 &override_op_data,
452 &te_to_expand,
454 index++);
455 }
456 else {
457 current_te = &ensure_label_element_for_ptr(te_to_expand, coll_item_path, itemptr, index);
458 }
459
460 MEM_delete(coll_item_path);
461 item_idx++;
462 previous_te = current_te;
463 }
465}
466
468{
469 BIFIconID icon = RNA_property_ui_icon(&prop);
470 if (icon) {
471 return icon;
472 }
473
474 /* Try if the collection item type has a dedicated icon (e.g. #ICON_MODIFIER for the
475 * #Object.modifiers property). */
476 if (RNA_property_type(&prop) == PROP_COLLECTION) {
477 const StructRNA *coll_ptr_type = RNA_property_pointer_type(&ptr, &prop);
478 icon = RNA_struct_ui_icon(coll_ptr_type);
479 if (icon != ICON_DOT) {
480 return icon;
481 }
482 }
483
484 return ICON_NONE;
485}
486
487TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_prop(
488 TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index)
489{
490 return *path_te_map.lookup_or_add_cb(elem_path, [&]() {
491 TreeElement *new_te = AbstractTreeDisplay::add_element(&space_outliner_,
492 &parent.subtree,
493 nullptr,
494 (void *)RNA_property_ui_name(&prop),
495 &parent,
497 index++,
498 false);
499 TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te);
500
501 te_label->set_icon(get_property_icon(ptr, prop));
502 return new_te;
503 });
504}
505
506TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_ptr(TreeElement &parent,
507 StringRef elem_path,
509 short &index)
510{
511 return *path_te_map.lookup_or_add_cb(elem_path, [&]() {
512 const char *dyn_name = RNA_struct_name_get_alloc(&ptr, nullptr, 0, nullptr);
513
514 TreeElement *new_te = AbstractTreeDisplay::add_element(
515 &space_outliner_,
516 &parent.subtree,
517 nullptr,
518 (void *)(dyn_name ? dyn_name : RNA_struct_ui_name(ptr.type)),
519 &parent,
521 index++);
522 TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te);
523 te_label->set_icon(RNA_struct_ui_icon(ptr.type));
524
525 MEM_delete(dyn_name);
526
527 return new_te;
528 });
529}
530
532
533} // namespace blender::ed::outliner
bool BKE_lib_override_library_property_is_animated(const ID *id, const IDOverrideLibraryProperty *liboverride_prop, const PropertyRNA *override_rna_prop, const int rnaprop_index)
IDOverrideLibraryPropertyOperation * BKE_lib_override_library_property_operation_find(IDOverrideLibraryProperty *liboverride_property, const char *subitem_refname, const char *subitem_locname, const std::optional< const ID * > &subitem_refid, const std::optional< const ID * > &subitem_locid, int subitem_refindex, int subitem_locindex, bool strict, bool *r_strict)
bool BKE_lib_override_rna_property_find(PointerRNA *idpoin, const IDOverrideLibraryProperty *library_prop, PointerRNA *r_override_poin, PropertyRNA **r_override_prop, int *r_index)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define ELEM(...)
#define RPT_(msgid)
#define IFACE_(msgid)
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition DNA_ID.h:723
@ LIBOVERRIDE_OP_FLAG_IDPOINTER_MATCH_REFERENCE
Definition DNA_ID.h:256
#define ID_REAL_USERS(id)
Definition DNA_ID.h:676
@ LIBOVERRIDE_OP_NOOP
Definition DNA_ID.h:228
@ LIBOVERRIDE_OP_ADD
Definition DNA_ID.h:233
@ LIBOVERRIDE_OP_SUBTRACT
Definition DNA_ID.h:235
@ LIBOVERRIDE_OP_REPLACE
Definition DNA_ID.h:230
@ LIBOVERRIDE_OP_MULTIPLY
Definition DNA_ID.h:237
@ LIBOVERRIDE_OP_INSERT_BEFORE
Definition DNA_ID.h:241
@ LIBOVERRIDE_OP_INSERT_AFTER
Definition DNA_ID.h:240
@ ID_FLAG_LIB_OVERRIDE_RESYNC_LEFTOVER
Definition DNA_ID.h:790
@ TSE_LIBRARY_OVERRIDE_OPERATION
@ TSE_LIBRARY_OVERRIDE
@ TSE_LIBRARY_OVERRIDE_BASE
@ TSE_LAYER_COLLECTION
@ TSE_GENERIC_LABEL
@ TSE_SOME_ID
@ SO_FILTER_SHOW_SYSTEM_OVERRIDES
int BIFIconID
Definition ED_asset.hh:28
#define RNA_PROP_END
#define RNA_PROP_BEGIN(sptr, itemptr, prop)
@ PROP_POINTER
Definition RNA_types.hh:167
@ PROP_COLLECTION
Definition RNA_types.hh:168
BMesh const char void * data
static TreeElement * add_element(SpaceOutliner *space_outliner, ListBase *lb, ID *owner_id, void *create_data, TreeElement *parent, short type, short index, const bool expand=true)
void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index)
TreeElementOverridesPropertyOperation(TreeElement &legacy_te, TreeElementOverridesData &override_data)
TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void iterate_properties_to_display(ID &id, const bool show_system_overrides, FunctionRef< void(TreeElementOverridesData &data)> fn)
TreeElementT * tree_element_cast(const TreeElement *te)
static BIFIconID get_property_icon(PointerRNA &ptr, PropertyRNA &prop)
ListBaseWrapperTemplate< ListBase, T > ListBaseWrapper
#define SUPPORT_FILTER_OUTLINER(space_outliner_)
const char * name
bool RNA_struct_is_ID(const StructRNA *type)
int RNA_property_ui_icon(const PropertyRNA *prop)
int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, const PointerRNA *t_ptr)
StructRNA * RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
char * RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
PropertyType RNA_property_type(PropertyRNA *prop)
const char * RNA_property_ui_name(const PropertyRNA *prop, const PointerRNA *ptr)
int RNA_struct_ui_icon(const StructRNA *type)
bool RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, PointerRNA *r_ptr)
const char * RNA_struct_ui_name(const StructRNA *type)
PointerRNA RNA_id_pointer_create(ID *id)
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
Definition rna_path.cc:608
char * RNA_path_append(const char *path, const PointerRNA *, PropertyRNA *prop, int intkey, const char *strkey)
Definition rna_path.cc:613
Definition DNA_ID.h:414
Establish and manage Outliner trees for different display modes.
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145