Design View: drop the undo blocks around writes that mint nothing; the membership actions own theirs
Flag writes are silent from a timer, so a block around a reapply or the reconcile repair could only manufacture a point. The real switch's span is unchanged.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#include "shell/panel/panel_window.h" // bankPanelInvalidate — footer toggle repaint
|
||||
#include "shell/view/view.h" // applyMode (D2 shell)
|
||||
#include "shell/view/view_lanes.h" // mintManagedLanes
|
||||
#include "shell/view/view_park.h" // kViewUndoMask
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_CountSelectedTracks
|
||||
@@ -201,40 +202,76 @@ void doActivateMode(const std::string& modeId) {
|
||||
bankPanelInvalidate(); // repaint the footer [Arrange|Design] toggle immediately
|
||||
}
|
||||
|
||||
// The block each membership-edit action below owns. The reapply they drive opens none
|
||||
// of its own — nothing it writes mints a point unaided — so a user-fired tag stays one
|
||||
// Ctrl-Z only because the ACTION spans it, and the label names the act the user
|
||||
// actually fired rather than the reapply it happened to trigger. Every one of them
|
||||
// closes the block before persistViewState, which can pop a modal Save-As. Stack RAII
|
||||
// so a throw between the two calls cannot leave the block open in the user's project.
|
||||
class ActionUndoBlock {
|
||||
public:
|
||||
ActionUndoBlock(std::string label, int mask) : label_(std::move(label)), mask_(mask) {
|
||||
Undo_BeginBlock2(nullptr);
|
||||
}
|
||||
~ActionUndoBlock() { Undo_EndBlock2(nullptr, label_.c_str(), mask_); }
|
||||
ActionUndoBlock(const ActionUndoBlock&) = delete;
|
||||
ActionUndoBlock& operator=(const ActionUndoBlock&) = delete;
|
||||
|
||||
private:
|
||||
std::string label_;
|
||||
int mask_;
|
||||
};
|
||||
|
||||
// tag() replaces any prior single-mode membership (a leaf lives in one mode; the
|
||||
// cross-mode case is show-both).
|
||||
void doTag(const std::string& modeId) {
|
||||
for (const std::string& g : selectedTrackGuids())
|
||||
g_session->view().membership().tag(g, modeId);
|
||||
reapplyActiveMode();
|
||||
const std::vector<std::string> guids = selectedTrackGuids();
|
||||
if (guids.empty()) return; // nothing selected — no act, no undo point (cf. doMoveItems)
|
||||
|
||||
{
|
||||
ActionUndoBlock undo("ReaSampler: tag selected tracks -> " + modeId, kViewUndoMask);
|
||||
for (const std::string& g : guids) g_session->view().membership().tag(g, modeId);
|
||||
reapplyActiveMode();
|
||||
}
|
||||
persistViewState(PersistScope::Full);
|
||||
}
|
||||
|
||||
// Shared body behind "Untag selected" and "Tag -> Arrange" — Arrange is the absence
|
||||
// of a tag, so the two actions are the same act.
|
||||
void doUntag() {
|
||||
for (const std::string& g : selectedTrackGuids())
|
||||
g_session->view().membership().untag(g);
|
||||
reapplyActiveMode();
|
||||
const std::vector<std::string> guids = selectedTrackGuids();
|
||||
if (guids.empty()) return;
|
||||
|
||||
{
|
||||
ActionUndoBlock undo("ReaSampler: untag selected tracks", kViewUndoMask);
|
||||
for (const std::string& g : guids) g_session->view().membership().untag(g);
|
||||
reapplyActiveMode();
|
||||
}
|
||||
persistViewState(PersistScope::Full);
|
||||
}
|
||||
|
||||
// Flips each track's pin independently — the honest semantics of a toggle on a
|
||||
// multi-selection (a mixed selection converges toward uniform only if it already was).
|
||||
void doShowBoth() {
|
||||
MembershipIndex& m = g_session->view().membership();
|
||||
for (const std::string& g : selectedTrackGuids())
|
||||
m.setShowBoth(g, !m.isShowBoth(g));
|
||||
reapplyActiveMode();
|
||||
const std::vector<std::string> guids = selectedTrackGuids();
|
||||
if (guids.empty()) return;
|
||||
|
||||
{
|
||||
ActionUndoBlock undo("ReaSampler: toggle show-both on selected tracks", kViewUndoMask);
|
||||
MembershipIndex& m = g_session->view().membership();
|
||||
for (const std::string& g : guids) m.setShowBoth(g, !m.isShowBoth(g));
|
||||
reapplyActiveMode();
|
||||
}
|
||||
persistViewState(PersistScope::Full);
|
||||
}
|
||||
|
||||
// Retag the current ITEM selection to `targetMode` (empty => untag -> Arrange
|
||||
// default). planItemRetag decides which items to retag (manual-lane items are
|
||||
// EXEMPT), upholding the managed-lanes-only invariant. Wrapped in ONE Undo block.
|
||||
// EXEMPT), upholding the managed-lanes-only invariant.
|
||||
//
|
||||
// UNDO/SAVE ordering: persistViewState may pop a Save-As dialog, which must NOT sit
|
||||
// inside the Undo block, so we close the block first, then persist.
|
||||
// The mask stays UNDO_STATE_ALL rather than the view mask the three track actions
|
||||
// close on: this one can mint lanes, and the fixed-lane properties are only ASSUMED to
|
||||
// ride UNDO_STATE_ITEMS (see kViewUndoMask).
|
||||
void doMoveItems(const std::string& targetMode) {
|
||||
const std::vector<RetagItem> selected = selectedRetagItems();
|
||||
const std::vector<ItemRetagOp> ops = planItemRetag(selected, targetMode);
|
||||
@@ -242,21 +279,20 @@ void doMoveItems(const std::string& targetMode) {
|
||||
|
||||
MembershipIndex& membership = g_session->view().membership();
|
||||
|
||||
Undo_BeginBlock2(nullptr);
|
||||
for (const ItemRetagOp& op : ops) {
|
||||
if (op.untag) membership.untag(op.guid);
|
||||
else membership.tag(op.guid, op.modeId);
|
||||
{
|
||||
ActionUndoBlock undo(targetMode.empty()
|
||||
? std::string("ReaSampler: untag selected items")
|
||||
: "ReaSampler: move selected items -> " + targetMode,
|
||||
-1);
|
||||
for (const ItemRetagOp& op : ops) {
|
||||
if (op.untag) membership.untag(op.guid);
|
||||
else membership.tag(op.guid, op.modeId);
|
||||
}
|
||||
// Re-drive the same minting/apply path auto-tag uses: mint/split lanes for any
|
||||
// track whose items now span modes, then reassert active-mode lane visibility.
|
||||
mintManagedLanes(g_session->view(), nullptr);
|
||||
reapplyActiveMode();
|
||||
}
|
||||
// Re-drive the same minting/apply path auto-tag uses: mint/split lanes for any
|
||||
// track whose items now span modes, then reassert active-mode lane visibility.
|
||||
mintManagedLanes(g_session->view(), nullptr);
|
||||
reapplyActiveMode();
|
||||
|
||||
const std::string label =
|
||||
targetMode.empty()
|
||||
? std::string("ReaSampler: untag selected items")
|
||||
: std::string("ReaSampler: move selected items -> ") + targetMode;
|
||||
Undo_EndBlock2(nullptr, label.c_str(), -1);
|
||||
|
||||
persistViewState(PersistScope::Full);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user