Reconcile orphaned Design View snapshots on track delete
Add ViewModeModel::reconcile(liveGuids) to prune snapshots of deleted tracks; wire it into applyMode before planning. Keep membership so undo-delete (same GUID) preserves the Design tag.
This commit is contained in:
@@ -144,6 +144,16 @@ bool applyMode(ViewModeModel& model, const std::string& targetModeId, ReaProject
|
||||
std::vector<TrackFolderEntry> entries = readFolderEntries(proj, handleByGuid);
|
||||
FolderTree tree = buildFolderTree(entries);
|
||||
|
||||
// Reconcile orphaned model state BEFORE planning: prune snapshots whose track was
|
||||
// deleted from the project (its GUID no longer appears in the live enumeration).
|
||||
// handleByGuid holds every currently-enumerated track GUID, so its keys are the
|
||||
// authoritative live set. Membership is intentionally NOT pruned (undo-delete
|
||||
// restores the same GUID — see ViewModeModel::reconcile). Because reapply-on-load
|
||||
// routes through applyMode, this also reconciles on project open.
|
||||
std::set<std::string> liveGuids;
|
||||
for (const auto& kv : handleByGuid) liveGuids.insert(kv.first);
|
||||
model.reconcile(liveGuids);
|
||||
|
||||
TogglePlan plan = model.planToggle(tree, targetModeId);
|
||||
|
||||
Undo_BeginBlock2(proj);
|
||||
|
||||
@@ -160,6 +160,21 @@ const TrackSnapshot* ViewModeModel::snapshot(const std::string& guid) const {
|
||||
return it == snapshots_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
std::size_t ViewModeModel::reconcile(const std::set<std::string>& liveGuids) {
|
||||
// Prune snapshots for GUIDs the project no longer contains (see header for the
|
||||
// deliberate snapshot-yes / membership-no asymmetry and the undo-delete rationale).
|
||||
std::size_t removed = 0;
|
||||
for (auto it = snapshots_.begin(); it != snapshots_.end();) {
|
||||
if (liveGuids.count(it->first) == 0) {
|
||||
it = snapshots_.erase(it);
|
||||
++removed;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
bool ViewModeModel::leafBelongsToMode(const std::string& guid, const std::string& modeId) const {
|
||||
const Membership* m = membership_.query(guid);
|
||||
if (!m) return modeId == kArrangeModeId; // untagged ⇒ Arrange default
|
||||
|
||||
@@ -267,6 +267,26 @@ public:
|
||||
const TrackSnapshot* snapshot(const std::string& guid) const;
|
||||
const std::map<std::string, TrackSnapshot>& snapshots() const { return snapshots_; }
|
||||
|
||||
// Prunes orphaned per-track state: drops every snapshot whose GUID is NOT in
|
||||
// `liveGuids` (the set of GUIDs the shell currently enumerates from the project).
|
||||
// Returns the number of snapshots removed. The shell calls this before planning a
|
||||
// toggle; because reapply-on-load also routes through the shell's applyMode, this
|
||||
// reconciles on project open too.
|
||||
//
|
||||
// Why snapshots and NOT membership: a parked track's snapshot is dead weight once
|
||||
// the track is deleted — it can never be restored, and if REAPER reuses that GUID
|
||||
// for a different track a stale snapshot would drive an INCORRECT restore. So it
|
||||
// must be pruned. Membership is deliberately KEPT: REAPER's undo of a track delete
|
||||
// restores the SAME GUID, so dropping the Design tag on delete would silently lose
|
||||
// it on undo-delete. Keeping membership means an undone delete brings the track
|
||||
// back correctly tagged and it re-snapshots + re-parks cleanly on the next toggle.
|
||||
// A genuinely-deleted-and-never-restored track leaves only a tiny dormant
|
||||
// membership entry — acceptable, and far better than losing tags on undo. Folder
|
||||
// RESTRUCTURE (moving tracks without deleting) is already self-healing: the tree is
|
||||
// rebuilt from I_FOLDERDEPTH every toggle, so a restructure leaves every GUID live
|
||||
// and reconcile is a no-op over it. This handles DELETION specifically.
|
||||
std::size_t reconcile(const std::set<std::string>& liveGuids);
|
||||
|
||||
// Does `guid` belong to `modeId`? A leaf belongs if it is tagged into modeId,
|
||||
// is show-both (belongs everywhere), or is untagged and modeId is Arrange (the
|
||||
// default). Parent derivation is NOT applied here — this is the LEAF rule; use
|
||||
|
||||
Reference in New Issue
Block a user