route bank_panel bank-op persists through persistBankOp so panel gestures get undo points like the bindable actions

This commit is contained in:
2026-07-26 15:25:15 -04:00
parent 3f3ee94e77
commit 3514bb0b6d
3 changed files with 71 additions and 25 deletions
+33 -16
View File
@@ -476,22 +476,6 @@ bool persistBook() { return g_session->saveToActiveProject(); }
// rejected op (duplicate name, un-deletable pool, etc.) returns before reaching here,
// so no dangling/empty undo point is ever opened for a rejected op.
//
// UNSAVED-PROJECT GUARDRAIL: on an unsaved / no-active project persistBook() no-ops
// (nothing is written to ext state). We must still CLOSE the block we opened, but with
// an EMPTY label and a zero flag so REAPER DISCARDS the point instead of recording a
// no-effect undo entry — mirroring view.cpp's empty-plan close. The in-session model
// change stands and persists on the user's next save; it just earns no undo point until
// there is a project to persist into (undo of an unsaved bank op has nothing to roll
// back to anyway). The Begin/End must still be balanced, hence the close-either-way.
void persistBankOp(const char* label) {
Undo_BeginBlock2(nullptr);
const bool persisted = persistBook();
if (persisted)
Undo_EndBlock2(nullptr, label, UNDO_STATE_MISCCFG);
else
Undo_EndBlock2(nullptr, "", 0); // no ext-state write -> discard the empty point
}
// 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
// cancel (SDK ~3808). `initial` pre-fills the field. Returns false (leaving `out`
@@ -827,6 +811,39 @@ void doBankRemoveSelected() {
} // namespace
// Persists a completed bank-index verb as a SINGLE batched REAPER undo point (R-B) —
// one bank op = one Ctrl-Z. Declared in actions.h so bank_panel.cpp can call it
// without duplicating the undo logic.
//
// 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 for a rejected op.
//
// UNSAVED-PROJECT GUARDRAIL: on an unsaved / no-active project persistBook() no-ops
// (nothing is written to ext state). We must still CLOSE the block we opened, but with
// an EMPTY label and a zero flag so REAPER DISCARDS the point instead of recording a
// no-effect undo entry — mirroring view.cpp's empty-plan close. The in-session model
// change stands and persists on the user's next save; it just earns no undo point until
// there is a project to persist into (undo of an unsaved bank op has nothing to roll
// back to anyway). The Begin/End must still be balanced, hence the close-either-way.
void persistBankOp(const char* label) {
Undo_BeginBlock2(nullptr);
const bool persisted = persistBook();
if (persisted)
Undo_EndBlock2(nullptr, label, UNDO_STATE_MISCCFG);
else
Undo_EndBlock2(nullptr, "", 0); // no ext-state write -> discard the empty point
}
void bankRegisterActions(reaper_plugin_info_t* rec, ReaSamplerSession* session) {
g_session = session; // shared with the Design View family; same live session