feat(actions): batch bank index verbs into single REAPER undo points (R-B)
Wrap each bank verb's persist in Undo_BeginBlock2/EndBlock2 with UNDO_STATE_MISCCFG so one bank op is one Ctrl-Z; ext-state participates in undo per SDK. Rejected/no-op ops open no block.
This commit is contained in:
+36
-7
@@ -455,6 +455,27 @@ gaccel_register_t g_accelBankBanksFull{};
|
|||||||
// follows capture's quiet-persist idiom, a Design-View mutation follows the prompt idiom.
|
// follows capture's quiet-persist idiom, a Design-View mutation follows the prompt idiom.
|
||||||
void persistBook() { g_session->saveToActiveProject(); }
|
void persistBook() { g_session->saveToActiveProject(); }
|
||||||
|
|
||||||
|
// Persists a completed bank index verb (create/rename/reorder/delete/evacuate/
|
||||||
|
// move/copy) as a SINGLE batched REAPER undo point (R-B) — one bank op = one Ctrl-Z.
|
||||||
|
//
|
||||||
|
// WHY THIS WRAPS AND persistBook() DOES NOT: a bank verb mutates ONLY our project
|
||||||
|
// ext-state (SetProjExtState under "reasampler"), which REAPER's undo system captures
|
||||||
|
// iff UNDO_STATE_MISCCFG is set in the Undo_EndBlock2 flags — the SDK documents
|
||||||
|
// MISCCFG as covering "extensions!" project ext-state (reaper_plugin.h ~1544, ~1199).
|
||||||
|
// We pass exactly UNDO_STATE_MISCCFG (not -1 / UNDO_STATE_ALL as the item-move family
|
||||||
|
// does): a bank verb touches no tracks, FX, items, or envelopes, so snapshotting them
|
||||||
|
// would be both heavier and semantically wrong. persistBook() (= SetProjExtState) runs
|
||||||
|
// INSIDE the block so the post-mutation ext-state is the block's "after" image.
|
||||||
|
//
|
||||||
|
// NO-OP GUARDRAIL: callers invoke this ONLY after the model mutation succeeded — a
|
||||||
|
// rejected op (duplicate name, un-deletable pool, etc.) returns before reaching here,
|
||||||
|
// so no dangling/empty undo point is ever opened.
|
||||||
|
void persistBankOp(const char* label) {
|
||||||
|
Undo_BeginBlock2(nullptr);
|
||||||
|
persistBook();
|
||||||
|
Undo_EndBlock2(nullptr, label, UNDO_STATE_MISCCFG);
|
||||||
|
}
|
||||||
|
|
||||||
// Prompts the user for a single line of text via REAPER's stock input dialog.
|
// Prompts the user for a single line of text via REAPER's stock input dialog.
|
||||||
// GetUserInputs(title, num_inputs=1, captions_csv, retvals_csv, sz) -> false on
|
// GetUserInputs(title, num_inputs=1, captions_csv, retvals_csv, sz) -> false on
|
||||||
// cancel (SDK ~3808). `initial` pre-fills the field. Returns false (leaving `out`
|
// cancel (SDK ~3808). `initial` pre-fills the field. Returns false (leaving `out`
|
||||||
@@ -523,7 +544,7 @@ void doBankCreate() {
|
|||||||
.c_str());
|
.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
persistBook();
|
persistBankOp("ReaSampler: create bank");
|
||||||
ShowConsoleMsg(("ReaSampler: created bank \"" + name + "\".\n").c_str());
|
ShowConsoleMsg(("ReaSampler: created bank \"" + name + "\".\n").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,7 +570,7 @@ void doBankRename() {
|
|||||||
"or another bank already uses that name).\n");
|
"or another bank already uses that name).\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
persistBook();
|
persistBankOp("ReaSampler: rename bank");
|
||||||
ShowConsoleMsg(("ReaSampler: renamed \"" + which + "\" -> \"" + newName + "\".\n")
|
ShowConsoleMsg(("ReaSampler: renamed \"" + which + "\" -> \"" + newName + "\".\n")
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
@@ -593,7 +614,7 @@ void doBankDelete() {
|
|||||||
ShowConsoleMsg("ReaSampler: cannot delete that bank (the pool is un-deletable).\n");
|
ShowConsoleMsg("ReaSampler: cannot delete that bank (the pool is un-deletable).\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
persistBook();
|
persistBankOp("ReaSampler: delete bank");
|
||||||
ShowConsoleMsg(("ReaSampler: deleted bank \"" + which + "\".\n").c_str());
|
ShowConsoleMsg(("ReaSampler: deleted bank \"" + which + "\".\n").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +636,7 @@ void doBankEvacuate() {
|
|||||||
"destination, not a source).\n");
|
"destination, not a source).\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
persistBook();
|
persistBankOp("ReaSampler: evacuate bank");
|
||||||
ShowConsoleMsg(("ReaSampler: evacuated \"" + which + "\" to the pool.\n").c_str());
|
ShowConsoleMsg(("ReaSampler: evacuated \"" + which + "\" to the pool.\n").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,7 +651,7 @@ void doBankActivateNext() {
|
|||||||
const std::string target = nextBankId(ids, g_session->book().activeBankId());
|
const std::string target = nextBankId(ids, g_session->book().activeBankId());
|
||||||
if (target.empty()) return; // degenerate (no banks) — cannot happen (pool seeded)
|
if (target.empty()) return; // degenerate (no banks) — cannot happen (pool seeded)
|
||||||
if (!g_session->book().setActiveBank(target)) return;
|
if (!g_session->book().setActiveBank(target)) return;
|
||||||
persistBook();
|
persistBankOp("ReaSampler: activate bank");
|
||||||
const Bank* b = g_session->book().bank(target);
|
const Bank* b = g_session->book().bank(target);
|
||||||
ShowConsoleMsg(("ReaSampler: active bank -> \"" +
|
ShowConsoleMsg(("ReaSampler: active bank -> \"" +
|
||||||
(b ? b->displayName : target) + "\".\n")
|
(b ? b->displayName : target) + "\".\n")
|
||||||
@@ -641,7 +662,7 @@ void doBankActivateNext() {
|
|||||||
// direct-by-id form; a general activate-bank-by-name/menu is a B4 affordance.
|
// direct-by-id form; a general activate-bank-by-name/menu is a B4 affordance.
|
||||||
void doBankActivatePool() {
|
void doBankActivatePool() {
|
||||||
if (!g_session->book().setActiveBank(kPoolBankId)) return;
|
if (!g_session->book().setActiveBank(kPoolBankId)) return;
|
||||||
persistBook();
|
persistBankOp("ReaSampler: activate bank");
|
||||||
ShowConsoleMsg("ReaSampler: active bank -> \"Pool\".\n");
|
ShowConsoleMsg("ReaSampler: active bank -> \"Pool\".\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,7 +713,15 @@ void doBankTransferSelected(bool copy) {
|
|||||||
case TransferResult::RejectedSameBank: break;
|
case TransferResult::RejectedSameBank: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
persistBook();
|
// No-op guardrail: if nothing actually changed the index (every selected sample was
|
||||||
|
// absent, or all were pre-checked rejects), don't open an undo point. `collapsed`
|
||||||
|
// counts a hash-collapse — that DID mutate the index (source entry removed on a
|
||||||
|
// move, or dest already held the hash), so it belongs inside the undo point.
|
||||||
|
if (ok > 0 || collapsed > 0) {
|
||||||
|
const std::string label =
|
||||||
|
std::string("ReaSampler: ") + verb + " sample(s)";
|
||||||
|
persistBankOp(label.c_str());
|
||||||
|
}
|
||||||
std::string log = std::string("ReaSampler: ") + verb + " -> \"" + destName +
|
std::string log = std::string("ReaSampler: ") + verb + " -> \"" + destName +
|
||||||
"\": " + std::to_string(ok) + " " + verb + "d";
|
"\": " + std::to_string(ok) + " " + verb + "d";
|
||||||
if (collapsed) log += ", " + std::to_string(collapsed) + " collapsed on hash";
|
if (collapsed) log += ", " + std::to_string(collapsed) + " collapsed on hash";
|
||||||
|
|||||||
Reference in New Issue
Block a user