Blender  V3.3
tree_element_overrides.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "BKE_collection.h"
8 #include "BKE_lib_override.h"
9 
10 #include "BLI_function_ref.hh"
11 #include "BLI_listbase_wrapper.hh"
12 #include "BLI_map.hh"
13 #include "BLI_utildefines.h"
14 
15 #include "BLT_translation.h"
16 
17 #include "DNA_space_types.h"
18 
19 #include "RNA_access.h"
20 #include "RNA_path.h"
21 
22 #include "../outliner_intern.hh"
23 
24 #include "tree_element_label.hh"
26 
27 namespace blender::ed::outliner {
28 
30  SpaceOutliner &space_outliner_;
32 
33  public:
35  void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index);
36 
37  private:
38  TreeElement &ensure_label_element_for_prop(
39  TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index);
40  TreeElement &ensure_label_element_for_ptr(TreeElement &parent,
41  StringRef elem_path,
42  PointerRNA &ptr,
43  short &index);
44  void ensure_entire_collection(TreeElement &te_to_expand,
45  const TreeElementOverridesData &override_data,
46  const char *coll_prop_path,
47  short &index);
48 };
49 
50 /* -------------------------------------------------------------------- */
59  : AbstractTreeElement(legacy_te), id(id)
60 {
62  if (legacy_te.parent != nullptr &&
64  legacy_te.name = IFACE_("Library Overrides");
65  }
66  else {
67  legacy_te.name = id.name + 2;
68  }
69 }
70 
72 {
73  if (id.flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) {
74  return TIP_("This override data-block is not needed anymore, but was detected as user-edited");
75  }
76 
77  if (ID_IS_OVERRIDE_LIBRARY_REAL(&id) && ID_REAL_USERS(&id) == 0) {
78  return TIP_("This override data-block is unused");
79  }
80 
81  return {};
82 }
83 
85  const bool show_system_overrides,
87 {
88  PointerRNA override_rna_ptr;
89  PropertyRNA *override_rna_prop;
90 
91  PointerRNA idpoin;
92  RNA_id_pointer_create(&id, &idpoin);
93 
94  for (IDOverrideLibraryProperty *override_prop :
95  ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) {
96  int rnaprop_index = 0;
97  const bool is_rna_path_valid = BKE_lib_override_rna_property_find(
98  &idpoin, override_prop, &override_rna_ptr, &override_rna_prop, &rnaprop_index);
99 
100  /* Check for conditions where the liboverride property should be considered as a system
101  * override, if needed. */
102  if (is_rna_path_valid && !show_system_overrides) {
103  bool do_skip = true;
104  bool is_system_override = false;
105 
106  /* Matching ID pointers are considered as system overrides. */
107  if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
108  RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) {
109  for (IDOverrideLibraryPropertyOperation *override_prop_op :
110  ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) {
111  if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
112  do_skip = false;
113  break;
114  }
115  is_system_override = true;
116  }
117  }
118 
119  /* Animated/driven properties are considered as system overrides. */
120  if (!is_system_override && !BKE_lib_override_library_property_is_animated(
121  &id, override_prop, override_rna_prop, rnaprop_index)) {
122  do_skip = false;
123  }
124 
125  if (do_skip) {
126  continue;
127  }
128  }
129 
131  id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid};
132 
133  fn(data);
134  }
135 }
136 
138 {
139  BLI_assert(id.override_library != nullptr);
140 
141  const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) &&
142  (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) !=
143  0);
144 
145  OverrideRNAPathTreeBuilder path_builder(space_outliner);
146  short index = 0;
147 
148  iterate_properties_to_display(id, show_system_overrides, [&](TreeElementOverridesData &data) {
149  path_builder.build_path(legacy_te_, data, index);
150  });
151 }
152 
155 /* -------------------------------------------------------------------- */
163  TreeElementOverridesData &override_data)
164  : AbstractTreeElement(legacy_te),
165  override_rna_ptr(override_data.override_rna_ptr),
166  override_rna_prop(override_data.override_rna_prop),
167  rna_path(override_data.override_property.rna_path),
168  is_rna_path_valid(override_data.is_rna_path_valid)
169 {
170  BLI_assert(
172 
173  legacy_te.name = RNA_property_ui_name(&override_data.override_rna_prop);
174 }
175 
177 {
178  if (!is_rna_path_valid) {
179  return TIP_(
180  "This override property does not exist in current data, it will be removed on "
181  "next .blend file save");
182  }
183 
184  return {};
185 }
186 
189 /* -------------------------------------------------------------------- */
196  TreeElement &legacy_te, TreeElementOverridesData &override_data)
197  : TreeElementOverridesProperty(legacy_te, override_data)
198 {
201  "Override operations are only supported for collections right now");
202  /* Quiet Clang Static Analyzer warning by throwing instead of asserting (possible
203  * null-dereference). */
204  if (!override_data.operation) {
205  throw std::invalid_argument("missing operation");
206  }
207 
208  operation_ = std::make_unique<IDOverrideLibraryPropertyOperation>(*override_data.operation);
209  /* Just for extra sanity. */
210  operation_->next = operation_->prev = nullptr;
211 
212  if (std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) {
213  const char *dyn_name = RNA_struct_name_get_alloc(&*col_item_ptr, nullptr, 0, nullptr);
214  if (dyn_name) {
215  legacy_te.name = dyn_name;
216  legacy_te.flag |= TE_FREE_NAME;
217  }
218  else {
219  legacy_te.name = RNA_struct_ui_name(col_item_ptr->type);
220  }
221  }
222 }
223 
225 {
226  if (ELEM(operation_->operation,
229  return TIP_("Added through override");
230  }
231 
233  return {};
234 }
235 
236 std::optional<BIFIconID> TreeElementOverridesPropertyOperation::getIcon() const
237 {
238  if (const std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) {
239  return (BIFIconID)RNA_struct_ui_icon(col_item_ptr->type);
240  }
241 
242  return {};
243 }
244 
245 std::optional<PointerRNA> TreeElementOverridesPropertyOperation::get_collection_ptr() const
246 {
247  PointerRNA col_item_ptr;
250  operation_->subitem_local_index,
251  &col_item_ptr)) {
252  return col_item_ptr;
253  }
254 
255  return {};
256 }
257 
260 /* -------------------------------------------------------------------- */
284  : space_outliner_(space_outliner)
285 {
286 }
287 
289  TreeElementOverridesData &override_data,
290  short &index)
291 {
292  PointerRNA idpoin;
293  RNA_id_pointer_create(&override_data.id, &idpoin);
294 
295  ListBase path_elems = {NULL};
296  if (!RNA_path_resolve_elements(&idpoin, override_data.override_property.rna_path, &path_elems)) {
297  return;
298  }
299 
300  const char *elem_path = nullptr;
301  TreeElement *te_to_expand = &parent;
302 
303  LISTBASE_FOREACH (PropertyElemRNA *, elem, &path_elems) {
304  if (!elem->next) {
305  /* The last element is added as #TSE_LIBRARY_OVERRIDE below. */
306  break;
307  }
308  const char *previous_path = elem_path;
309  const char *new_path = RNA_path_append(previous_path, &elem->ptr, elem->prop, -1, nullptr);
310 
311  te_to_expand = &ensure_label_element_for_prop(
312  *te_to_expand, new_path, elem->ptr, *elem->prop, index);
313 
314  /* Above the collection property was added (e.g. "Modifiers"), to get the actual collection
315  * item the path refers to, we have to peek at the following path element and add a tree
316  * element for its pointer (e.g. "My Subdiv Modifier"). */
317  if (RNA_property_type(elem->prop) == PROP_COLLECTION) {
318  const int coll_item_idx = RNA_property_collection_lookup_index(
319  &elem->ptr, elem->prop, &elem->next->ptr);
320  const char *coll_item_path = RNA_path_append(
321  previous_path, &elem->ptr, elem->prop, coll_item_idx, nullptr);
322 
323  te_to_expand = &ensure_label_element_for_ptr(
324  *te_to_expand, coll_item_path, elem->next->ptr, index);
325 
326  MEM_delete(new_path);
327  new_path = coll_item_path;
328  }
329 
330  if (new_path) {
331  MEM_delete(elem_path);
332  elem_path = new_path;
333  }
334  }
335  BLI_freelistN(&path_elems);
336 
337  /* Special case: Overriding collections, e.g. adding or removing items. In this case we add
338  * elements for all collection items to show full context, and indicate which ones were
339  * added/removed (currently added only). Note that a single collection override may add/remove
340  * multiple items. */
341  if (RNA_property_type(&override_data.override_rna_prop) == PROP_COLLECTION) {
342  /* Tree element for the actual collection item (e.g. "Modifiers"). Can just use the override
343  * ptr & prop here, since they point to the collection property (e.g. `modifiers`). */
344  te_to_expand = &ensure_label_element_for_prop(*te_to_expand,
345  override_data.override_property.rna_path,
346  override_data.override_rna_ptr,
347  override_data.override_rna_prop,
348  index);
349 
350  ensure_entire_collection(*te_to_expand, override_data, elem_path, index);
351  }
352  /* Some properties have multiple operations (e.g. an array property with multiple changed
353  * values), so the element may already be present. At this point they are displayed as a single
354  * property in the tree, so don't add it multiple times here. */
355  else if (!path_te_map.contains(override_data.override_property.rna_path)) {
356  outliner_add_element(&space_outliner_,
357  &te_to_expand->subtree,
358  &override_data,
359  te_to_expand,
361  index++);
362  }
363 
364  MEM_delete(elem_path);
365 }
366 
367 void OverrideRNAPathTreeBuilder::ensure_entire_collection(
368  TreeElement &te_to_expand,
369  const TreeElementOverridesData &override_data,
370  /* The path of the owning collection property. */
371  const char *coll_prop_path,
372  short &index)
373 {
374  AbstractTreeElement *abstract_parent = tree_element_cast<AbstractTreeElement>(&te_to_expand);
375  BLI_assert(abstract_parent != nullptr);
376  UNUSED_VARS_NDEBUG(abstract_parent);
377 
378  TreeElement *previous_te = nullptr;
379  int item_idx = 0;
380  RNA_PROP_BEGIN (&override_data.override_rna_ptr, itemptr, &override_data.override_rna_prop) {
381  const char *coll_item_path = RNA_path_append(coll_prop_path,
382  &override_data.override_rna_ptr,
383  &override_data.override_rna_prop,
384  item_idx,
385  nullptr);
386  IDOverrideLibraryPropertyOperation *item_operation =
388  &override_data.override_property, nullptr, nullptr, -1, item_idx, false, nullptr);
389  TreeElement *current_te = nullptr;
390 
391  TreeElement *existing_te = path_te_map.lookup_default(coll_item_path, nullptr);
392 
393  if (existing_te) {
394  /* Reinsert the element to make sure the order is right. It may have been inserted by a
395  * previous override. */
396  BLI_remlink(&te_to_expand.subtree, existing_te);
397  BLI_insertlinkafter(&te_to_expand.subtree, previous_te, existing_te);
398  current_te = existing_te;
399  }
400  /* Is there an operation for this item (added or removed the item to/from the collection)? If
401  * so indicate it as override using #TSE_LIBRARY_OVERRIDE_OPERATION. Otherwise it's just a
402  * regular collection we display for context. */
403  else if (item_operation) {
404  TreeElementOverridesData override_op_data = override_data;
405  override_op_data.operation = item_operation;
406 
407  current_te = outliner_add_element(&space_outliner_,
408  &te_to_expand.subtree,
409  /* Element will store a copy. */
410  &override_op_data,
411  &te_to_expand,
413  index++);
414  }
415  else {
416  current_te = &ensure_label_element_for_ptr(te_to_expand, coll_item_path, itemptr, index);
417  }
418 
419  MEM_delete(coll_item_path);
420  item_idx++;
421  previous_te = current_te;
422  }
423  RNA_PROP_END;
424 }
425 
427 {
429  if (icon) {
430  return icon;
431  }
432 
433  /* Try if the collection item type has a dedicated icon (e.g. #ICON_MODIFIER for the
434  * #Object.modifiers property). */
435  if (RNA_property_type(&prop) == PROP_COLLECTION) {
436  const StructRNA *coll_ptr_type = RNA_property_pointer_type(&ptr, &prop);
437  icon = (BIFIconID)RNA_struct_ui_icon(coll_ptr_type);
438  if (icon != ICON_DOT) {
439  return icon;
440  }
441  }
442 
443  return ICON_NONE;
444 }
445 
446 TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_prop(
447  TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index)
448 {
449  return *path_te_map.lookup_or_add_cb(elem_path, [&]() {
450  TreeElement *new_te = outliner_add_element(&space_outliner_,
451  &parent.subtree,
452  (void *)RNA_property_ui_name(&prop),
453  &parent,
455  index++,
456  false);
457  TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te);
458 
459  te_label->setIcon(get_property_icon(ptr, prop));
460  return new_te;
461  });
462 }
463 
464 TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_ptr(TreeElement &parent,
465  StringRef elem_path,
466  PointerRNA &ptr,
467  short &index)
468 {
469  return *path_te_map.lookup_or_add_cb(elem_path, [&]() {
470  const char *dyn_name = RNA_struct_name_get_alloc(&ptr, nullptr, 0, nullptr);
471 
473  &space_outliner_,
474  &parent.subtree,
475  (void *)(dyn_name ? dyn_name : RNA_struct_ui_name(ptr.type)),
476  &parent,
478  index++);
479  TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te);
480  te_label->setIcon((BIFIconID)RNA_struct_ui_icon(ptr.type));
481 
482  MEM_delete(dyn_name);
483 
484  return new_te;
485  });
486 }
487 
490 } // namespace blender::ed::outliner
struct IDOverrideLibraryPropertyOperation * BKE_lib_override_library_property_operation_find(struct IDOverrideLibraryProperty *override_property, const char *subitem_refname, const char *subitem_locname, int subitem_refindex, int subitem_locindex, bool strict, bool *r_strict)
bool BKE_lib_override_rna_property_find(struct PointerRNA *idpoin, const struct IDOverrideLibraryProperty *library_prop, struct PointerRNA *r_override_poin, struct PropertyRNA **r_override_prop, int *r_index)
bool BKE_lib_override_library_property_is_animated(const ID *id, const IDOverrideLibraryProperty *override_prop, const struct PropertyRNA *override_rna_prop, const int rnaprop_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)
Definition: BLI_listbase.h:336
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define TIP_(msgid)
#define IFACE_(msgid)
@ IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE
Definition: DNA_ID.h:244
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition: DNA_ID.h:581
@ IDOVERRIDE_LIBRARY_OP_INSERT_AFTER
Definition: DNA_ID.h:230
@ IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE
Definition: DNA_ID.h:231
@ LIB_LIB_OVERRIDE_RESYNC_LEFTOVER
Definition: DNA_ID.h:651
#define ID_REAL_USERS(id)
Definition: DNA_ID.h:553
@ 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
#define RNA_PROP_END
Definition: RNA_access.h:563
#define RNA_PROP_BEGIN(sptr, itemptr, prop)
Definition: RNA_access.h:556
@ PROP_POINTER
Definition: RNA_types.h:64
@ PROP_COLLECTION
Definition: RNA_types.h:65
BIFIconID
Definition: UI_resources.h:18
Value lookup_default(const Key &key, const Value &default_value) const
Definition: BLI_map.hh:510
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition: BLI_map.hh:561
bool contains(const Key &key) const
Definition: BLI_map.hh:308
void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index)
TreeElementOverridesBase(TreeElement &legacy_te, ID &id)
TreeElementOverridesPropertyOperation(TreeElement &legacy_te, TreeElementOverridesData &override_data)
TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data)
TreeElement * outliner_add_element(SpaceOutliner *space_outliner, ListBase *lb, void *idv, TreeElement *parent, short type, short index, const bool expand)
static void iterate_properties_to_display(ID &id, const bool show_system_overrides, FunctionRef< void(TreeElementOverridesData &data)> fn)
static BIFIconID get_property_icon(PointerRNA &ptr, PropertyRNA &prop)
#define SUPPORT_FILTER_OUTLINER(space_outliner_)
@ TE_FREE_NAME
void RNA_id_pointer_create(ID *id, PointerRNA *r_ptr)
Definition: rna_access.c:112
bool RNA_struct_is_ID(const StructRNA *type)
Definition: rna_access.c:655
int RNA_property_ui_icon(const PropertyRNA *prop)
Definition: rna_access.c:1900
int RNA_property_collection_lookup_index(PointerRNA *ptr, PropertyRNA *prop, const PointerRNA *t_ptr)
Definition: rna_access.c:4059
char * RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
Definition: rna_access.c:907
const char * RNA_struct_ui_name(const StructRNA *type)
Definition: rna_access.c:591
PropertyType RNA_property_type(PropertyRNA *prop)
Definition: rna_access.c:1010
int RNA_property_collection_lookup_int(PointerRNA *ptr, PropertyRNA *prop, int key, PointerRNA *r_ptr)
Definition: rna_access.c:4097
StructRNA * RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:1405
int RNA_struct_ui_icon(const StructRNA *type)
Definition: rna_access.c:601
const char * RNA_property_ui_name(const PropertyRNA *prop)
Definition: rna_access.c:1875
bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
Definition: rna_path.cc:579
char * RNA_path_append(const char *path, const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int intkey, const char *strkey)
Definition: rna_path.cc:584
Definition: DNA_ID.h:368
struct StructRNA * type
Definition: RNA_types.h:37
struct TreeElement * parent
ListBase subtree
TreeStoreElem * store_elem
const char * name
struct TreeElement * next
IDOverrideLibraryPropertyOperation * operation
PointerRNA * ptr
Definition: wm_files.c:3480