tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_EditItem.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14EditItem::EditItem (EditItemID id, Edit& ed)
15 : edit (ed), itemID (id)
16{
17}
18
19//==============================================================================
20EditItemID EditItemID::fromVar (const juce::var& v)
21{
22 return fromRawID ((uint64_t) static_cast<juce::int64> (v));
23}
24
25EditItemID EditItemID::fromString (const juce::String& s)
26{
27 return fromRawID ((uint64_t) s.getLargeIntValue());
28}
29
30EditItemID EditItemID::fromID (const juce::ValueTree& v)
31{
32 return EditItemID::fromVar (v.getProperty (IDs::id));
33}
34
35void EditItemID::writeID (juce::ValueTree& v, juce::UndoManager* um) const
36{
37 v.setProperty (IDs::id, *this, um);
38}
39
40EditItemID EditItemID::fromRawID (uint64_t raw)
41{
42 EditItemID i;
43 i.itemID = raw;
44 return i;
45}
46
47static inline bool isSorted (const std::vector<EditItemID>& ids)
48{
49 uint64_t last = 0;
50
51 for (auto id : ids)
52 {
53 auto i = id.getRawID();
54
55 if (i < last)
56 return false;
57
58 last = i;
59 }
60
61 return true;
62}
63
64EditItemID EditItemID::findFirstIDNotIn (const std::vector<EditItemID>& existingIDs, uint64_t rawID)
65{
66 jassert (isSorted (existingIDs)); // the vector must be sorted
67
68 for (auto existing : existingIDs)
69 {
70 auto i = existing.getRawID();
71
72 if (i > rawID)
73 break;
74
75 ++rawID;
76 }
77
78 return fromRawID (rawID);
79}
80
81EditItemID EditItemID::fromProperty (const juce::ValueTree& v, const juce::Identifier& prop)
82{
83 return fromVar (v.getProperty (prop));
84}
85
86EditItemID EditItemID::fromXML (const juce::XmlElement& xml, const char* attributeName)
87{
88 return fromVar (xml.getStringAttribute (attributeName));
89}
90
91EditItemID EditItemID::fromXML (const juce::XmlElement& xml, const juce::Identifier& attributeName)
92{
93 return fromVar (xml.getStringAttribute (attributeName));
94}
95
96void EditItemID::setProperty (juce::ValueTree& v, const juce::Identifier& prop, juce::UndoManager* um) const
97{
98 v.setProperty (prop, toVar(), um);
99}
100
101void EditItemID::setXML (juce::XmlElement& xml, const juce::Identifier& attributeName) const
102{
103 xml.setAttribute (attributeName, toString());
104}
105
106void EditItemID::setXML (juce::XmlElement& xml, const char* attributeName) const
107{
108 xml.setAttribute (attributeName, toString());
109}
110
111juce::var EditItemID::toVar() const
112{
113 return juce::var (static_cast<juce::int64> (itemID));
114}
115
116juce::String EditItemID::toString() const
117{
118 return toVar().toString();
119}
120
121EditItemID EditItemID::readOrCreateNewID (Edit& edit, const juce::ValueTree& v)
122{
123 auto i = fromID (v);
124
125 if (i.isValid())
126 return i;
127
128 i = edit.createNewItemID();
129 juce::ValueTree localTree (v);
130 i.writeID (localTree, nullptr);
131 return i;
132}
133
134juce::Array<EditItemID> EditItemID::parseStringList (const juce::String& list)
135{
137 auto items = juce::StringArray::fromTokens (list, ",|", {});
138 trackIDs.ensureStorageAllocated (items.size());
139
140 for (auto& s : items)
141 trackIDs.add (fromVar (s));
142
143 return trackIDs;
144}
145
146juce::String EditItemID::listToString (const juce::Array<EditItemID>& items)
147{
148 if (items.isEmpty())
149 return {};
150
151 if (items.size() == 1)
152 return items.getFirst().toString();
153
155 ids.ensureStorageAllocated (items.size());
156
157 for (auto& i : items)
158 ids.add (i.toString());
159
160 return ids.joinIntoString (",");
161}
162
163//==============================================================================
165{
166 static bool isIDDeclaration (juce::StringRef att)
167 {
168 return att == IDs::id || att == IDs::groupID;
169 }
170
171 static bool isIDReference (const juce::Identifier& parentType, juce::StringRef att)
172 {
173 for (auto p : { IDs::currentAutoParamPluginID, IDs::currentAutoParamTag,
174 IDs::targetID, IDs::sourceTrack, IDs::src, IDs::dst,
175 IDs::pluginID, IDs::rackType, IDs::paramID })
176 if (p == att)
177 return true;
178
179 if (att == IDs::source && ! Clip::isClipState (parentType))
180 return true;
181
182 if (att == IDs::track && parentType == IDs::COMPSECTION)
183 return true;
184
185 return false;
186 }
187
188 static bool isIDRefOrDecl (const juce::XmlElement& xml, juce::StringRef att)
189 {
190 return isIDDeclaration (att) || isIDReference (xml.getTagName(), att);
191 }
192
193 static bool isIDRefOrDecl (const juce::ValueTree& v, juce::StringRef att)
194 {
195 return isIDDeclaration (att) || isIDReference (v.getType(), att);
196 }
197
198 static bool isIDList (juce::StringRef att)
199 {
200 return att == IDs::hiddenClips || att == IDs::lockedClips
201 || att == IDs::ghostTracks || att == IDs::renderTracks;
202 }
203
204 template <typename Visitor>
205 static void visitAllIDDecls (const juce::XmlElement& xml, Visitor&& visitor)
206 {
207 for (int i = 0; i < xml.getNumAttributes(); ++i)
208 if (isIDDeclaration (xml.getAttributeName (i)))
209 visitor (xml.getAttributeValue (i));
210
211 for (auto e : xml.getChildIterator())
212 visitAllIDDecls (*e, visitor);
213 }
214
215 template <typename Visitor>
216 static void visitAllIDDecls (const juce::ValueTree& v, Visitor&& visitor)
217 {
218 for (int i = 0; i < v.getNumProperties(); ++i)
219 {
220 auto propName = v.getPropertyName (i);
221
222 if (isIDDeclaration (propName))
223 visitor (v.getProperty (propName));
224 }
225
226 for (const auto& child : v)
227 visitAllIDDecls (child, visitor);
228 }
229
231
232 static juce::String remapIDList (const juce::String& list, const StringToIDMap& newIDsToApply)
233 {
234 juce::Array<EditItemID> newItemIDs;
235
236 for (auto oldID : juce::StringArray::fromTokens (list, "|,", {}))
237 {
238 if (oldID.isNotEmpty())
239 {
240 auto newID = newIDsToApply.find (oldID);
241
242 if (newID != newIDsToApply.end())
243 newItemIDs.add (newID->second);
244 }
245 }
246
247 return EditItemID::listToString (newItemIDs);
248 }
249
250 static void applyNewIDs (juce::XmlElement& xml, const StringToIDMap& newIDsToApply)
251 {
252 for (int i = 0; i < xml.getNumAttributes(); ++i)
253 {
254 auto attName = xml.getAttributeName (i);
255
256 if (isIDRefOrDecl (xml, attName))
257 {
258 auto oldID = xml.getAttributeValue (i);
259
260 if (oldID.isNotEmpty())
261 {
262 auto newID = newIDsToApply.find (oldID);
263
264 if (newID != newIDsToApply.end())
265 xml.setAttribute (attName, newID->second.toString());
266 }
267 }
268 else if (isIDList (attName))
269 {
270 xml.setAttribute (attName, remapIDList (xml.getAttributeValue (i), newIDsToApply));
271 }
272
273 if (EditItemID::applyNewIDsToExternalXML != nullptr)
274 EditItemID::applyNewIDsToExternalXML (xml, attName, newIDsToApply);
275 }
276
277 for (auto e : xml.getChildIterator())
278 applyNewIDs (*e, newIDsToApply);
279 }
280
281 static void applyNewIDs (juce::ValueTree& v, const StringToIDMap& newIDsToApply, juce::UndoManager* um)
282 {
283 for (int i = 0; i < v.getNumProperties(); ++i)
284 {
285 auto propName = v.getPropertyName (i);
286
287 if (isIDRefOrDecl (v, propName))
288 {
289 auto oldID = v.getProperty (propName).toString();
290
291 if (oldID.isNotEmpty())
292 {
293 auto newID = newIDsToApply.find (oldID);
294
295 if (newID != newIDsToApply.end())
296 {
297 v.setProperty (propName, newID->second, um);
298 }
299 else
300 {
301 DBG ("Dangling ID found: " << oldID);
302 }
303 }
304 }
305 else if (isIDList (propName))
306 {
307 v.setProperty (propName, remapIDList (v.getProperty (propName), newIDsToApply), um);
308 }
309
310 if (EditItemID::applyNewIDsToExternalValueTree)
311 EditItemID::applyNewIDsToExternalValueTree (v, propName, newIDsToApply, um);
312 }
313
314 for (auto child : v)
315 applyNewIDs (child, newIDsToApply, um);
316 }
317
318 static void addItemsToReturnedMap (const StringToIDMap& newIDs, EditItemID::IDMap* result)
319 {
320 if (result != nullptr)
321 {
322 for (auto& i : newIDs)
323 {
324 auto itemID = EditItemID::fromString (i.first);
325
326 if (itemID.isValid())
327 (*result)[itemID] = i.second;
328 else
329 jassertfalse; // we're trying to get out a list of ID -> ID, but some
330 // of the original IDs weren't valid. Could be that they were
331 // legacy string IDs?
332 }
333 }
334 }
335};
336
337std::vector<EditItemID> EditItemID::findAllIDs (const juce::XmlElement& xml)
338{
340
341 IDRemapping::visitAllIDDecls (xml, [&] (const juce::String& oldID)
342 {
343 auto i = EditItemID::fromString (oldID);
344
345 if (i.isValid())
346 ids.push_back (i);
347 });
348
349 return ids;
350}
351
352std::vector<EditItemID> EditItemID::findAllIDs (const juce::ValueTree& v)
353{
355
356 IDRemapping::visitAllIDDecls (v, [&] (const juce::var& oldID)
357 {
358 auto i = EditItemID::fromVar (oldID);
359
360 if (i.isValid())
361 ids.push_back (i);
362 });
363
364 return ids;
365}
366
367void EditItemID::remapIDs (juce::XmlElement& xml, std::function<EditItemID()> createNewID, IDMap* remappedIDs)
368{
369 IDRemapping::StringToIDMap newIDs;
370
371 IDRemapping::visitAllIDDecls (xml, [&] (const juce::String& oldID)
372 {
373 if (oldID.isNotEmpty())
374 {
375 auto& remapped = newIDs[oldID];
376
377 if (remapped.isInvalid())
378 {
379 remapped = createNewID();
380 //DBG ("Remapping ID: " << oldID << " " << remapped.toString());
381 }
382 }
383 });
384
385 IDRemapping::applyNewIDs (xml, newIDs);
386 IDRemapping::addItemsToReturnedMap (newIDs, remappedIDs);
387}
388
389void EditItemID::remapIDs (juce::ValueTree& v, juce::UndoManager* um,
390 std::function<EditItemID()> createNewID, IDMap* remappedIDs)
391{
392 IDRemapping::StringToIDMap newIDs;
393
394 IDRemapping::visitAllIDDecls (v, [&] (const juce::var& oldID)
395 {
396 auto oldIDString = oldID.toString();
397
398 if (oldIDString.isNotEmpty())
399 {
400 auto& remapped = newIDs[oldIDString];
401
402 if (remapped.isInvalid() && remappedIDs != nullptr && remappedIDs->contains (EditItemID::fromString (oldIDString)))
403 {
404 remapped = remappedIDs->at (EditItemID::fromString (oldIDString));
405 //DBG ("Remapping ID: " << oldIDString << " " << remapped.toString());
406 }
407
408 if (remapped.isInvalid())
409 {
410 remapped = createNewID();
411 //DBG ("Remapping ID: " << oldIDString << " " << remapped.toString());
412 }
413 }
414 });
415
416 IDRemapping::applyNewIDs (v, newIDs, um);
417 IDRemapping::addItemsToReturnedMap (newIDs, remappedIDs);
418}
419
421{
422 MultipleNewIDGenerator (Edit& ed, EditItemID::IDMap* remappedIDs) : edit (ed)
423 {
424 if (remappedIDs != nullptr)
425 {
426 alreadyUsed.reserve (alreadyUsed.size() + remappedIDs->size());
427
428 for (auto& i : *remappedIDs)
429 alreadyUsed.push_back (i.second);
430 }
431 }
432
433 EditItemID operator()()
434 {
435 auto newID = edit.createNewItemID (alreadyUsed);
436 alreadyUsed.push_back (newID);
437 return newID;
438 }
439
440 Edit& edit;
441 std::vector<EditItemID> alreadyUsed;
442};
443
444void EditItemID::remapIDs (juce::XmlElement& xml, Edit& ed, IDMap* remappedIDs)
445{
446 MultipleNewIDGenerator idCreator (ed, remappedIDs);
447 remapIDs (xml, idCreator, remappedIDs);
448}
449
450void EditItemID::remapIDs (juce::ValueTree& v, juce::UndoManager* um, Edit& ed, IDMap* remappedIDs)
451{
452 MultipleNewIDGenerator idCreator (ed, remappedIDs);
453 remapIDs (v, um, idCreator, remappedIDs);
454}
455
456//==============================================================================
457std::function<void (juce::ValueTree&, const juce::Identifier&, const std::map<juce::String, EditItemID>&, juce::UndoManager*)> EditItemID::applyNewIDsToExternalValueTree;
458std::function<void (juce::XmlElement&, const juce::String&, const std::map<juce::String, EditItemID>&)> EditItemID::applyNewIDsToExternalXML;
459
460}} // namespace tracktion { inline namespace engine
bool isEmpty() const noexcept
void ensureStorageAllocated(int minNumElements)
int size() const noexcept
ElementType getFirst() const noexcept
void add(const ElementType &newElement)
const String & toString() const noexcept
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
void ensureStorageAllocated(int minNumElements)
int64 getLargeIntValue() const noexcept
bool isNotEmpty() const noexcept
const String & getAttributeValue(int attributeIndex) const noexcept
const String & getAttributeName(int attributeIndex) const noexcept
Iterator< GetNextElement > getChildIterator() const
const String & getTagName() const noexcept
int getNumAttributes() const noexcept
const String & getStringAttribute(StringRef attributeName) const noexcept
void setAttribute(const Identifier &attributeName, const String &newValue)
The Tracktion Edit class!
T end(T... args)
T find(T... args)
#define jassert(expression)
#define DBG(textToWrite)
#define jassertfalse
long long int64
T push_back(T... args)
T size(T... args)
typedef uint64_t
ID for objects of type EditElement - e.g.