Q-W6: registration table (OCP) in main.cpp; bank verbs -> shell/bank_ops(Session&); persist.h + wav_trim + namespaces.h shims deleted; 61/61

capture.h realtime seam split to capture_realtime_shell.h; GetProjExtState grow-loop rehomed to core/wire/ext_state_read; stale persist.cpp/bank_panel.cpp comment refs fixed; CLAUDE.md persist/bank_book/actions bullets updated. Command-id suffixes, display phrases, and undo labels byte-identical.
This commit is contained in:
2026-07-29 13:40:09 -04:00
parent 4831e0e172
commit f3be4d8cce
81 changed files with 970 additions and 1085 deletions
+10 -2
View File
@@ -1,4 +1,3 @@
#include "core/namespaces.h"
// draw_kit — the LICE/SWELL shell half of the drawing kit. See draw_kit.h.
//
// Compiled into the reaper_reasampler MODULE. SHELL layer: it is the only kit file that
@@ -12,7 +11,7 @@
#include "core/ui/bank_grid.h" // compressAmplitudeForDisplay — the shared dB display curve (pure)
// SWELL / LICE. On Windows use native Win32 (windows.h first); on mac/linux SWELL is
// provided by the host. Mirrors bank_panel.cpp's include discipline.
// provided by the host. Mirrors the panel TUs' (shell/panel/) include discipline.
#ifdef _WIN32
#include <windows.h>
#else
@@ -24,6 +23,15 @@
namespace reasampler {
// Real-namespace-home using-declarations (Q-W6: the namespaces.h shim is retired).
using audio::ChannelEnvelope;
using audio::columnMinMax;
using audio::MinMax;
using ui::compressAmplitudeForDisplay;
using ui::roleColor;
using ui::roleColorState;
using ui::spectralColor;
// --- KitColor <-> LICE boundary ----------------------------------------------
// The one place a pure KitColor becomes a LICE_pixel. Verified packing: LICE_RGBA(r,g,b,a)
+13 -1
View File
@@ -1,5 +1,4 @@
#pragma once
#include "core/namespaces.h"
// draw_kit — the LICE-facing SHELL half of the shared drawing kit (Phase L, L1). This is
// the ONE source of drawing for the whole system: every surface (bank_panel now; the VST
// editor + embed strip at L3) fills, buttons, rows, sliders, waveforms, and — above all —
@@ -41,6 +40,19 @@ class LICE_IBitmap;
namespace reasampler {
// Real-namespace-home using-declarations (Q-W6: the interim core/namespaces.h shim
// is retired; the kit's pure vocabulary names its Q-W1 homes explicitly). These are
// deliberate re-exports: every draw_kit consumer speaks these types at the call
// boundary, so they surface here exactly as panel_state.h surfaces the panel's.
using audio::Envelope;
using ui::InteractionState;
using ui::KitBox;
using ui::KitButtonBox;
using ui::KitColor;
using ui::ListRowBox;
using ui::Role;
using ui::SliderGeometry;
// The kit's four cached fonts (§3.1 type scale). Consumers pass a Font to text() to pick
// the size/weight; the kit maps it to the matching LICE_CachedFont.
enum class Font {
+30 -204
View File
@@ -1,17 +1,10 @@
// panel_bank_ops.cpp — the bank-CRUD + menus seam of the docked bank panel (Q-W2
// 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 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.
// panel_bank_ops.cpp — the bank-CRUD-UX + menus seam of the docked bank panel
// (Q-W2 split of the former bank_panel god-module; Phase B4/B5). Since Q-W6 the
// promptless bank verbs live in shell/bank_ops (model op + persistBankOp, taking
// ReaSamplerSession&); this TU is the panel's THIN UX SKIN over them — the menu
// handlers (prompts / confirms / message boxes / panel-state nudges / repaint),
// the book/bank accessors, the popup menus that drive them, and the selection-id /
// OS-drag path resolvers. The bindable bank_actions family is the sibling skin.
//
// Compiled into the reaper_reasampler MODULE. Includes reaper_plugin_functions.h
// WITHOUT REAPERAPI_IMPLEMENT — main.cpp owns the API pointers; here they are
@@ -25,24 +18,21 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_bank_ops.h"
#include "persist.h" // ReaSamplerSession — the live session the ops mutate
#include "shell/bank_ops/bank_ops.h" // bankOp* promptless verbs (the Q-W6 non-UI seam)
#include "shell/persist/session.h" // ReaSamplerSession — the live session the ops mutate
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_EnumProjects
#define REAPERAPI_WANT_GetUserInputs
#define REAPERAPI_WANT_ShowMessageBox
#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 {
namespace fs = std::filesystem;
// --- Current-project directory (mirrors persist.cpp's derivation) -------------
// --- Current-project directory (mirrors the persist shell's derivation, ext_state_io.cpp)
std::string currentProjectDir() {
std::vector<char> buf(4096, '\0');
EnumProjects(-1, buf.data(), static_cast<int>(buf.size()));
@@ -84,19 +74,21 @@ std::vector<const Bank*> namedBanks() {
// --- Bank management ops (id-keyed; THIN UX SKINS over the bankOp* verbs) ------
//
// 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).
// Q-W4/Q-W6: 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 (shell/bank_ops), which takes the live session by
// reference — the book() check answers the one session-liveness question per
// handler. 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).
void doCreateBank() {
if (!book()) return;
std::string name;
if (!promptBankName("ReaSampler: create bank", "Bank name:", "", name)) return;
const std::string id = bankOpCreate(name);
const std::string id = bankOpCreate(*g_panel.session, name);
if (id.empty()) {
ShowMessageBox("A bank with that name already exists.",
"ReaSampler: create bank", 0);
@@ -116,7 +108,7 @@ void doRenameBank(const std::string& bankId) {
const std::string current = bk->displayName; // copy before any mutation
std::string newName;
if (!promptBankName("ReaSampler: rename bank", "New name:", current, newName)) return;
if (!bankOpRename(bankId, newName)) {
if (!bankOpRename(*g_panel.session, bankId, newName)) {
ShowMessageBox("Another bank already uses that name.",
"ReaSampler: rename bank", 0);
return;
@@ -156,7 +148,7 @@ void doDeleteBank(const std::string& bankId) {
// delete path moved/dropped members) — both change what a live instance could play. An
// 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;
if (!bankOpDelete(*g_panel.session, 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;
@@ -167,12 +159,13 @@ void doEvacuateBank(const std::string& bankId) {
if (!book()) return;
const Bank* bk = book()->bank(bankId);
if (!bk || bk->isPool()) return;
if (!bankOpEvacuate(bankId)) return;
if (!bankOpEvacuate(*g_panel.session, bankId)) return;
invalidatePanel();
}
void doActivateBank(const std::string& bankId) {
if (!bankOpActivate(bankId)) return; // rejects an unknown id
if (!book()) return; // no live session — nothing to activate against
if (!bankOpActivate(*g_panel.session, bankId)) return; // rejects an unknown id
invalidatePanel();
}
@@ -185,7 +178,8 @@ void doActivateBank(const std::string& bankId) {
void transferSamples(const std::vector<std::string>& sampleIds,
const std::string& srcBankId, const std::string& destBankId,
bool copy) {
if (!bankOpTransfer(sampleIds, srcBankId, destBankId, copy))
if (!book()) return; // no live session — nothing to transfer within
if (!bankOpTransfer(*g_panel.session, 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).
@@ -198,7 +192,8 @@ void transferSamples(const std::vector<std::string>& sampleIds,
// 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 (!bankOpRemove(sampleIds, srcBankId))
if (!book()) return; // no live session — nothing to remove from
if (!bankOpRemove(*g_panel.session, 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).
@@ -410,35 +405,7 @@ void showSelectionMenu(int screenX, int screenY) {
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. 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).
// 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.
// One home (Q-W4) for the former actions/panel 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.
@@ -457,147 +424,6 @@ bool promptBankName(const char* title, const char* caption, const std::string& i
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.
//
// 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() ->
// 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() {
+12 -78
View File
@@ -1,97 +1,31 @@
#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 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.
// panel_bank_ops — the bank-CRUD-UX + selection-read seam of the bank panel (Q-W2
// split of the former bank_panel god-module; Phase B4/B5). Since Q-W6 the
// promptless bank verbs themselves live in the NON-UI shell/bank_ops seam
// (bankOp* + persistBankOp, taking ReaSamplerSession&); this TU is the panel's
// thin UX skin over them — prompts / confirms / message boxes / panel-state
// nudges / repaints — plus the popup menus that drive them. The bank_actions
// bindable family is the sibling skin over the same verbs. This header carries
// the shared prompt helper and the panel's public selection-read surface.
//
// The selection reads are REAPER-free; the verbs and helpers are REAPER-facing
// (persist + stock dialogs) but SDK-free in this header.
// The selection reads are REAPER-free; the prompt helper is REAPER-facing (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.
// One home (Q-W4) for the former actions/panel byte-identical twins; shared by the
// panel menus and the bank_actions bindable family.
bool promptBankName(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.
//
// NULL-SESSION GUARD: this is a public API with callers outside panel_bank_ops.cpp
// (e.g. panel_drag.cpp). If the panel's session pointer is absent (no live session),
// this is a no-op — no undo block is opened. Today every real caller only reaches
// here via a prior session-backed check, so the guard is not yet reachable in
// practice; it exists to make the function safe to call standalone.
//
// 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
+3 -3
View File
@@ -20,7 +20,7 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_bank_ops.h" // persistBankOp — shared undo-block wrapper (R-B)
#include "shell/bank_ops/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)
@@ -374,7 +374,7 @@ namespace {
void doReorderDrop(const std::string& id, const std::string& bankId, int targetSlot) {
if (!book() || id.empty() || bankId.empty() || targetSlot < 0) return;
if (!book()->reorderSample(id, bankId, targetSlot)) return; // rejected/no-op: no undo point
persistBankOp("ReaSampler: reorder sample");
persistBankOp(*g_panel.session, "ReaSampler: reorder sample");
g_panel.selection = Selection{};
invalidatePanel();
}
@@ -387,7 +387,7 @@ void doReplaceDrop(const std::string& newId, const std::string& oldId,
const std::string& bankId) {
if (!book() || newId.empty() || oldId.empty() || bankId.empty()) return;
if (!book()->replaceSample(newId, oldId, bankId)) return; // pool-guard reject: NO-OP
persistBankOp("ReaSampler: replace sample");
persistBankOp(*g_panel.session, "ReaSampler: replace sample");
g_panel.selection = Selection{};
invalidatePanel();
}
+3 -2
View File
@@ -18,7 +18,7 @@
#include "shell/panel/panel_input.h"
#include "shell/actions/bank_actions.h" // bankPruneCommandId — the footer Prune dispatch (R3)
#include "persist.h" // ReaSamplerSession — view/tail reads + mutation
#include "shell/persist/session.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)
#include "shell/capture/track_guid.h" // guidString — canonical track GUID key (D2 Wave 2)
@@ -157,7 +157,8 @@ void enumerateLiveGuids(ReaProject* proj, std::set<std::string>& allGuids,
//
// INTENTIONAL: membership mutation happens OUTSIDE any Undo block. Auto-tag is a
// background metadata update (like setting a label), not a destructive project edit.
// persist.cpp writes it on the next project save alongside the bank and view state, the
// the persist shell (ext_state_io.cpp) writes it on the next project save alongside the
// bank and view state, the
// same way an action-driven tag is persisted. Wrapping this in an Undo block would flood
// the REAPER undo history with a new entry for every timer tick that sees new content.
// Returns true iff this tick tagged at least one new GUID into a mode — the signal the
+1 -1
View File
@@ -20,7 +20,7 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_layout.h"
#include "persist.h" // ReaSamplerSession — mode/view reads
#include "shell/persist/session.h" // ReaSamplerSession — mode/view reads
#include "core/view/view_mode_model.h" // ViewModeModel — modes()/activeModeId()
// Action-trigger buttons (M11): resolve each button's command id at runtime from the
+1 -1
View File
@@ -18,7 +18,7 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/draw_kit.h" // kit text()/fillSurface/drawButton/drawWaveform (L1)
#include "persist.h" // ReaSamplerSession — mode/view/tail reads
#include "shell/persist/session.h" // ReaSamplerSession — mode/view/tail reads
#include "core/view/view_mode_model.h" // ViewModeModel / Mode — the footer toggle's model
namespace reasampler::panel {
+7 -11
View File
@@ -14,13 +14,9 @@
// plain free function — direct call-through, no interface, no virtual dispatch
// (T4-28: the audition path and the per-mouse-move path must stay direct calls).
// * 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; 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.
// reasampler::panel from their REAL namespace homes (Q-W1 sub-namespaces). The
// interim core/namespaces.h shim is GONE (deleted in Q-W6 with the last split);
// every symbol below names its true home.
//
// REFERENCE-INVALIDATION GUARDRAIL (CONTEXT.md §Multi-bank): a bank-structural
// mutation (create/delete/evacuate/activate/move) can reallocate the book's vector,
@@ -84,10 +80,10 @@ namespace reasampler::panel {
// --- Real-namespace-home using-declarations -----------------------------------
//
// The panel's pre-split internals reference the pure modules' symbols unqualified;
// these explicit per-symbol usings (NOT the core/namespaces.h shim) keep those
// references valid while documenting each symbol's Q-W1 home. Flat-`reasampler`
// symbols (BankBook / ViewModeModel / the draw_kit shell / persistBankOp / ...)
// resolve via the enclosing namespace and need no using.
// these explicit per-symbol usings keep those references valid while documenting
// each symbol's Q-W1 home. Flat-`reasampler` symbols (BankBook / ViewModeModel /
// the draw_kit shell / the shell/bank_ops verbs / ...) resolve via the enclosing
// namespace and need no using.
// core/ui
using ui::ActionBarRect;