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 PXR_USD_USD_LIST_EDIT_IMPL_H 25 #define PXR_USD_USD_LIST_EDIT_IMPL_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 _TranslatePath(SdfPath * path,const UsdEditTarget & editTarget)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()) { 59 TF_CODING_ERROR( 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 _TranslatePath(RefOrPayloadType * refOrPayload,const UsdEditTarget & editTarget)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 AddUsd_ListEditImpl110 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 RemoveUsd_ListEditImpl142 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 ClearUsd_ListEditImpl169 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 SetUsd_ListEditImpl189 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 _GetListEditorUsd_ListEditImpl236 _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 // PXR_USD_USD_LIST_EDIT_IMPL_H 262