Q-W4: split actions.cpp into design_view_actions/bank_actions/prune_action + shared action_registry; bank verbs deduped into promptless bankOp* inner verbs in panel_bank_ops (one mutation home, two UX skins); command-id strings byte-identical; actions.h shim carrier retired

This commit is contained in:
2026-07-29 12:56:02 -04:00
parent 5232227323
commit 430e117620
24 changed files with 1353 additions and 1241 deletions
+218 -104
View File
@@ -1,13 +1,15 @@
// panel_bank_ops.cpp — the bank-CRUD + menus seam of the docked bank panel (Q-W2
// split of bank_panel.cpp; Phase B4/B5). The SINGLE home of the panel-side bank verbs
// (create / rename / delete / evacuate / activate / move / copy / remove) — the owner
// Q-W4 dedupes actions.cpp against — plus the book/bank accessors, the popup menus
// that drive them, and the selection-id / OS-drag path resolvers.
// split of bank_panel.cpp; Phase B4/B5). Since Q-W4 this TU is the ONE implementation
// home of the bank verbs (create / rename / delete / evacuate / activate / move /
// copy / remove): the promptless bankOp* inner verbs (model op + persistBankOp only)
// serve BOTH thin UX skins — the panel's menu handlers here and the bindable
// bank_actions family — plus the book/bank accessors, the popup menus that drive
// them, and the selection-id / OS-drag path resolvers.
//
// Each op mutates g_session.book() then persists via persistBankOp() (one bank op =
// one Ctrl-Z; a true index no-op opens NO undo point). It DOES mutate the bank BOOK —
// that is the whole point of B4 — but only the index/model + ext-state, never the
// arrange, never a sample file on disk (bank ops are index-only; files stay put —
// Each verb mutates the session's book() then persists via persistBankOp() (one bank
// op = one Ctrl-Z; a true index no-op opens NO undo point). It DOES mutate the bank
// BOOK — that is the whole point of B4 — but only the index/model + ext-state, never
// the arrange, never a sample file on disk (bank ops are index-only; files stay put —
// CONTEXT.md §Multi-bank). REFERENCE-INVALIDATION GUARDRAIL: after a STRUCTURAL
// mutation any Bank*/BankModel& is invalid — resolve fresh, pass ids.
//
@@ -23,7 +25,6 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_bank_ops.h"
#include "actions.h" // persistBankOp — shared undo-block wrapper (R-B panel path)
#include "persist.h" // ReaSamplerSession — the live session the ops mutate
#define REAPERAPI_MINIMAL
@@ -33,6 +34,8 @@
#define REAPERAPI_WANT_Main_OnCommand
#define REAPERAPI_WANT_genGuid
#define REAPERAPI_WANT_guidToString
#define REAPERAPI_WANT_Undo_BeginBlock2
#define REAPERAPI_WANT_Undo_EndBlock2
#include "reaper_plugin_functions.h"
namespace reasampler::panel {
@@ -79,55 +82,28 @@ std::vector<const Bank*> namedBanks() {
return out;
}
// --- Bank management ops (id-keyed; drive the B1 model + persist) --------------
// --- Bank management ops (id-keyed; THIN UX SKINS over the bankOp* verbs) ------
//
// Each op mutates g_session.book() then persists via persistBankOp(). After a
// STRUCTURAL mutation (create/delete/evacuate) any Bank*/BankModel& is invalid — we
// resolve fresh, pass ids, and let the next refreshFingerprint repaint. On an
// unsaved project the empty-close discard in persistBankOp ensures no stale state
// Q-W4: each handler here owns only the panel's UX (prompts / confirms / message
// boxes / panel-state nudges / repaint); the model op + persist is the shared
// bankOp* inner verb (defined in the public section below). After a STRUCTURAL
// mutation (create/delete/evacuate) any Bank*/BankModel& is invalid — we resolve
// fresh, pass ids, and let the next refreshFingerprint repaint. On an unsaved
// project the empty-close discard in persistBankOp ensures no stale state
// survives (matches the capture/B3 quiet-persist idiom).
namespace {
// REAPER's stock single-line input (comma-safe via the \x1f return separator, as B3).
bool promptText(const char* title, const char* caption, const std::string& initial,
std::string& out) {
std::vector<char> buf(512, '\0');
std::snprintf(buf.data(), buf.size(), "%s", initial.c_str());
const std::string captions = std::string(caption) + ",separator=\x1f";
if (!GetUserInputs(title, 1, captions.c_str(), buf.data(),
static_cast<int>(buf.size())))
return false;
std::string s(buf.data());
if (s.empty()) return false;
out = std::move(s);
return true;
}
// Mints a genuine REAPER GUID string as a stable bank id (same as B3 mintBankId).
std::string mintBankId() {
GUID g{};
genGuid(&g);
char buf[64] = {0};
guidToString(&g, buf);
return std::string(buf);
}
} // namespace
void doCreateBank() {
if (!book()) return;
std::string name;
if (!promptText("ReaSampler: create bank", "Bank name:", "", name)) return;
const std::string id = mintBankId();
if (!book()->createBank(id, name)) {
const std::string id = bankOpCreate(name);
if (id.empty()) {
ShowMessageBox("A bank with that name already exists.",
"ReaSampler: create bank", 0);
return;
}
g_panel.shownBankId = id; // show the freshly-created bank
g_panel.focusedRegion = Region::Banks;
persistBankOp("ReaSampler: create bank");
invalidatePanel();
}
@@ -140,12 +116,11 @@ void doRenameBank(const std::string& bankId) {
const std::string current = bk->displayName; // copy before any mutation
std::string newName;
if (!promptText("ReaSampler: rename bank", "New name:", current, newName)) return;
if (!book()->renameBank(bankId, newName)) {
if (!bankOpRename(bankId, newName)) {
ShowMessageBox("Another bank already uses that name.",
"ReaSampler: rename bank", 0);
return;
}
persistBankOp("ReaSampler: rename bank");
invalidatePanel();
}
@@ -177,11 +152,11 @@ void doDeleteBank(const std::string& bankId) {
}
// r == 6 (Yes) falls through to a plain delete (drops members).
}
if (!book()->deleteBank(bankId)) return;
// S9: bump when the bank held samples (either the Yes-drop path or the No-evacuate-then-
// delete path moved/dropped members) — both change what a live instance could play. An
// empty-bank delete is purely organizational, no bump.
persistBankOp("ReaSampler: delete bank", /*bumpGeneration=*/members > 0);
// empty-bank delete is purely organizational, no bump. The ORIGINAL member count decides
// (the No-path evacuated them moments ago, but the membership still changed).
if (!bankOpDelete(bankId, /*bumpGeneration=*/members > 0)) return;
// shownBankId is reconciled by the next fingerprint pass. If no named banks remain,
// nudge focus to the pool so the selection has a valid home.
if (namedBanks().empty()) g_panel.focusedRegion = Region::Pool;
@@ -192,79 +167,39 @@ void doEvacuateBank(const std::string& bankId) {
if (!book()) return;
const Bank* bk = book()->bank(bankId);
if (!bk || bk->isPool()) return;
if (!book()->evacuate(bankId)) return;
persistBankOp("ReaSampler: evacuate bank", /*bumpGeneration=*/true); // S9: membership changed
if (!bankOpEvacuate(bankId)) return;
invalidatePanel();
}
void doActivateBank(const std::string& bankId) {
if (!book()) return;
if (!book()->setActiveBank(bankId)) return; // rejects an unknown id
persistBankOp("ReaSampler: activate bank");
if (!bankOpActivate(bankId)) return; // rejects an unknown id
invalidatePanel();
}
} // namespace
// Move or copy `sampleIds` from `srcBankId` to `destBankId` (index-only). Both pass
// ids straight to the model op (no BankModel& cached across the loop's mutations).
//
// NO-OP GUARDRAIL — VERB-AWARE (matches the action layer's doBankTransferSelected):
// * MOVE collapse: the source entry WAS removed (bank_book removes unconditionally
// before the dest add collapses on hash), so the index DID mutate — counts.
// * COPY collapse: the source is left intact AND the dest already held the hash,
// so NOTHING changed — a true index no-op. Must NOT open an undo point.
// Hence: copy counts only real gains (Copied); move counts gains OR collapses.
// Move or copy `sampleIds` from `srcBankId` to `destBankId` (index-only). Thin panel
// skin over bankOpTransfer (the one-home verb owns the loop, the verb-aware no-op
// guardrail, and the undo-batched persist); this layer clears the stale selection
// and repaints on an actual mutation.
void transferSamples(const std::vector<std::string>& sampleIds,
const std::string& srcBankId, const std::string& destBankId,
bool copy) {
if (!book()) return;
if (sampleIds.empty() || srcBankId == destBankId) return;
if (!book()->bank(srcBankId) || !book()->bank(destBankId)) return;
int ok = 0, collapsed = 0;
for (const std::string& sid : sampleIds) {
const TransferResult r =
copy ? book()->copySample(sid, srcBankId, destBankId)
: book()->moveSample(sid, srcBankId, destBankId);
switch (r) {
case TransferResult::Moved:
case TransferResult::Copied: ++ok; break;
case TransferResult::Collapsed: ++collapsed; break;
case TransferResult::RejectedUnknownBank:
case TransferResult::RejectedSampleAbsent:
case TransferResult::RejectedSameBank: break;
}
}
const bool mutated = copy ? (ok > 0) : (ok > 0 || collapsed > 0);
if (!mutated) return; // nothing changed — no persist, no undo point
const char* label = copy ? "ReaSampler: copy sample(s)" : "ReaSampler: move sample(s)";
persistBankOp(label, /*bumpGeneration=*/true); // S9: bank membership changed
if (!bankOpTransfer(sampleIds, srcBankId, destBankId, copy))
return; // nothing changed — no persist, no undo point
// The selection indexed into the source; after a move those indices are stale, so
// clear it (the fingerprint pass will also clear, but do it now for immediacy).
g_panel.selection = Selection{};
invalidatePanel();
}
// Remove `sampleIds` from `srcBankId` (index-only, this-bank scope). Non-destructive to
// the file: a last-reference remove leaves the file on disk, orphaned until Phase R
// prune — remove NEVER deletes bytes (the manifest is untouched). Removes are silent
// (no confirm dialog); recoverability is provided by the batched REAPER undo (R-B) —
// one Ctrl-Z restores the index entry. Ids passed by value — no BankModel& cached
// across the loop's mutations.
// Remove `sampleIds` from `srcBankId` (index-only, this-bank scope). Thin panel skin
// over bankOpRemove — see the verb for the never-deletes-bytes / silent-remove /
// one-Ctrl-Z contract. Clears the stale selection and repaints on an actual removal.
void removeSamples(const std::vector<std::string>& sampleIds,
const std::string& srcBankId) {
if (!book() || sampleIds.empty()) return;
if (!book()->bank(srcBankId)) return;
int removed = 0;
for (const std::string& sid : sampleIds)
if (book()->removeSample(sid, srcBankId, RemoveScope::ThisBank) ==
RemoveResult::Removed)
++removed;
if (removed == 0) return; // nothing changed — no persist, no undo point
persistBankOp("ReaSampler: remove sample(s)", /*bumpGeneration=*/true); // S9: sample dropped
if (!bankOpRemove(sampleIds, srcBankId))
return; // nothing changed — no persist, no undo point
// The selection indexed into the source; after a remove those indices are stale, so
// clear it (the fingerprint pass will also clear, but do it now for immediacy).
g_panel.selection = Selection{};
@@ -471,10 +406,189 @@ void showSelectionMenu(int screenX, int screenY) {
} // namespace reasampler::panel
// --- Public API (the selection read seam — panel_bank_ops.h) -------------------
// --- Public API (panel_bank_ops.h) ---------------------------------------------
namespace reasampler {
namespace {
// Persists the book after a bank mutation. Mirrors the CAPTURE path, NOT the
// Design-View path: quiet persist — saveToActiveProject no-ops on an unsaved project
// (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(); }
// 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).
// Distinct from a track GUID by origin only — both are canonical guidToString output.
std::string mintBankId() {
GUID g{};
genGuid(&g);
char buf[64] = {0}; // guidToString needs >=64 chars (SDK contract)
guidToString(&g, buf);
return std::string(buf);
}
} // namespace
// One home (Q-W4) for the former actions.cpp/panel_bank_ops.cpp byte-identical twins.
// 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) {
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());
const std::string captions = std::string(caption) + ",separator=\x1f";
if (!GetUserInputs(title, 1, captions.c_str(), buf.data(),
static_cast<int>(buf.size())))
return false; // user cancelled
std::string s(buf.data());
if (s.empty()) return false; // an empty name is not a valid bank name
out = std::move(s);
return true;
}
// Persists a completed bank-index verb 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.
//
// 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, bool bumpGeneration) {
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() ->
// saveToActiveProject() stamps bankGeneration()). Bumped only for content-changing verbs
// (the caller decides); a pure-organizational verb passes false and leaves the counter be,
// so a rename/activate does not needlessly refresh live instances.
if (bumpGeneration) panel::g_panel.session->bumpBankGeneration();
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
}
// --- Promptless inner bank verbs (Q-W4 single home) ----------------------------
// Model op + persistBankOp only; NO UX. Callers own prompts/confirms/nudges. Each
// verb resolves the book fresh (panel::book(), null when no live session) and
// persists ONLY after the model accepted — a rejected op opens no undo point.
std::string bankOpCreate(const std::string& name) {
BankBook* b = panel::book();
if (!b) return {};
const std::string id = mintBankId();
if (!b->createBank(id, name)) return {}; // duplicate display name (model rule)
persistBankOp("ReaSampler: create bank");
return id;
}
bool bankOpRename(const std::string& bankId, const std::string& newName) {
BankBook* b = panel::book();
if (!b || !b->renameBank(bankId, newName)) return false; // pool / name in use
persistBankOp("ReaSampler: rename bank");
return true;
}
bool bankOpDelete(const std::string& bankId, bool bumpGeneration) {
BankBook* b = panel::book();
if (!b || !b->deleteBank(bankId)) return false; // pool un-deletable (model rule)
persistBankOp("ReaSampler: delete bank", bumpGeneration);
return true;
}
bool bankOpEvacuate(const std::string& bankId) {
BankBook* b = panel::book();
if (!b || !b->evacuate(bankId)) return false; // pool is a destination, not a source
// S9: evacuate moves members between banks (bank membership changes) -> bump.
persistBankOp("ReaSampler: evacuate bank", /*bumpGeneration=*/true);
return true;
}
bool bankOpActivate(const std::string& bankId) {
BankBook* b = panel::book();
if (!b || !b->setActiveBank(bankId)) return false; // rejects an unknown id
persistBankOp("ReaSampler: activate bank");
return true;
}
// NO-OP GUARDRAIL — VERB-AWARE (a collapse means different things per verb):
// * MOVE collapse: the source entry WAS removed (bank_book moveSample removes
// unconditionally before the dest add collapses on hash), so the index DID
// mutate — it counts toward opening an undo point.
// * COPY collapse: the source is left intact AND the dest already held the hash,
// so NOTHING changed — a true index no-op. It must NOT open an undo point.
// Hence: copy counts only real gains; move counts gains OR collapses. Ids pass
// straight to the model op — no BankModel& cached across the loop's mutations.
bool bankOpTransfer(const std::vector<std::string>& sampleIds,
const std::string& srcBankId, const std::string& destBankId,
bool copy) {
BankBook* b = panel::book();
if (!b || sampleIds.empty() || srcBankId == destBankId) return false;
if (!b->bank(srcBankId) || !b->bank(destBankId)) return false;
int ok = 0, collapsed = 0;
for (const std::string& sid : sampleIds) {
const TransferResult r =
copy ? b->copySample(sid, srcBankId, destBankId)
: b->moveSample(sid, srcBankId, destBankId);
switch (r) {
case TransferResult::Moved:
case TransferResult::Copied: ++ok; break;
case TransferResult::Collapsed: ++collapsed; break;
case TransferResult::RejectedUnknownBank:
case TransferResult::RejectedSampleAbsent:
case TransferResult::RejectedSameBank: break;
}
}
const bool mutated = copy ? (ok > 0) : (ok > 0 || collapsed > 0);
if (!mutated) return false; // nothing changed — no persist, no undo point
// S9: a move/copy changes bank membership (a sample arrives in / leaves a bank an
// instance may reference) -> bump so assigned instances refresh hands-free.
persistBankOp(copy ? "ReaSampler: copy sample(s)" : "ReaSampler: move sample(s)",
/*bumpGeneration=*/true);
return true;
}
// Index-only, this-bank scope (fork R-A: the sole surfaced verb; RemoveScope::AllBanks
// stays latent in the model). Non-destructive to the file: a last-reference remove
// leaves the file on disk, orphaned until Phase R prune — remove NEVER deletes bytes
// (the manifest is untouched). Silent: recoverability is the batched undo (R-B).
bool bankOpRemove(const std::vector<std::string>& sampleIds,
const std::string& srcBankId) {
BankBook* b = panel::book();
if (!b || sampleIds.empty() || !b->bank(srcBankId)) return false;
int removed = 0;
for (const std::string& sid : sampleIds)
if (b->removeSample(sid, srcBankId, RemoveScope::ThisBank) ==
RemoveResult::Removed)
++removed;
if (removed == 0) return false; // every id already absent — no undo point
// S9: a remove drops a sample from a bank (an instance referencing it must refresh —
// it will resolve to silence, per the stale-id policy) -> bump.
persistBankOp("ReaSampler: remove sample(s)", /*bumpGeneration=*/true);
return true;
}
// --- Selection read seam --------------------------------------------------------
std::vector<std::string> bankPanelSelectedSampleIds() {
return panel::focusedSelectionIds();
}
+79 -7
View File
@@ -1,19 +1,91 @@
#pragma once
// panel_bank_ops — the bank-CRUD + selection-read seam of the bank panel (Q-W2 split
// of bank_panel.h; Phase B4/B5). The .cpp is the single home of the panel-side bank
// verbs (create / rename / delete / evacuate / activate / move / copy / remove),
// each driven against the B1 BankBook model on the session and persisted via
// persistBankOp (one bank op = one Ctrl-Z) — the owner Q-W4 dedupes actions.cpp
// against. This header carries the panel's public selection-read surface.
// of bank_panel.h; Phase B4/B5). The .cpp is the SINGLE implementation home of the
// bank verbs (create / rename / delete / evacuate / activate / move / copy / remove):
// each promptless inner verb below drives the B1 BankBook model on the session and
// persists via persistBankOp (one bank op = one Ctrl-Z). Q-W4 dedupe: the panel's
// menu handlers and the bank_actions bindable family are both thin UX skins
// (prompts / confirms / console vs. message boxes / panel-state nudges) over these
// one-home verbs. This header carries that verb surface, the shared prompt/persist
// helpers, and the panel's public selection-read surface.
//
// REAPER-free: main.cpp (insert action) and actions.cpp read the selection through
// these free functions.
// The selection reads are REAPER-free; the verbs and helpers are REAPER-facing
// (persist + stock dialogs) but SDK-free in this header.
#include <string>
#include <vector>
namespace reasampler {
// --- Promptless inner bank verbs (Q-W4 single home) --------------------------
// Each verb: model op on the session's BankBook + persistBankOp (undo-batched
// ext-state persist) — NO prompts, NO message boxes, NO panel-state nudges. The
// caller owns all UX. Every verb returns whether the model accepted the mutation
// (a rejected op persists nothing and opens no undo point). Verbs resolve the
// session via the panel's live session pointer (set at load by bankPanelInit,
// before any action can fire) and fail safe (false / "") when it is absent.
// Mints a stable GUID bank id, creates `name` in the book. Returns the new bank id,
// or "" when the model rejects the name (duplicate, trimmed + case-insensitive).
// Create is purely organizational — no generation bump.
std::string bankOpCreate(const std::string& name);
// Renames `bankId`. False when the model rejects (pool un-renamable / name in use).
bool bankOpRename(const std::string& bankId, const std::string& newName);
// Deletes `bankId`. False when the model rejects (pool un-deletable). The caller
// passes `bumpGeneration` from the member count it read BEFORE any evacuate/delete
// (an evacuate-then-delete flow must still bump on the ORIGINAL membership).
bool bankOpDelete(const std::string& bankId, bool bumpGeneration);
// Evacuates `bankId`'s members to the pool. False when the model rejects (the pool
// itself). Bumps the generation (membership changed).
bool bankOpEvacuate(const std::string& bankId);
// Activates `bankId` as the capture target. False on an unknown id. No bump.
bool bankOpActivate(const std::string& bankId);
// Moves (copy=false) or copies (copy=true) `sampleIds` from `srcBankId` to
// `destBankId` (index-only; files never relocate). Returns whether the index
// actually mutated — the verb-aware no-op guardrail: a COPY collapse changes
// nothing (no undo point); a MOVE collapse did remove the source entry (counts).
// Persists ONE undo point ("move/copy sample(s)") only when mutated.
bool bankOpTransfer(const std::vector<std::string>& sampleIds,
const std::string& srcBankId, const std::string& destBankId,
bool copy);
// Removes `sampleIds` from `srcBankId` (index-only, this-bank scope; never deletes
// bytes). Returns whether anything was removed; persists one undo point when so.
bool bankOpRemove(const std::vector<std::string>& sampleIds,
const std::string& srcBankId);
// --- Shared UX/persist helpers ------------------------------------------------
// Prompts the user for a single line of text via REAPER's stock input dialog
// (GetUserInputs). `initial` pre-fills the field. Returns false (leaving `out`
// untouched) on cancel or an empty entry. COMMA GUARD: the return separator is
// overridden to \x1f (un-typeable) via the documented `separator=X` pseudo-caption,
// so any printable name — commas included — round-trips whole (SDK ~3806/3808).
// One home (Q-W4) for the former actions.cpp/panel_bank_ops.cpp twins.
bool promptText(const char* title, const char* caption, const std::string& initial,
std::string& out);
// Persists a completed bank-index verb as a single REAPER undo point (R-B).
// Wraps the session persist (SetProjExtState) in a Begin/End block with
// UNDO_STATE_MISCCFG so the bank op is one Ctrl-Z. On an unsaved / no-active project
// the persist no-ops and the block is closed with an empty label + zero flag (REAPER
// discards it). Callers must invoke this ONLY after a successful/effective mutation —
// rejected ops (duplicate name, un-deletable pool, etc.) must return before reaching
// here so no empty undo point is ever opened for a no-op.
//
// S9 bank-generation bump: pass `bumpGeneration = true` for a verb that changes what a
// live instance would PLAY — move / copy / remove / evacuate / delete-with-members. Leave
// it false (the default) for a PURELY ORGANIZATIONAL verb — create / rename / activate /
// reorder. The bump (when requested) happens INSIDE the block, BEFORE the persist, so
// the stamped counter rides the same ext-state write and undo captures the pre/post
// generation with the rest of the blob.
void persistBankOp(const char* label, bool bumpGeneration = false);
// The stable ids of the currently-selected samples, in bank (insertion) order.
// Empty when nothing is selected or the panel has never opened. This is the clean
// seam the `insert` action reads to know WHAT to place — it returns ids (not grid
+1 -1
View File
@@ -20,7 +20,7 @@
#include "shell/panel/panel_state.h"
#include "actions.h" // persistBankOp — shared undo-block wrapper (R-B)
#include "shell/panel/panel_bank_ops.h" // persistBankOp — shared undo-block wrapper (R-B)
#include "shell/actions/drag_out_win.h" // OLE / SWELL drag-out initiation seam (M11)
#include "shell/actions/instrument_drop_win.h" // resolveFxDropTarget / performInstrumentDrop (S17)
+1 -1
View File
@@ -17,7 +17,7 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_input.h"
#include "actions.h" // bankPruneCommandId — the footer Prune dispatch (R3)
#include "shell/actions/bank_actions.h" // bankPruneCommandId — the footer Prune dispatch (R3)
#include "persist.h" // ReaSamplerSession — view/tail reads + mutation
#include "core/view/view_mode_model.h" // autoTagNewContent / NewItem / AutoTag (D2 Wave 2)
#include "shell/capture/item_read.h" // itemGuid / itemLaneName — shared item-read seam (D2 W3-B)
+1 -1
View File
@@ -217,7 +217,7 @@ std::string activeModeIdOrEmpty() {
// The BOTTOM toolbar inventory (L5 refinement 3): FOUR Item/Track x Arrange/Design tag buttons
// then a set-apart Show Both. The suffixes are the ACTUAL registered command-id strings from
// actions.cpp (VIEW_MOVE_ITEMS_ARRANGE / VIEW_MOVE_ITEMS_DESIGN for the item moves;
// design_view_actions.cpp (VIEW_MOVE_ITEMS_ARRANGE / VIEW_MOVE_ITEMS_DESIGN for the item moves;
// VIEW_TAG_ARRANGE / VIEW_TAG_DESIGN for the track tags; VIEW_SHOW_BOTH) — grepped, not
// paraphrased. "…: Arrange" routes through the untag/arrange path (Arrange = absence of a tag).
// The Toggle + both Activate buttons are REMOVED (L5 refinement 4 / settled inventory): the
+1 -1
View File
@@ -6,7 +6,7 @@
// row/cluster builders, region rects, the L7 slot-order display bridge) is internal to
// panel_layout.cpp (see panel_state.h for the intra-panel seam).
//
// REAPER-free: main.cpp / actions.cpp drive these through plain free functions.
// REAPER-free: main.cpp / bank_actions.cpp drive these through plain free functions.
namespace reasampler {
+5 -4
View File
@@ -16,10 +16,11 @@
// * Explicit using-declarations pulling the pure modules' symbols into
// reasampler::panel from their REAL namespace homes (Q-W1 sub-namespaces) — this
// header itself does not directly include the interim core/namespaces.h shim
// (Q-W2 retires that direct dependency for this module). Six of the eight panel
// TUs still pull the shim in TRANSITIVELY via actions.h/persist.h/ingest.h/
// draw_kit.h/view.h; only panel_thumbnails.cpp and panel_audition.cpp are
// shim-free end to end. Nothing HERE depends on it either way.
// (Q-W2 retires that direct dependency for this module; Q-W4 retired the
// actions.h carrier with the actions split). Several panel TUs still pull the
// shim in TRANSITIVELY via persist.h/ingest.h/draw_kit.h/view.h; only
// panel_thumbnails.cpp and panel_audition.cpp are shim-free end to end.
// Nothing HERE depends on it either way.
//
// REFERENCE-INVALIDATION GUARDRAIL (CONTEXT.md §Multi-bank): a bank-structural
// mutation (create/delete/evacuate/activate/move) can reallocate the book's vector,