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:
@@ -1,9 +1,10 @@
|
||||
// action_registry.cpp — shared registration plumbing (Q-W4 split of actions.cpp).
|
||||
// See action_registry.h. Needs no REAPER API pointers: rec->Register is a member
|
||||
// call on the dispatch struct REAPER hands the entry point.
|
||||
// action_registry.cpp — shared registration plumbing (Q-W4) + the registration
|
||||
// table (Q-W6). See action_registry.h. Needs no REAPER API pointers: rec->Register
|
||||
// is a member call on the dispatch struct REAPER hands the entry point.
|
||||
|
||||
#include "shell/actions/action_registry.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
@@ -23,6 +24,18 @@ using version::channelCommandId;
|
||||
// pointer for a given action.
|
||||
std::deque<std::string> g_strStore;
|
||||
|
||||
// One registered table row: the row data plus the registry-owned registration
|
||||
// artifacts (interned id, minted cmd, gaccel storage REAPER holds a pointer to).
|
||||
// A std::deque so element addresses never move after push_back — REAPER keeps each
|
||||
// &accel until the mirror-unregister.
|
||||
struct TableEntry {
|
||||
ActionTableRow row;
|
||||
const char* id = nullptr; // interned channel-qualified command id
|
||||
int cmd = 0; // minted command id (0 = mint failed / inert row)
|
||||
gaccel_register_t accel{}; // Actions-list entry; address handed to REAPER
|
||||
};
|
||||
std::deque<TableEntry> g_table;
|
||||
|
||||
} // namespace
|
||||
|
||||
const char* channelIdFor(const char* suffix) {
|
||||
@@ -46,4 +59,39 @@ int registerAction(reaper_plugin_info_t* rec, const char* suffix,
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void registerActionTable(reaper_plugin_info_t* rec, const ActionTableRow* rows,
|
||||
std::size_t count) {
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
g_table.push_back(TableEntry{rows[i]});
|
||||
TableEntry& e = g_table.back();
|
||||
e.id = channelIdFor(e.row.suffix);
|
||||
e.cmd = registerAction(rec, e.row.suffix, e.accel, e.row.phrase);
|
||||
}
|
||||
}
|
||||
|
||||
bool actionTableHandleCommand(int command) {
|
||||
if (command == 0) return false;
|
||||
for (const TableEntry& e : g_table)
|
||||
if (e.cmd != 0 && command == e.cmd) {
|
||||
e.row.run(e.row.arg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int actionTableCommandId(const char* suffix) {
|
||||
for (const TableEntry& e : g_table)
|
||||
if (std::strcmp(e.row.suffix, suffix) == 0) return e.cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unregisterActionTable(reaper_plugin_info_t* rec) {
|
||||
// Reverse table order, mirroring the register loop. Each '-command_id'
|
||||
// re-presents the SAME interned pointer channelIdFor handed out at register.
|
||||
for (auto it = g_table.rbegin(); it != g_table.rend(); ++it) {
|
||||
rec->Register("-gaccel", (void*)&it->accel);
|
||||
rec->Register("-command_id", (void*)it->id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
#pragma once
|
||||
// action_registry — shared registration plumbing for the bindable action families
|
||||
// (Q-W4 split of actions.cpp). Owns the durable interned-string store both the
|
||||
// Design View and multi-bank families register through, so a composed command id
|
||||
// keeps ONE stable pointer from register to the mirror-unregister, and the
|
||||
// register-a-command_id-then-gaccel sequence has one implementation.
|
||||
// action_registry — shared registration plumbing + the Q-W6 registration TABLE.
|
||||
//
|
||||
// Two layers, one TU:
|
||||
//
|
||||
// * The Q-W4 plumbing (channelIdFor / registerAction): the durable interned-string
|
||||
// store the action families register through, so a composed command id keeps ONE
|
||||
// stable pointer from register to the mirror-unregister, and the
|
||||
// register-a-command_id-then-gaccel sequence has one implementation. The
|
||||
// design_view / bank / ingest families still register row-by-row through this.
|
||||
//
|
||||
// * The Q-W6 registration TABLE (ActionTableRow + registerActionTable /
|
||||
// actionTableHandleCommand / actionTableCommandId / unregisterActionTable): the
|
||||
// data-driven home of main.cpp's own action family (capture scopes, panel toggle,
|
||||
// insert, batch, realtime, recapture, version). One row = one action (FOREVER-
|
||||
// STABLE id suffix, display phrase, flat function-pointer handler); registration
|
||||
// iterates the rows, hookcommand dispatch walks the same rows, and unload
|
||||
// mirror-unregisters from them — adding an action touches the table only (OCP).
|
||||
// Handlers are plain function pointers (a static dispatch walk, no std::function,
|
||||
// no virtual — the §3 performance guardrail); gaccel + interned-id storage is
|
||||
// owned here for the module lifetime, so REAPER's held pointers stay valid and
|
||||
// the '-command_id' unregister re-presents the IDENTICAL pointer registered.
|
||||
//
|
||||
// Includes reaper_plugin.h (gaccel_register_t / reaper_plugin_info_t full defs);
|
||||
// only the action-family TUs include this header. Q-W6's registration table
|
||||
// subsumes this helper when the hand-written blocks become data.
|
||||
// only the action-family TUs and main.cpp include this header.
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "reaper_plugin.h" // reaper_plugin_info_t, gaccel_register_t
|
||||
|
||||
@@ -26,4 +43,41 @@ const char* channelIdFor(const char* suffix);
|
||||
int registerAction(reaper_plugin_info_t* rec, const char* suffix,
|
||||
gaccel_register_t& accel, const char* phrase);
|
||||
|
||||
// --- The registration table (Q-W6) -------------------------------------------
|
||||
|
||||
// One bindable action. `suffix` and `phrase` are the channel-AGNOSTIC pieces (the
|
||||
// registry composes the full id/label via channelCommandId / channelActionName);
|
||||
// both must have static storage duration (string literals, or a pure static table
|
||||
// like captureActionTable()). `run` fires when the minted command does; `arg` is an
|
||||
// opaque per-row value passed through to it (e.g. a captureActionTable row index, or
|
||||
// a bool-like flag), so sibling actions can share one handler without captures.
|
||||
struct ActionTableRow {
|
||||
const char* suffix; // FOREVER-STABLE command-id suffix — never change shipped
|
||||
const char* phrase; // Actions-list display phrase (after the channel prefix)
|
||||
void (*run)(int arg); // handler — a flat function pointer, no state
|
||||
int arg = 0; // opaque per-row handler argument
|
||||
};
|
||||
|
||||
// Registers every row (command_id -> gaccel, via the same interning plumbing as
|
||||
// registerAction) in table order. Rows are COPIED into registry-owned storage whose
|
||||
// element addresses never move (REAPER holds each gaccel pointer until unload).
|
||||
// Call once at load; a failed command_id mint (cmd 0) leaves that row inert but
|
||||
// still mirror-unregistered on unload (harmless, matches the pre-table behavior).
|
||||
void registerActionTable(reaper_plugin_info_t* rec, const ActionTableRow* rows,
|
||||
std::size_t count);
|
||||
|
||||
// Dispatches one fired command: fires the matching row's handler and returns true;
|
||||
// false when the command belongs to no table row (caller's hookcommand keeps
|
||||
// looking, per the claim-only contract). A flat walk over the registered rows.
|
||||
bool actionTableHandleCommand(int command);
|
||||
|
||||
// The minted command id for `suffix` (0 when unregistered / mint failed). For the
|
||||
// callers that need a raw command id outside dispatch — e.g. the toggleaction
|
||||
// checked-state hook resolving TOGGLE_BANK_PANEL once at load.
|
||||
int actionTableCommandId(const char* suffix);
|
||||
|
||||
// Mirror-unregisters every table row (reverse table order): '-gaccel' with the same
|
||||
// held storage, '-command_id' with the SAME interned pointer used at register.
|
||||
void unregisterActionTable(reaper_plugin_info_t* rec);
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// bank_actions.cpp — the multi-bank bindable action family (Phase B3; Q-W4 split of
|
||||
// actions.cpp). See bank_actions.h.
|
||||
//
|
||||
// Q-W4 dedupe: each mutating handler is a THIN UX SKIN — text prompts (promptBankName),
|
||||
// name resolution, and console feedback — over the promptless bankOp* inner verbs
|
||||
// homed in panel_bank_ops (model op + persistBankOp, one bank op = one Ctrl-Z). The
|
||||
// book's rules (pool privileges, collapse-by-hash, active-fallback-to-pool) all live
|
||||
// in bank_book; these handlers only drive the verbs and react to the boolean.
|
||||
// Q-W4 dedupe / Q-W6 seam: each mutating handler is a THIN UX SKIN — text prompts
|
||||
// (promptBankName), name resolution, and console feedback — over the promptless
|
||||
// bankOp* inner verbs homed in shell/bank_ops (model op + persistBankOp, one bank op
|
||||
// = one Ctrl-Z), driven against this family's registered session. The book's rules
|
||||
// (pool privileges, collapse-by-hash, active-fallback-to-pool) all live in
|
||||
// bank_book; these handlers only drive the verbs and react to the boolean.
|
||||
//
|
||||
// REFERENCE-INVALIDATION GUARDRAIL (B2 review): book().activeIndex() / bank()->index
|
||||
// return a reference INTO the book's internal vector, which a create/delete can
|
||||
@@ -27,9 +28,10 @@
|
||||
#include "shell/actions/prune_action.h" // doBankPruneFolder — the guarded prune body
|
||||
|
||||
#include "core/model/bank_book.h" // BankBook, nextBankId, kPoolBankId (B1)
|
||||
#include "persist.h" // ReaSamplerSession (owns book())
|
||||
#include "shell/panel/panel_bank_ops.h" // bankOp* inner verbs + promptBankName + selection seam
|
||||
#include "shell/bank_ops/bank_ops.h" // bankOp* promptless verbs (Q-W6 non-UI seam)
|
||||
#include "shell/panel/panel_bank_ops.h" // promptBankName + the panel selection seam
|
||||
#include "shell/panel/panel_layout.h" // full-height toggles (B3)
|
||||
#include "shell/persist/session.h" // ReaSamplerSession (owns book())
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
@@ -61,9 +63,9 @@ constexpr const char* kIdBankBanksFull = "BANK_BANKS_FULLHEIGHT";
|
||||
// and R3 extends the confirm-and-delete step behind this SAME id — never a throwaway id.
|
||||
constexpr const char* kIdBankPruneFolder = "BANK_PRUNE_FOLDER";
|
||||
|
||||
// The live session the actions read (name resolution, member counts, prune). The
|
||||
// mutations themselves run through the bankOp* verbs, which resolve the same session
|
||||
// via the panel seam. Set once by bankRegisterActions; not owned here.
|
||||
// The live session the actions read (name resolution, member counts, prune) and
|
||||
// pass to the bankOp* verbs by reference (bankHandleCommand guards it non-null
|
||||
// before any handler runs). Set once by bankRegisterActions; not owned here.
|
||||
ReaSamplerSession* g_session = nullptr;
|
||||
|
||||
int g_cmdBankCreate = 0;
|
||||
@@ -116,7 +118,7 @@ std::string bankIdByDisplayName(const std::string& name) {
|
||||
void doBankCreate() {
|
||||
std::string name;
|
||||
if (!promptBankName("ReaSampler: create bank", "Bank name:", "", name)) return;
|
||||
if (bankOpCreate(name).empty()) {
|
||||
if (bankOpCreate(*g_session, name).empty()) {
|
||||
ShowConsoleMsg(
|
||||
("ReaSampler: could not create bank \"" + name +
|
||||
"\" (a bank with that name already exists).\n")
|
||||
@@ -139,7 +141,7 @@ void doBankRename() {
|
||||
}
|
||||
std::string newName;
|
||||
if (!promptBankName("ReaSampler: rename bank", "New name:", which, newName)) return;
|
||||
if (!bankOpRename(id, newName)) {
|
||||
if (!bankOpRename(*g_session, id, newName)) {
|
||||
// The verb rejects the pool (un-renamable) or a name already used by another
|
||||
// bank (unique display names, trimmed + case-insensitive).
|
||||
ShowConsoleMsg("ReaSampler: cannot rename that bank (the pool is un-renamable, "
|
||||
@@ -184,7 +186,7 @@ void doBankDelete() {
|
||||
}
|
||||
// S9: bump only when the deleted bank held samples — dropping them changes what a live
|
||||
// instance referencing one could play. Deleting an EMPTY bank is purely organizational.
|
||||
if (!bankOpDelete(id, /*bumpGeneration=*/members > 0)) {
|
||||
if (!bankOpDelete(*g_session, id, /*bumpGeneration=*/members > 0)) {
|
||||
ShowConsoleMsg("ReaSampler: cannot delete that bank (the pool is un-deletable).\n");
|
||||
}
|
||||
}
|
||||
@@ -202,7 +204,7 @@ void doBankEvacuate() {
|
||||
ShowConsoleMsg(("ReaSampler: no bank named \"" + which + "\".\n").c_str());
|
||||
return;
|
||||
}
|
||||
if (!bankOpEvacuate(id)) {
|
||||
if (!bankOpEvacuate(*g_session, id)) {
|
||||
ShowConsoleMsg("ReaSampler: cannot evacuate that bank (the pool is the "
|
||||
"destination, not a source).\n");
|
||||
}
|
||||
@@ -218,13 +220,13 @@ void doBankActivateNext() {
|
||||
for (const Bank& b : g_session->book().banks()) ids.push_back(b.id);
|
||||
const std::string target = nextBankId(ids, g_session->book().activeBankId());
|
||||
if (target.empty()) return; // degenerate (no banks) — cannot happen (pool seeded)
|
||||
bankOpActivate(target);
|
||||
bankOpActivate(*g_session, target);
|
||||
}
|
||||
|
||||
// Activate the pool directly (the common "back to the default target" jump). Bindable
|
||||
// direct-by-id form; a general activate-bank-by-name/menu is a panel affordance.
|
||||
void doBankActivatePool() {
|
||||
bankOpActivate(kPoolBankId);
|
||||
bankOpActivate(*g_session, kPoolBankId);
|
||||
}
|
||||
|
||||
// Move or copy the panel's selected samples into a named destination bank (prompted
|
||||
@@ -257,7 +259,7 @@ void doBankTransferSelected(bool copy) {
|
||||
ShowConsoleMsg("ReaSampler: source and destination are the same bank.\n");
|
||||
return;
|
||||
}
|
||||
bankOpTransfer(selected, srcId, destId, copy);
|
||||
bankOpTransfer(*g_session, selected, srcId, destId, copy);
|
||||
}
|
||||
|
||||
// Remove the panel's selected samples from the SOURCE bank (the focused region's
|
||||
@@ -275,7 +277,7 @@ void doBankRemoveSelected() {
|
||||
ShowConsoleMsg("ReaSampler: the selection's bank no longer exists.\n");
|
||||
return;
|
||||
}
|
||||
bankOpRemove(selected, srcId);
|
||||
bankOpRemove(*g_session, selected, srcId);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include "core/view/lane_keys.h" // view::isOnManualLane — the single managed/manual predicate
|
||||
#include "core/view/view_mode_model.h"
|
||||
#include "persist.h" // ReaSamplerSession (owns view() model)
|
||||
#include "shell/persist/session.h" // ReaSamplerSession (owns view() model)
|
||||
#include "shell/capture/item_read.h" // shared MediaItem* -> GUID + fixed-lane-name reads (D2 W3-B)
|
||||
#include "shell/capture/track_guid.h" // shared MediaTrack* -> canonical GUID key
|
||||
#include "shell/panel/panel_window.h" // bankPanelInvalidate — footer toggle repaint
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "core/namespaces.h"
|
||||
// drag_out_win — OS/COM initiation of native OS drag-out (M11). See drag_out_win.h.
|
||||
//
|
||||
// Windows path (primary): a hand-rolled minimal IDataObject exposing exactly one format,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "core/namespaces.h"
|
||||
// drag_out_win — the OS/COM initiation half of native OS drag-out (Milestone 11). The pure
|
||||
// gesture-boundary decision and path-list assembly live in drag_out.*; THIS is the platform
|
||||
// shell that hands a resolved, existing-file path list to the operating system's drag-drop
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "core/namespaces.h"
|
||||
// instrument_drop_win — the REAPER shell for S17 drop-and-load. See instrument_drop_win.h.
|
||||
//
|
||||
// Compiled into the reaper_reasampler MODULE. Includes reaper_plugin_functions.h WITHOUT
|
||||
@@ -30,6 +29,10 @@
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
// Real-namespace-home using-declarations (Q-W6: the namespaces.h shim is retired).
|
||||
using version::vstPluginName;
|
||||
using wire::infoNamesFxHotspot;
|
||||
|
||||
namespace {
|
||||
|
||||
// Write `bytes` to a fresh uniquely-named .vstpreset in the OS temp dir and return its path;
|
||||
@@ -43,7 +46,7 @@ namespace {
|
||||
//
|
||||
// Non-throwing: every std::filesystem call uses the error_code overload. The whole body is
|
||||
// wrapped in try/catch to guarantee no exception crosses the REAPER C callback boundary
|
||||
// (the same discipline persist.cpp uses — see its non-throwing scanPruneOrphans comment).
|
||||
// (the same discipline the prune shell uses — see prune_fs.cpp's non-throwing scan comment).
|
||||
//
|
||||
// Returns the path object (not a narrow string) so the caller can:
|
||||
// (a) pass path.u8string() to TrackFX_SetPreset — UTF-8 on MSVC, not ACP-converted,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "core/namespaces.h"
|
||||
// instrument_drop_win — the REAPER-facing shell half of S17 drop-and-load. The pure gesture
|
||||
// decision lives in drag_out (DragGesture::InstrumentDrop) and the pure payload construction
|
||||
// in instrument_drop; THIS is the platform shell that (a) resolves a screen point to a track
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "persist.h" // ReaSamplerSession — pruneDryRun / pruneOrphanSet / pruneReclaim
|
||||
#include "shell/persist/session.h" // ReaSamplerSession — pruneDryRun / pruneOrphanSet / pruneReclaim
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
@@ -31,7 +31,7 @@ namespace reasampler {
|
||||
// is opened (file deletion is not REAPER-undoable and pruneReclaim mutates no project
|
||||
// state) — a Ctrl-Z after a prune correctly cannot claim to restore deleted files.
|
||||
void doBankPruneFolder(ReaSamplerSession& session) {
|
||||
const PruneReport report = session.pruneDryRun();
|
||||
const reclaim::PruneReport report = session.pruneDryRun();
|
||||
|
||||
// pS-usage FAIL-SAFE: a present instance-usage record could not be read — the
|
||||
// protected set is unknowable, so the prune HALTS outright (deletes nothing) rather
|
||||
@@ -85,7 +85,7 @@ void doBankPruneFolder(ReaSamplerSession& session) {
|
||||
}
|
||||
|
||||
// Confirmed -> delete exactly the confirmed set (recomputed fresh, stale entries skipped).
|
||||
const PruneDeletionResult del = session.pruneReclaim(orphanSet);
|
||||
const reclaim::PruneDeletionResult del = session.pruneReclaim(orphanSet);
|
||||
|
||||
std::string done = "ReaSampler prune: reclaimed " +
|
||||
std::to_string(del.reclaimedCount) + " file(s), " +
|
||||
|
||||
Reference in New Issue
Block a user