All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
listEditImpl.h
1 //
2 // Copyright 2019 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef USD_LISTEDITIMPL_H
25 #define USD_LISTEDITIMPL_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/usd/usd/api.h"
29 #include "pxr/usd/usd/common.h"
30 #include "pxr/usd/usd/prim.h"
31 #include "pxr/usd/usd/stage.h"
32 #include "pxr/usd/usd/valueUtils.h"
33 
34 PXR_NAMESPACE_OPEN_SCOPE
35 
36 // Non templated base class to namespace the overloading of _TranslatePath on
37 // the list item type.
38 class Usd_ListEditImplBase
39 {
40 protected:
41  // Generic path translation for the list edit types.
42  static bool
43  _TranslatePath(SdfPath *path, const UsdEditTarget& editTarget)
44  {
45  if (path->IsEmpty()) {
46  TF_CODING_ERROR("Invalid empty path");
47  return false;
48  }
49 
50  // Root prim paths for all list edit types aren't expected to be
51  // mappable across non-local edit targets, so we can just use the given
52  // path as-is.
53  if (path->IsRootPrimPath()) {
54  return true;
55  }
56 
57  const SdfPath mappedPath = editTarget.MapToSpecPath(*path);
58  if (mappedPath.IsEmpty()) {
60  "Cannot map <%s> to current edit target.", path->GetText());
61  return false;
62  }
63 
64  // If the edit target points inside a variant, the mapped path may
65  // contain a variant selection. We need to strip this out, since
66  // paths for these purposes may not contain variant selections.
67  *path = mappedPath.StripAllVariantSelections();
68  return true;
69  }
70 
71  // Special path translation for references and payloads
72  template <typename RefOrPayloadType>
73  static bool
74  _TranslatePath(RefOrPayloadType* refOrPayload,
75  const UsdEditTarget& editTarget)
76  {
77  // We do not map prim paths across the edit target for non-internal
78  // references or payloads, as these paths are supposed to be in the
79  // namespace of the layer stack.
80  if (!refOrPayload->GetAssetPath().empty()) {
81  return true;
82  }
83 
84  // The generic _TranslatePath errors for empty paths as those are
85  // invalid for specializes and inherits. However an empty prim path
86  // is find for references and payloads.
87  SdfPath path = refOrPayload->GetPrimPath();
88  if (path.IsEmpty()) {
89  return true;
90  }
91 
92  // Translate the path and update the reference or payload.
93  if (!_TranslatePath(&path, editTarget)) {
94  return false;
95  }
96  refOrPayload->SetPrimPath(path);
97  return true;
98  }
99 };
100 
101 // Templated implementation of the edit operations provided by UsdRefereneces
102 // and UsdPayloads. Editing payloads and references is identical outside of
103 // their type.
104 template <class UsdListEditorType, class ListOpProxyType>
105 struct Usd_ListEditImpl : public Usd_ListEditImplBase
106 {
107  using ListOpValueType = typename ListOpProxyType::value_type;
108  using ListOpValueVector = typename ListOpProxyType::value_vector_type;
109 
110  static bool Add(const UsdListEditorType &editor,
111  const ListOpValueType& itemIn,
112  UsdListPosition position)
113  {
114  const UsdPrim &prim = editor.GetPrim();
115 
116  if (!prim) {
117  TF_CODING_ERROR("Invalid prim");
118  return false;
119  }
120 
121  ListOpValueType item = itemIn;
122  if (!_TranslatePath(&item, prim.GetStage()->GetEditTarget())) {
123  return false;
124  }
125 
126  SdfChangeBlock block;
127  bool success = false;
128  {
129  TfErrorMark mark;
130  if (ListOpProxyType listEditor = _GetListEditor(prim)) {
131  Usd_InsertListItem(listEditor, item, position);
132  // mark *should* contain only errors from adding the item,
133  // NOT any recomposition errors, because the
134  // SdfChangeBlock handily defers composition till after we
135  // leave this scope.
136  success = mark.IsClean();
137  }
138  }
139  return success;
140  }
141 
142  static bool Remove(const UsdListEditorType &editor,
143  const ListOpValueType& itemIn)
144  {
145  const UsdPrim &prim = editor.GetPrim();
146 
147  if (!prim) {
148  TF_CODING_ERROR("Invalid prim");
149  return false;
150  }
151 
152  ListOpValueType item = itemIn;
153  if (!_TranslatePath(&item, prim.GetStage()->GetEditTarget())) {
154  return false;
155  }
156 
157  SdfChangeBlock block;
158  TfErrorMark mark;
159  bool success = false;
160 
161  if (ListOpProxyType listEditor = _GetListEditor(prim)) {
162  listEditor.Remove(item);
163  success = mark.IsClean();
164  }
165  mark.Clear();
166  return success;
167  }
168 
169  static bool Clear(const UsdListEditorType &editor)
170  {
171  const UsdPrim &prim = editor.GetPrim();
172 
173  if (!prim) {
174  TF_CODING_ERROR("Invalid prim");
175  return false;
176  }
177 
178  SdfChangeBlock block;
179  TfErrorMark mark;
180  bool success = false;
181 
182  if (ListOpProxyType listEditor = _GetListEditor(prim)) {
183  success = listEditor.ClearEdits() && mark.IsClean();
184  }
185  mark.Clear();
186  return success;
187  }
188 
189  static bool Set(const UsdListEditorType &editor,
190  const ListOpValueVector& itemsIn)
191  {
192  const UsdPrim &prim = editor.GetPrim();
193 
194  if (!prim) {
195  TF_CODING_ERROR("Invalid prim");
196  return false;
197  }
198 
199  const UsdEditTarget& editTarget = prim.GetStage()->GetEditTarget();
200 
201  TfErrorMark mark;
202 
203  ListOpValueVector items;
204  items.reserve(itemsIn.size());
205  for (ListOpValueType item : itemsIn) {
206  if (_TranslatePath(&item, editTarget)) {
207  items.push_back(item);
208  }
209  }
210 
211  if (!mark.IsClean()) {
212  return false;
213  }
214 
215  SdfChangeBlock block;
216  if (ListOpProxyType listEditor = _GetListEditor(prim)) {
217  // There's a specific semantic meaning to setting the list op to
218  // an empty list which is to make the list explicitly
219  // empty. We have to handle this case specifically as setting the
220  // the list edit proxy's explicit items to an empty vector is a
221  // no-op when the list op is not currently explicit.
222  if (items.empty()) {
223  listEditor.ClearEditsAndMakeExplicit();
224  } else {
225  listEditor.GetExplicitItems() = items;
226  }
227  }
228 
229  bool success = mark.IsClean();
230  mark.Clear();
231  return success;
232  }
233 
234 private:
235  static ListOpProxyType
236  _GetListEditor(const UsdPrim &prim)
237  {
238  if (!TF_VERIFY(prim)) {
239  return ListOpProxyType();
240  }
241 
242  SdfPrimSpecHandle spec =
243  prim.GetStage()->_CreatePrimSpecForEditing(prim);
244 
245  if (!spec) {
246  return ListOpProxyType();
247  }
248 
249  return _GetListEditorForSpec(spec);
250  }
251 
252  // This is purposefully not defined here as this is needs to be specialized
253  // by UsdPayloads, UsdReferences, etc. The implementation is defined in
254  // their cpp files.
255  static ListOpProxyType
256  _GetListEditorForSpec(const SdfPrimSpecHandle &spec);
257 };
258 
259 PXR_NAMESPACE_CLOSE_SCOPE
260 
261 #endif // USD_LISTEDITIMPL_H
bool Clear() const
Remove all errors in this mark from the error system.
Definition: errorMark.h:109
USD_API UsdStageWeakPtr GetStage() const
Return the stage that owns the object, and to whose state and lifetime this object&#39;s validity is tied...
UsdListPosition
Specifies a position to add items to lists.
Definition: common.h:110
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:87
SDF_API SdfPath GetPrimPath() const
Creates a path by stripping all relational attributes, targets, properties, and variant selections fr...
DANGER DANGER DANGER
Definition: changeBlock.h:72
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:289
UsdPrim GetPrim() const
Return this object if it is a prim, otherwise return this object&#39;s nearest owning prim...
Definition: prim.h:1644
Class used to record the end of the error-list.
Definition: errorMark.h:66
Defines a mapping from scene graph paths to Sdf spec paths in a SdfLayer where edits should be direct...
Definition: editTarget.h:78
UsdPrim is the sole persistent scenegraph object on a UsdStage, and is the embodiment of a &quot;Prim&quot; as ...
Definition: prim.h:131
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:287
USD_API SdfPath MapToSpecPath(const SdfPath &scenePath) const
Map the provided scenePath into a SdfSpec path for the EditTarget&#39;s layer, according to the EditTarge...
bool IsClean() const
Return true if no new errors were posted in this thread since the last call to SetMark(), false otherwise.
Definition: errorMark.h:99
SDF_API bool IsRootPrimPath() const
Returns whether the path identifies a root prim.
bool IsEmpty() const noexcept
Returns true if this is the empty path (SdfPath::EmptyPath()).
Definition: path.h:409
SDF_API const char * GetText() const
Returns the string representation of this path as a c string.
SDF_API SdfPath StripAllVariantSelections() const
Create a path by stripping all variant selections from all components of this path, leaving a path with no embedded variant selections.