fix: guard persistBankOp/persistBook against a null session; rename promptText to promptBankName

This commit is contained in:
2026-07-29 13:06:12 -04:00
parent 430e117620
commit 4f587258b2
3 changed files with 34 additions and 17 deletions
+18 -7
View File
@@ -95,7 +95,7 @@ std::vector<const Bank*> namedBanks() {
void doCreateBank() {
if (!book()) return;
std::string name;
if (!promptText("ReaSampler: create bank", "Bank name:", "", name)) return;
if (!promptBankName("ReaSampler: create bank", "Bank name:", "", name)) return;
const std::string id = bankOpCreate(name);
if (id.empty()) {
ShowMessageBox("A bank with that name already exists.",
@@ -115,7 +115,7 @@ void doRenameBank(const std::string& bankId) {
if (!bk || bk->isPool()) return;
const std::string current = bk->displayName; // copy before any mutation
std::string newName;
if (!promptText("ReaSampler: rename bank", "New name:", current, newName)) return;
if (!promptBankName("ReaSampler: rename bank", "New name:", current, newName)) return;
if (!bankOpRename(bankId, newName)) {
ShowMessageBox("Another bank already uses that name.",
"ReaSampler: rename bank", 0);
@@ -417,9 +417,13 @@ namespace {
// (the change stays valid for the session and persists on the user's next save).
// Deliberately NO Save-As prompt; do not "align" with persistViewState's prompt
// idiom. Returns whether a persist actually happened, so persistBankOp can discard
// its undo block when nothing was written. Session pointer is live for the whole
// extension lifetime (bankPanelInit at load, before any action registers).
bool persistBook() { return panel::g_panel.session->saveToActiveProject(); }
// its undo block when nothing was written. Guards a null session pointer (false,
// no-op) — see persistBankOp's guard below for why this is defensive rather than
// dead code.
bool persistBook() {
if (!panel::g_panel.session) return false; // no live session: nothing to persist
return panel::g_panel.session->saveToActiveProject();
}
// Mints a fresh, genuine REAPER GUID string as a stable bank id (the B2/model
// design: ids are caller-supplied and stable; the model stays pure and mints none).
@@ -438,8 +442,8 @@ std::string mintBankId() {
// COMMA GUARD: GetUserInputs splits returned values on a separator defaulting to ',',
// so the return separator is overridden to \x1f (un-typeable) via the documented
// `separator=X` trailing pseudo-caption (SDK ~3806) — any printable name round-trips.
bool promptText(const char* title, const char* caption, const std::string& initial,
std::string& out) {
bool promptBankName(const char* title, const char* caption, const std::string& initial,
std::string& out) {
std::vector<char> buf(512, '\0');
// Pre-fill: GetUserInputs seeds the field from the retvals buffer's initial value.
std::snprintf(buf.data(), buf.size(), "%s", initial.c_str());
@@ -472,7 +476,14 @@ bool promptText(const char* title, const char* caption, const std::string& initi
// 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.
//
// NULL-SESSION GUARD: this is a public API (panel_bank_ops.h) with callers outside
// this TU (e.g. panel_drag.cpp), not all of which are guaranteed to have re-checked
// the session pointer immediately beforehand. Bail out BEFORE Undo_BeginBlock2 — no
// block is opened, so there is nothing to balance and no risk of an unbalanced
// Begin/End pair.
void persistBankOp(const char* label, bool bumpGeneration) {
if (!panel::g_panel.session) return; // no live session: no-op, no undo point opened
Undo_BeginBlock2(nullptr);
// S9: bump the bank-generation counter INSIDE the block, before persistBook(), so the
// fresh generation rides the same ext-state write the persist makes (persistBook() ->