@@ -82,6 +82,8 @@ class PropertySetAction : public UndoableAction
8282 var newValue, oldValue;
8383};
8484
85+ // ==============================================================================
86+
8587class PropertyRemoveAction : public UndoableAction
8688{
8789public:
@@ -121,6 +123,8 @@ class PropertyRemoveAction : public UndoableAction
121123 var oldValue;
122124};
123125
126+ // ==============================================================================
127+
124128class RemoveAllPropertiesAction : public UndoableAction
125129{
126130public:
@@ -141,13 +145,9 @@ class RemoveAllPropertiesAction : public UndoableAction
141145 return false ;
142146
143147 if (state == UndoableActionState::Redo)
144- {
145148 dataTree.object ->properties .clear ();
146- }
147- else // Undo
148- {
149+ else
149150 dataTree.object ->properties = oldProperties;
150- }
151151
152152 for (int i = 0 ; i < oldProperties.size (); ++i)
153153 dataTree.object ->sendPropertyChangeMessage (oldProperties.getName (i));
@@ -160,6 +160,8 @@ class RemoveAllPropertiesAction : public UndoableAction
160160 NamedValueSet oldProperties;
161161};
162162
163+ // ==============================================================================
164+
163165class AddChildAction : public UndoableAction
164166{
165167public:
@@ -182,7 +184,6 @@ class AddChildAction : public UndoableAction
182184
183185 if (state == UndoableActionState::Redo)
184186 {
185- // Capture the child's current parent (if any) for undo
186187 if (auto currentParent = childTree.object ->parent .lock ())
187188 {
188189 previousParent = DataTree (currentParent);
@@ -203,18 +204,16 @@ class AddChildAction : public UndoableAction
203204
204205 parentTree.object ->sendChildAddedMessage (childTree);
205206 }
206- else // Undo
207+ else
207208 {
208209 const int childIndex = parentTree.indexOf (childTree);
209210 if (childIndex >= 0 )
210211 {
211212 parentTree.object ->children .erase (parentTree.object ->children .begin () + childIndex);
212213 parentTree.object ->sendChildRemovedMessage (childTree, childIndex);
213-
214- // Restore previous parent
214+
215215 if (previousParent.isValid ())
216216 {
217- // Restore to previous parent at previous index
218217 const int numChildren = static_cast <int > (previousParent.object ->children .size ());
219218 const int actualIndex = (previousIndex < 0 || previousIndex > numChildren) ? numChildren : previousIndex;
220219
@@ -224,7 +223,6 @@ class AddChildAction : public UndoableAction
224223 }
225224 else
226225 {
227- // No previous parent - clear parent reference
228226 childTree.object ->parent .reset ();
229227 }
230228 }
@@ -237,10 +235,12 @@ class AddChildAction : public UndoableAction
237235 DataTree parentTree;
238236 DataTree childTree;
239237 int index;
240- DataTree previousParent; // For undo: the child's parent before this action
241- int previousIndex = -1 ; // For undo: the child's index in previous parent
238+ DataTree previousParent;
239+ int previousIndex = -1 ;
242240};
243241
242+ // ==============================================================================
243+
244244class RemoveChildAction : public UndoableAction
245245{
246246public:
@@ -270,7 +270,7 @@ class RemoveChildAction : public UndoableAction
270270 childTree.object ->parent .reset ();
271271 parentTree.object ->sendChildRemovedMessage (childTree, index);
272272 }
273- else // Undo
273+ else
274274 {
275275 if (childTree.object == nullptr )
276276 return false ;
@@ -292,6 +292,8 @@ class RemoveChildAction : public UndoableAction
292292 int index;
293293};
294294
295+ // ==============================================================================
296+
295297class RemoveAllChildrenAction : public UndoableAction
296298{
297299public:
@@ -321,7 +323,7 @@ class RemoveAllChildrenAction : public UndoableAction
321323 parentTree.object ->sendChildRemovedMessage (children[i], static_cast <int > (i));
322324 }
323325 }
324- else // Undo
326+ else
325327 {
326328 parentTree.object ->children = children;
327329
@@ -340,6 +342,8 @@ class RemoveAllChildrenAction : public UndoableAction
340342 std::vector<DataTree> children;
341343};
342344
345+ // ==============================================================================
346+
343347class MoveChildAction : public UndoableAction
344348{
345349public:
@@ -372,7 +376,7 @@ class MoveChildAction : public UndoableAction
372376
373377 parentTree.object ->sendChildMovedMessage (child, oldIndex, newIndex);
374378 }
375- else // Undo
379+ else
376380 {
377381 auto child = parentTree.object ->children [static_cast <size_t > (newIndex)];
378382 parentTree.object ->children .erase (parentTree.object ->children .begin () + newIndex);
@@ -389,27 +393,19 @@ class MoveChildAction : public UndoableAction
389393 int oldIndex, newIndex;
390394};
391395
392- // ==============================================================================
393-
394- class SimpleTransactionAction : public UndoableAction
396+ class CompoundAction : public UndoableAction
395397{
396398public:
397- SimpleTransactionAction (DataTree tree, const String& desc, const NamedValueSet& origProps, const std::vector<DataTree>& origChildren )
399+ CompoundAction (DataTree tree, const String& desc, std::vector<UndoableAction::Ptr>&& actions )
398400 : dataTree (tree)
399401 , description (desc)
400- , originalProperties (origProps)
401- , originalChildren (origChildren)
402+ , individualActions (std::move (actions))
402403 {
403- if (dataTree.object != nullptr )
404- {
405- currentProperties = dataTree.object ->properties ;
406- currentChildren = dataTree.object ->children ;
407- }
408404 }
409405
410406 bool isValid () const override
411407 {
412- return dataTree.object != nullptr ;
408+ return dataTree.object != nullptr && !individualActions. empty () ;
413409 }
414410
415411 bool perform (UndoableActionState state) override
@@ -418,52 +414,29 @@ class SimpleTransactionAction : public UndoableAction
418414 return false ;
419415
420416 if (state == UndoableActionState::Redo)
421- restoreState (currentProperties, currentChildren);
422- else
423- restoreState (originalProperties, originalChildren);
424-
425- return true ;
426- }
427-
428- private:
429- void restoreState (const NamedValueSet& props, const std::vector<DataTree>& children)
430- {
431- for (const auto & currentChild : dataTree.object ->children )
432417 {
433- bool willBeKept = false ;
434- for (const auto & newChild : children)
418+ for (auto & action : individualActions)
435419 {
436- if (currentChild.object == newChild.object )
437- {
438- willBeKept = true ;
439- break ;
440- }
420+ if (! action->perform (UndoableActionState::Redo))
421+ return false ;
441422 }
442-
443- if (! willBeKept && currentChild.object != nullptr )
444- currentChild.object ->parent .reset ();
445423 }
446-
447- dataTree.object ->properties = props;
448- dataTree.object ->children = children;
449-
450- for (const auto & child : dataTree.object ->children )
424+ else
451425 {
452- if (child.object != nullptr )
453- child.object ->parent = dataTree.object ;
426+ for (auto it = individualActions.rbegin (); it != individualActions.rend (); ++it)
427+ {
428+ if (! (*it)->perform (UndoableActionState::Undo))
429+ return false ;
430+ }
454431 }
455432
456- for (int i = 0 ; i < props.size (); ++i)
457- dataTree.object ->sendPropertyChangeMessage (props.getName (i));
458-
459- for (size_t i = 0 ; i < children.size (); ++i)
460- dataTree.object ->sendChildAddedMessage (children[i]);
433+ return true ;
461434 }
462435
436+ private:
463437 DataTree dataTree;
464438 String description;
465- NamedValueSet originalProperties, currentProperties;
466- std::vector<DataTree> originalChildren, currentChildren;
439+ std::vector<UndoableAction::Ptr> individualActions;
467440};
468441
469442// ==============================================================================
@@ -1198,7 +1171,6 @@ bool DataTree::isEquivalentTo (const DataTree& other) const
11981171}
11991172
12001173// ==============================================================================
1201- // Transaction Implementation
12021174
12031175DataTree::Transaction::Transaction (DataTree& tree, const String& desc, UndoManager* manager)
12041176 : dataTree (tree)
@@ -1258,17 +1230,87 @@ void DataTree::Transaction::commit()
12581230 if (! active || dataTree.object == nullptr )
12591231 return ;
12601232
1261- if (undoManager != nullptr && (! propertyChanges.empty () || ! childChanges.empty ()))
1233+ // Always build individual actions and execute them
1234+ std::vector<UndoableAction::Ptr> actions;
1235+
1236+ // Create property actions that capture current state
1237+ for (const auto & change : propertyChanges)
1238+ {
1239+ switch (change.type )
1240+ {
1241+ case PropertyChange::Set:
1242+ {
1243+ var oldValue = dataTree.hasProperty (change.name ) ? dataTree.getProperty (change.name ) : var::undefined ();
1244+ actions.push_back (new PropertySetAction (dataTree, change.name , change.newValue , oldValue));
1245+ break ;
1246+ }
1247+ case PropertyChange::Remove:
1248+ {
1249+ if (dataTree.hasProperty (change.name ))
1250+ {
1251+ var oldValue = dataTree.getProperty (change.name );
1252+ actions.push_back (new PropertyRemoveAction (dataTree, change.name , oldValue));
1253+ }
1254+ break ;
1255+ }
1256+ case PropertyChange::RemoveAll:
1257+ {
1258+ if (! dataTree.object ->properties .isEmpty ())
1259+ actions.push_back (new RemoveAllPropertiesAction (dataTree, dataTree.object ->properties ));
1260+ break ;
1261+ }
1262+ }
1263+ }
1264+
1265+ // Create child actions that capture current state
1266+ for (const auto & change : childChanges)
12621267 {
1263- // Apply changes first to get final state, then create undo action with before/after states
1264- applyChangesToTree (dataTree, originalProperties, originalChildren, propertyChanges, childChanges);
1268+ switch (change.type )
1269+ {
1270+ case ChildChange::Add:
1271+ {
1272+ actions.push_back (new AddChildAction (dataTree, change.child , change.newIndex ));
1273+ break ;
1274+ }
1275+
1276+ case ChildChange::Remove:
1277+ {
1278+ int childIndex = dataTree.indexOf (change.child );
1279+ if (childIndex >= 0 )
1280+ actions.push_back (new RemoveChildAction (dataTree, change.child , childIndex));
1281+ break ;
1282+ }
1283+
1284+ case ChildChange::RemoveAll:
1285+ {
1286+ if (!dataTree.object ->children .empty ())
1287+ actions.push_back (new RemoveAllChildrenAction (dataTree, dataTree.object ->children ));
1288+ break ;
1289+ }
12651290
1266- // Create a simple action that can restore the original state
1267- undoManager->perform (new SimpleTransactionAction (dataTree, description, originalProperties, originalChildren));
1291+ case ChildChange::Move:
1292+ {
1293+ const int numChildren = static_cast <int > (dataTree.object ->children .size ());
1294+ if (change.oldIndex >= 0 && change.oldIndex < numChildren &&
1295+ change.newIndex >= 0 && change.newIndex < numChildren &&
1296+ change.oldIndex != change.newIndex )
1297+ {
1298+ actions.push_back (new MoveChildAction (dataTree, change.oldIndex , change.newIndex ));
1299+ }
1300+ break ;
1301+ }
1302+ }
1303+ }
1304+
1305+ // If we have undo manager, use compound action for undo/redo
1306+ if (undoManager != nullptr && !actions.empty ())
1307+ {
1308+ undoManager->perform (new CompoundAction (dataTree, description, std::move (actions)));
12681309 }
12691310 else
12701311 {
1271- applyChangesToTree (dataTree, originalProperties, originalChildren, propertyChanges, childChanges);
1312+ for (auto & action : actions)
1313+ action->perform (UndoableActionState::Redo);
12721314 }
12731315
12741316 active = false ;
0 commit comments