Cut shell/actions, bank_ops, app comment bloat ~48% (comments only, zero code change)
This commit is contained in:
@@ -1,24 +1,16 @@
|
||||
// design_view_actions.cpp — the Design View action family (Phase D4; Q-W4 split of
|
||||
// actions.cpp). See design_view_actions.h.
|
||||
// design_view_actions.cpp — see design_view_actions.h.
|
||||
//
|
||||
// Compiled into the reaper_reasampler MODULE. Includes reaper_plugin_functions.h
|
||||
// WITHOUT REAPERAPI_IMPLEMENT — main.cpp is the one TU that defines the API pointers
|
||||
// (CLAUDE.md §contract). The action ids are minted from FOREVER-STABLE strings (the
|
||||
// same CEREBELLUM_REASAMPLER_ family prefix main.cpp uses); user keybindings key off
|
||||
// them, so they must never change after ship.
|
||||
// main.cpp owns the API pointers; this TU gets them extern. Action ids are minted
|
||||
// from FOREVER-STABLE strings — never change one after ship.
|
||||
//
|
||||
// Each action:
|
||||
// 1. mutates the session's ViewModeModel (membership tag/untag/show-both, or the
|
||||
// active mode via toggle/activate) — the pure D1 state,
|
||||
// 2. reapplies the active mode through the D2 view shell (applyMode) so the change
|
||||
// takes visible effect immediately (tagging a track into Design while in Arrange
|
||||
// parks it right away; a mode change re-partitions and re-parks in one step).
|
||||
// Each action mutates the session's ViewModeModel (membership tag/untag/show-both, or
|
||||
// the active mode via toggle/activate), then reapplies the active mode through the
|
||||
// view shell (applyMode) so the change takes visible effect immediately.
|
||||
//
|
||||
// Selection-driven mutations iterate the CURRENT REAPER track selection
|
||||
// (CountSelectedTracks/GetSelectedTrack — both ignore the master, which is correct:
|
||||
// the master is never tagged) and resolve each track to its canonical GUID key via
|
||||
// the shared guidString helper, so the keys match exactly what the D2 shell / view
|
||||
// tree key on (the cross-module key contract).
|
||||
// (CountSelectedTracks/GetSelectedTrack ignore the master, which is correct — the
|
||||
// master is never tagged) and resolve each track to its canonical GUID key so the
|
||||
// keys match what the view shell / view tree key on.
|
||||
|
||||
#include "shell/actions/design_view_actions.h"
|
||||
|
||||
@@ -55,11 +47,8 @@ using view::isOnManualLane;
|
||||
|
||||
namespace {
|
||||
|
||||
// FOREVER-STABLE action-id SUFFIXES (Phase V, V4). The channel family prefix is prepended
|
||||
// at register time via channelCommandId (app_version), so stable rebuilds the exact shipped
|
||||
// id ("CEREBELLUM_REASAMPLER_VIEW_TOGGLE_MODE") and beta yields the isolated forever-family
|
||||
// id ("CEREBELLUM_REASAMPLER_BETA_VIEW_TOGGLE_MODE"). Each composed id is minted into a
|
||||
// persistent command id user keybindings key off — NEVER change a shipped suffix after ship.
|
||||
// FOREVER-STABLE action-id SUFFIXES: the channel prefix is prepended at register
|
||||
// (channelCommandId) — NEVER change a shipped suffix.
|
||||
constexpr const char* kIdToggleMode = "VIEW_TOGGLE_MODE";
|
||||
constexpr const char* kIdActivateArrange = "VIEW_ACTIVATE_ARRANGE";
|
||||
constexpr const char* kIdActivateDesign = "VIEW_ACTIVATE_DESIGN";
|
||||
@@ -67,17 +56,14 @@ constexpr const char* kIdTagDesign = "VIEW_TAG_DESIGN";
|
||||
constexpr const char* kIdTagArrange = "VIEW_TAG_ARRANGE";
|
||||
constexpr const char* kIdUntag = "VIEW_UNTAG";
|
||||
constexpr const char* kIdShowBoth = "VIEW_SHOW_BOTH";
|
||||
// D2 Wave 3-B item-level mode moves — the item analog of the track tag family. Same
|
||||
// FOREVER-STABLE contract (suffix composed with the channel prefix) — NEVER change these.
|
||||
// Item-level mode moves — the item analog of the track tag family above.
|
||||
constexpr const char* kIdMoveItemsDesign = "VIEW_MOVE_ITEMS_DESIGN";
|
||||
constexpr const char* kIdMoveItemsArrange = "VIEW_MOVE_ITEMS_ARRANGE";
|
||||
constexpr const char* kIdUntagItems = "VIEW_UNTAG_ITEMS";
|
||||
|
||||
// The live session the actions mutate. Set once by designViewRegisterActions and
|
||||
// read by the hookcommand handler. Not owned here (main.cpp owns g_session).
|
||||
// Not owned here (main.cpp owns g_session).
|
||||
ReaSamplerSession* g_session = nullptr;
|
||||
|
||||
// Minted command ids (0 until registration succeeds). Compared in the handler.
|
||||
int g_cmdToggleMode = 0;
|
||||
int g_cmdActivateArrange = 0;
|
||||
int g_cmdActivateDesign = 0;
|
||||
@@ -102,9 +88,6 @@ gaccel_register_t g_accelMoveItemsDesign{};
|
||||
gaccel_register_t g_accelMoveItemsArrange{};
|
||||
gaccel_register_t g_accelUntagItems{};
|
||||
|
||||
// Collects the canonical GUID keys of the current track selection. Empty if nothing
|
||||
// is selected. CountSelectedTracks/GetSelectedTrack ignore the master (SDK), which is
|
||||
// exactly right — the master is never a tagged leaf.
|
||||
std::vector<std::string> selectedTrackGuids() {
|
||||
std::vector<std::string> guids;
|
||||
const int n = CountSelectedTracks(nullptr); // nullptr = active project
|
||||
@@ -118,22 +101,17 @@ std::vector<std::string> selectedTrackGuids() {
|
||||
return guids;
|
||||
}
|
||||
|
||||
// Reapplies the model's CURRENT active mode to the active project so a membership
|
||||
// mutation takes visible effect immediately (park/unpark/re-derive parents). Called
|
||||
// after every tag/untag/show-both. `proj = nullptr` -> REAPER's active project.
|
||||
// Reapplies the CURRENT active mode so a membership mutation takes visible effect
|
||||
// immediately (park/unpark/re-derive parents).
|
||||
void reapplyActiveMode() {
|
||||
applyMode(g_session->view(), g_session->view().activeModeId(), nullptr);
|
||||
}
|
||||
|
||||
// Track fixed-lane mode value (I_FREEMODE=2). Mirrors the shell's constant; used only to
|
||||
// decide whether an item's lane name is meaningful for the manual-lane read.
|
||||
constexpr int kFreeModeFixedLanes = 2;
|
||||
constexpr int kFreeModeFixedLanes = 2; // I_FREEMODE value; mirrors the shell's constant
|
||||
|
||||
// Collects the current media-item selection as the pure decision's input: each selected
|
||||
// item's GUID plus whether it sits on a MANUAL lane (⇒ EXEMPT — never retagged/re-laned).
|
||||
// The manual-lane read follows the shared pure predicate exactly as the shell's readers
|
||||
// do: only on a fixed-lane track (I_FREEMODE==2) is the item's lane name read; on a normal
|
||||
// track isOnManualLane returns false for the empty name, so the P_LANENAME read is skipped.
|
||||
// Each selected item's GUID plus whether it sits on a MANUAL lane (EXEMPT — never
|
||||
// retagged/re-laned). The lane name is read only on a fixed-lane track; on a normal
|
||||
// track the shared predicate returns false for an empty name, so the read is skipped.
|
||||
// Items whose GUID cannot be read are dropped (an empty GUID must never be retagged).
|
||||
std::vector<RetagItem> selectedRetagItems() {
|
||||
std::vector<RetagItem> items;
|
||||
@@ -148,33 +126,18 @@ std::vector<RetagItem> selectedRetagItems() {
|
||||
MediaTrack* tr = GetMediaItemTrack(it);
|
||||
const bool fixedLane =
|
||||
tr && static_cast<int>(GetMediaTrackInfo_Value(tr, "I_FREEMODE")) == kFreeModeFixedLanes;
|
||||
// Only read the lane name on a fixed-lane track; the pure predicate handles the
|
||||
// normal-track case (returns false) so we pass an empty name and skip the read.
|
||||
const std::string laneNm = fixedLane ? itemLaneName(tr, it) : std::string{};
|
||||
items.push_back(RetagItem{std::move(g), isOnManualLane(fixedLane, laneNm)});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
// Persists both the bank and the Design-View model to the active project's ext
|
||||
// state. Called after every state-changing Design View action so the view model
|
||||
// is not lost across save/close/reopen. Marking the project dirty is correct —
|
||||
// a Design View mutation is a project-level change the user should be prompted
|
||||
// to save.
|
||||
//
|
||||
// When the membership index is non-empty AND the project is unsaved, we prompt
|
||||
// the user to Save-As before persisting — mirroring the flow capture uses.
|
||||
// Gate: if membership is empty (no tracks tagged), skip the prompt entirely;
|
||||
// saveToActiveProject will no-op for an unsaved project, which is correct.
|
||||
//
|
||||
// DAW-ONLY ASSUMPTION: Main_SaveProject(proj, true) opens a Save-As dialog and
|
||||
// blocks until the user dismisses it. The blocking behaviour and dialog
|
||||
// appearance can only be confirmed in a running REAPER (same caveat as capture).
|
||||
// Persists the bank + Design-View model after every state-changing action so the
|
||||
// view model is not lost across save/close/reopen. If membership is non-empty and
|
||||
// the project is unsaved, prompts Save-As first (mirrors the flow capture uses) —
|
||||
// DAW-ONLY: Main_SaveProject(proj, true) blocks until the dialog is dismissed.
|
||||
void persistViewState() {
|
||||
if (!g_session->view().membership().empty()) {
|
||||
// At least one track is tagged — worth persisting. Check whether the
|
||||
// project is saved and, if not, prompt Save-As so saveToActiveProject
|
||||
// can write ext state. Mirrors capture's readRppPath idiom exactly.
|
||||
ReaProject* proj = EnumProjects(-1, nullptr, 0);
|
||||
if (proj) {
|
||||
auto readRppPath = [&]() -> std::string {
|
||||
@@ -184,15 +147,11 @@ void persistViewState() {
|
||||
};
|
||||
|
||||
if (readRppPath().empty()) {
|
||||
// Project is unsaved — prompt Save-As.
|
||||
Main_SaveProject(proj, true);
|
||||
// Re-read: still empty means the user cancelled.
|
||||
if (readRppPath().empty()) {
|
||||
if (readRppPath().empty()) { // still empty -> user cancelled
|
||||
ShowConsoleMsg(
|
||||
"ReaSampler: Design View state will not persist until "
|
||||
"the project is saved.\n");
|
||||
// The in-session tag state is left as-is — the mode change
|
||||
// already applied and remains valid for this session.
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -201,11 +160,8 @@ void persistViewState() {
|
||||
g_session->saveToActiveProject();
|
||||
}
|
||||
|
||||
// -- Action bodies ---------------------------------------------------------
|
||||
|
||||
// Toggle: cycle to the next mode in ordinal order (Arrange <-> Design with two
|
||||
// seeds; scales to cycle-through-all for >2 modes with no change here). applyMode
|
||||
// itself sets the model's active mode, so we only compute the target and apply.
|
||||
// Cycle to the next mode in ordinal order. applyMode itself sets the model's active
|
||||
// mode, so we only compute the target and apply.
|
||||
void doToggleMode() {
|
||||
const std::string target =
|
||||
nextModeId(g_session->view().modes(), g_session->view().activeModeId());
|
||||
@@ -223,9 +179,8 @@ void doActivateMode(const std::string& modeId) {
|
||||
bankPanelInvalidate(); // repaint the footer [Arrange|Design] toggle immediately
|
||||
}
|
||||
|
||||
// Tag the selection's leaves into `modeId`, then reapply so the change is immediate.
|
||||
// tag() replaces any prior single-mode membership (a leaf lives in one mode; the
|
||||
// cross-mode case is show-both), matching the D1 contract.
|
||||
// cross-mode case is show-both).
|
||||
void doTag(const std::string& modeId) {
|
||||
for (const std::string& g : selectedTrackGuids())
|
||||
g_session->view().membership().tag(g, modeId);
|
||||
@@ -233,9 +188,8 @@ void doTag(const std::string& modeId) {
|
||||
persistViewState();
|
||||
}
|
||||
|
||||
// Untag the selection entirely (return each to the Arrange default). This is the
|
||||
// shared body behind both "Untag selected" and "Tag -> Arrange" (Arrange = the
|
||||
// absence of a tag), so the two actions are the same act by definition.
|
||||
// Shared body behind "Untag selected" and "Tag -> Arrange" — Arrange is the absence
|
||||
// of a tag, so the two actions are the same act.
|
||||
void doUntag() {
|
||||
for (const std::string& g : selectedTrackGuids())
|
||||
g_session->view().membership().untag(g);
|
||||
@@ -243,11 +197,8 @@ void doUntag() {
|
||||
persistViewState();
|
||||
}
|
||||
|
||||
// Toggle the per-track show-both pin for the selection. Read the CURRENT pin of each
|
||||
// track and flip it independently (a mixed selection converges toward "all on" then
|
||||
// "all off" only if uniform; per-track flip is the honest semantics of a toggle on a
|
||||
// multi-selection). show-both leaves are never parked (D1), so reapply reflects the
|
||||
// change immediately.
|
||||
// Flips each track's pin independently — the honest semantics of a toggle on a
|
||||
// multi-selection (a mixed selection converges toward uniform only if it already was).
|
||||
void doShowBoth() {
|
||||
MembershipIndex& m = g_session->view().membership();
|
||||
for (const std::string& g : selectedTrackGuids())
|
||||
@@ -256,19 +207,12 @@ void doShowBoth() {
|
||||
persistViewState();
|
||||
}
|
||||
|
||||
// -- Item-level mode moves (D2 Wave 3-B) -----------------------------------
|
||||
// Retag the current ITEM selection to `targetMode` (empty => untag -> Arrange
|
||||
// default). planItemRetag decides which items to retag (manual-lane items are
|
||||
// EXEMPT), upholding the managed-lanes-only invariant. Wrapped in ONE Undo block.
|
||||
//
|
||||
// Retag the current ITEM selection to `targetMode` (empty ⇒ untag → Arrange default),
|
||||
// then re-drive the minting + apply path so each moved item lands on its target mode's
|
||||
// managed lane and the active-mode lane visibility is reasserted. The pure planItemRetag
|
||||
// decides which selected items to retag (manual-lane items are EXEMPT — never retagged,
|
||||
// never re-laned), upholding the managed-lanes-only invariant even under this explicit
|
||||
// user action. The whole structural act is wrapped in ONE Undo block with a descriptive
|
||||
// label (the inner blocks mintManagedLanes / applyMode open nest harmlessly under it).
|
||||
//
|
||||
// UNDO/SAVE ordering: persistViewState may pop a Save-As dialog (Main_SaveProject) which
|
||||
// must NOT sit inside the Undo block, so we close the block first, then persist — the same
|
||||
// separation the track actions rely on (they persist outside applyMode's own block).
|
||||
// UNDO/SAVE ordering: persistViewState may pop a Save-As dialog, which must NOT sit
|
||||
// inside the Undo block, so we close the block first, then persist.
|
||||
void doMoveItems(const std::string& targetMode) {
|
||||
const std::vector<RetagItem> selected = selectedRetagItems();
|
||||
const std::vector<ItemRetagOp> ops = planItemRetag(selected, targetMode);
|
||||
@@ -277,15 +221,12 @@ void doMoveItems(const std::string& targetMode) {
|
||||
MembershipIndex& membership = g_session->view().membership();
|
||||
|
||||
Undo_BeginBlock2(nullptr);
|
||||
// Apply the pure decision's membership writes: tag into targetMode, or untag.
|
||||
for (const ItemRetagOp& op : ops) {
|
||||
if (op.untag) membership.untag(op.guid);
|
||||
else membership.tag(op.guid, op.modeId);
|
||||
}
|
||||
// Re-drive the SAME minting/apply path auto-tag uses: mint/split lanes for any track
|
||||
// whose items now span modes and assign each moved item to its mode's managed lane,
|
||||
// then reassert the active mode's lane visibility. Manual lanes stay untouched
|
||||
// (mintManagedLanes reports their items exempt and never mints over them).
|
||||
// Re-drive the same minting/apply path auto-tag uses: mint/split lanes for any
|
||||
// track whose items now span modes, then reassert active-mode lane visibility.
|
||||
mintManagedLanes(g_session->view(), nullptr);
|
||||
reapplyActiveMode();
|
||||
|
||||
@@ -303,8 +244,6 @@ void doMoveItems(const std::string& targetMode) {
|
||||
void designViewRegisterActions(reaper_plugin_info_t* rec, ReaSamplerSession* session) {
|
||||
g_session = session;
|
||||
|
||||
// command_id -> gaccel for each. The single hookcommand that routes these lives
|
||||
// in main.cpp (one hook per extension); designViewHandleCommand services them.
|
||||
g_cmdToggleMode = registerAction(rec, kIdToggleMode, g_accelToggleMode,
|
||||
"toggle Design View mode");
|
||||
g_cmdActivateArrange = registerAction(rec, kIdActivateArrange, g_accelActivateArrange,
|
||||
@@ -320,7 +259,6 @@ void designViewRegisterActions(reaper_plugin_info_t* rec, ReaSamplerSession* ses
|
||||
g_cmdShowBoth = registerAction(rec, kIdShowBoth, g_accelShowBoth,
|
||||
"show both for selected tracks");
|
||||
|
||||
// Item-level mode moves (D2 W3-B): the item analog of the track tag family.
|
||||
g_cmdMoveItemsDesign = registerAction(rec, kIdMoveItemsDesign, g_accelMoveItemsDesign,
|
||||
"move selected items -> Design");
|
||||
g_cmdMoveItemsArrange = registerAction(rec, kIdMoveItemsArrange, g_accelMoveItemsArrange,
|
||||
@@ -336,13 +274,10 @@ bool designViewHandleCommand(int command) {
|
||||
if (command == g_cmdActivateArrange) { doActivateMode(kArrangeModeId); return true; }
|
||||
if (command == g_cmdActivateDesign) { doActivateMode(kDesignModeId); return true; }
|
||||
if (command == g_cmdTagDesign) { doTag(kDesignModeId); return true; }
|
||||
// Tag -> Arrange and Untag are the same act (Arrange = the absence of a tag).
|
||||
if (command == g_cmdTagArrange) { doUntag(); return true; }
|
||||
if (command == g_cmdUntag) { doUntag(); return true; }
|
||||
if (command == g_cmdShowBoth) { doShowBoth(); return true; }
|
||||
|
||||
// Item-level moves. Move -> Arrange and Untag items collapse to the same act (an
|
||||
// empty target ⇒ untag ⇒ Arrange default), mirroring the track-level pairing above.
|
||||
if (command == g_cmdMoveItemsDesign) { doMoveItems(kDesignModeId); return true; }
|
||||
if (command == g_cmdMoveItemsArrange) { doMoveItems(std::string{}); return true; }
|
||||
if (command == g_cmdUntagItems) { doMoveItems(std::string{}); return true; }
|
||||
@@ -351,11 +286,8 @@ bool designViewHandleCommand(int command) {
|
||||
}
|
||||
|
||||
void designViewUnregisterActions(reaper_plugin_info_t* rec) {
|
||||
// Mirror-unregister with '-'-prefixed strings, per the contract's unload rule.
|
||||
// gaccel first, then the command_id string (reverse of registration order — the item
|
||||
// moves registered last, so they tear down first).
|
||||
// Each '-command_id' re-presents the SAME interned, channel-qualified id (channelIdFor
|
||||
// returns the memoized pointer registered above), so the unregister matches exactly.
|
||||
// Reverse registration order; each '-command_id' re-presents the SAME interned
|
||||
// pointer channelIdFor returned above.
|
||||
rec->Register("-gaccel", (void*)&g_accelUntagItems);
|
||||
rec->Register("-command_id", (void*)channelIdFor(kIdUntagItems));
|
||||
rec->Register("-gaccel", (void*)&g_accelMoveItemsArrange);
|
||||
|
||||
Reference in New Issue
Block a user