Design View: expose a park-shaped no-record track visible instead of adopting or refusing it
Adopting silently wipes hand repairs or hides untouched tracks forever; refusing was rejected too. EXPOSE hands the track back and self-heals on the next apply.
This commit is contained in:
@@ -3,12 +3,15 @@
|
||||
|
||||
#include "shell/view/view_park.h"
|
||||
|
||||
#include <cstring> // strnlen — bounded read of GetTrackName's buffer
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_GetMediaTrackInfo_Value
|
||||
#define REAPERAPI_WANT_GetTrackName
|
||||
#define REAPERAPI_WANT_SetMediaTrackInfo_Value
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
#define REAPERAPI_WANT_TrackFX_GetCount
|
||||
#define REAPERAPI_WANT_TrackFX_GetFXGUID
|
||||
#define REAPERAPI_WANT_TrackFX_GetOffline
|
||||
@@ -73,6 +76,22 @@ bool applyFlags(MediaTrack* tr, const std::vector<TrackFlagOp>& flags) {
|
||||
return wrote;
|
||||
}
|
||||
|
||||
// The visible/active mirror of a park write: same four ops, value 1 instead of
|
||||
// whatever the plan says (always 0 for a park plan — see makeParkPlan). Reusing
|
||||
// the incoming park ops rather than minting a separate "expose plan" keeps the
|
||||
// two writes provably addressing the same four parameters.
|
||||
bool exposeFlags(MediaTrack* tr, const std::vector<TrackFlagOp>& parkFlags) {
|
||||
bool wrote = false;
|
||||
for (const TrackFlagOp& op : parkFlags)
|
||||
if (writeIfChanged(tr, trackFlagParm(op.flag), 1.0)) wrote = true;
|
||||
return wrote;
|
||||
}
|
||||
|
||||
// The last EXPOSE report, so an unchanged set is not reprinted on every reapply
|
||||
// the track hasn't yet self-healed from.
|
||||
ReaProject* g_reportedOwner = nullptr;
|
||||
std::vector<std::string> g_reportedExposed;
|
||||
|
||||
} // namespace
|
||||
|
||||
const int kViewUndoMask = UNDO_STATE_TRACKCFG | UNDO_STATE_ITEMS | UNDO_STATE_MISCCFG;
|
||||
@@ -83,11 +102,28 @@ bool writeIfChanged(MediaTrack* tr, const char* parm, double value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parkTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
const std::vector<TrackFlagOp>& flags) {
|
||||
bool wrote = false;
|
||||
TrackParkResult parkTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
const std::vector<TrackFlagOp>& flags) {
|
||||
TrackParkResult result;
|
||||
|
||||
if (!model.holdsSnapshotFlags(guid)) {
|
||||
const TrackSnapshot* prior = model.snapshot(guid);
|
||||
|
||||
// No record of ANY kind (see parkTrack's header doc for why prior == nullptr
|
||||
// is the right gate): the ambiguous case. A live chain already reading
|
||||
// park-shaped is EXPOSEd — handed back visible, no snapshot stored — rather
|
||||
// than adopted as the user's intent.
|
||||
if (!prior) {
|
||||
auto read = [tr](Flag f) {
|
||||
return static_cast<int>(GetMediaTrackInfo_Value(tr, trackFlagParm(f)));
|
||||
};
|
||||
if (chainReadsParked(flags, read)) {
|
||||
if (exposeFlags(tr, flags)) result.wrote = true;
|
||||
result.exposed = true;
|
||||
return result; // this cycle hands the track back; it does not park it
|
||||
}
|
||||
}
|
||||
|
||||
TrackSnapshot snap = snapshotTrack(tr);
|
||||
|
||||
// A restore hands the flags back immediately but leaves the chain offline
|
||||
@@ -100,7 +136,6 @@ bool parkTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
// — so the false arm is unreached. Kept anyway: it is the direct
|
||||
// statement of what this branch actually needs, and cheaper to keep true
|
||||
// than to re-derive if that guarantee ever loosens.
|
||||
const TrackSnapshot* prior = model.snapshot(guid);
|
||||
if (prior && prior->fxHeld && prior->fxKeying == FxKeying::Identity) {
|
||||
snap.fxOffline = unionFxOfflineOnRepark(prior->fxOffline, snap.fxOffline);
|
||||
snap.fxKeying = FxKeying::Identity;
|
||||
@@ -112,11 +147,11 @@ bool parkTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
snap.fxKeying = prior->fxKeying;
|
||||
}
|
||||
model.storeSnapshot(guid, snap);
|
||||
wrote = true;
|
||||
result.wrote = true;
|
||||
}
|
||||
|
||||
if (applyFlags(tr, flags)) wrote = true;
|
||||
return wrote;
|
||||
if (applyFlags(tr, flags)) result.wrote = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool restoreTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
@@ -126,4 +161,22 @@ bool restoreTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string trackDisplayName(MediaTrack* tr) {
|
||||
std::vector<char> buf(1024, '\0');
|
||||
if (!GetTrackName || !GetTrackName(tr, buf.data(), static_cast<int>(buf.size())))
|
||||
return "(name unavailable)";
|
||||
return std::string(buf.data(), strnlen(buf.data(), buf.size()));
|
||||
}
|
||||
|
||||
void reportExposedTracks(ReaProject* proj, const std::vector<std::string>& trackNames) {
|
||||
if (!shouldReport(proj == g_reportedOwner, g_reportedExposed, trackNames)) return;
|
||||
g_reportedOwner = proj;
|
||||
g_reportedExposed = trackNames;
|
||||
|
||||
const std::string msg = describeExposedTracks(trackNames);
|
||||
// "!SHOW:" so an unattended project-load reapply cannot force the console open
|
||||
// (reaper_plugin_functions.h:6536).
|
||||
if (!msg.empty()) ShowConsoleMsg(("!SHOW:" + msg).c_str());
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
Reference in New Issue
Block a user