Merge drop-onto-panel bank-fill only

Dropping audio onto the bank panel imports to the bank without
force-selecting on any live instance; generation bump retained so open
instances see the new sounds.
This commit is contained in:
2026-07-27 14:02:44 -04:00
2 changed files with 27 additions and 40 deletions
+6 -5
View File
@@ -3199,10 +3199,11 @@ void handleRightClick(int x, int y) {
// --- Dialog proc + docking ---------------------------------------------------- // --- Dialog proc + docking ----------------------------------------------------
// Decodes a WM_DROPFILES HDROP into the dropped file paths (absolute, OS-native) and hands // Decodes a WM_DROPFILES HDROP into the dropped file paths (absolute, OS-native) and hands
// them to the S8 ingest path. Multi-file drop: ingestDroppedFiles imports all and assigns // them to the S8 ingest path. Multi-file drop: ingestDroppedFiles imports all into the active
// the first. Always DragFinish's the HDROP (frees the shell-allocated drop buffer) on every // bank (bank-fill only — no assignment to any live instance). Always DragFinish's the HDROP
// path. DragQueryFile(hDrop, 0xFFFFFFFF, ...) returns the file count; then each path is // (frees the shell-allocated drop buffer) on every path. DragQueryFile(hDrop, 0xFFFFFFFF, ...)
// queried by index. Both Win32 and SWELL expose DragQueryFile/DragFinish with this contract. // returns the file count; then each path is queried by index. Both Win32 and SWELL expose
// DragQueryFile/DragFinish with this contract.
void handleDropFiles(HDROP hDrop) { void handleDropFiles(HDROP hDrop) {
std::vector<std::string> paths; std::vector<std::string> paths;
const UINT count = DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0); const UINT count = DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0);
@@ -3224,7 +3225,7 @@ WDL_DLGRET dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) { switch (msg) {
case WM_DROPFILES: case WM_DROPFILES:
// S8 drop-onto-panel ingest: OS file drop on the docked panel HWND -> import // S8 drop-onto-panel ingest: OS file drop on the docked panel HWND -> import
// into the active bank + assign the first. wParam is the HDROP. // into the active bank (bank-fill only). wParam is the HDROP.
handleDropFiles(reinterpret_cast<HDROP>(wParam)); handleDropFiles(reinterpret_cast<HDROP>(wParam));
return 0; return 0;
case WM_PAINT: { case WM_PAINT: {
+21 -35
View File
@@ -532,12 +532,11 @@ void ingestAssignActiveInstance(const std::string& bankId, const std::string& sa
void ingestDroppedFiles(const std::vector<std::string>& absolutePaths) { void ingestDroppedFiles(const std::vector<std::string>& absolutePaths) {
if (!g_session || absolutePaths.empty()) return; if (!g_session || absolutePaths.empty()) return;
// Import ALL dropped files; assign the FIRST successfully-imported one (documented // Import ALL dropped files into the active bank — bank-fill only. No assignment_request
// multi-file policy). Batch the persist + undo point: many imports are ONE undo entry. // is written on this path; the drop has no effect on what any live instance plays.
std::string firstAssignId; // Batch the persist + undo point: many imports are ONE undo entry.
std::string firstAssignBank; int importedNew = 0;
int importedNew = 0; int importedTotal = 0;
int importedTotal = 0; // includes dedup collapses that still yielded an id to assign
std::string lastFailure; std::string lastFailure;
for (const std::string& path : absolutePaths) { for (const std::string& path : absolutePaths) {
@@ -548,44 +547,31 @@ void ingestDroppedFiles(const std::vector<std::string>& absolutePaths) {
} }
++importedTotal; ++importedTotal;
if (r.added) ++importedNew; if (r.added) ++importedNew;
if (firstAssignId.empty()) {
firstAssignId = r.sampleId;
firstAssignBank = g_session->book().activeBankId();
}
} }
// One undo point for the whole drop, opened only if a NEW index entry was created (a // One undo point for the whole drop, opened only if a NEW index entry was created (a
// drop that only re-hit existing content mutated nothing on the index). The assign // drop that only re-hit existing content mutated nothing on the index).
// request is written INSIDE the same block so Ctrl-Z rolls back both keys together:
// undo restores `banks` (removing the new samples) AND clears the `assign_request` that
// named one of them, so no stale request survives pointing to a removed sample.
// If saveToActiveProject() no-ops (unsaved project), we close with an empty label + zero // If saveToActiveProject() no-ops (unsaved project), we close with an empty label + zero
// flag so REAPER discards the undo entry (house pattern from actions.cpp). // flag so REAPER discards the undo entry (house pattern from actions.cpp).
if (!firstAssignId.empty()) { if (importedNew > 0) {
if (importedNew > 0) { Undo_BeginBlock2(nullptr);
Undo_BeginBlock2(nullptr); // S9: one coalesced generation bump for the whole drop (>=1 new sample landed) so
// S9: one coalesced bump for the whole drop (>=1 new sample landed) inside the // open VST3 browser instances refresh to show the newly available sounds.
// block so the generation refreshes the assigned instance and undo rolls it back. g_session->bumpBankGeneration();
g_session->bumpBankGeneration(); const bool persisted = g_session->saveToActiveProject();
const bool persisted = g_session->saveToActiveProject(); if (persisted)
// Assign inside the block: undo restores both keys atomically. Undo_EndBlock2(nullptr, "ReaSampler: import dropped file(s)", UNDO_STATE_MISCCFG);
ingestAssignActiveInstance(firstAssignBank, firstAssignId); else
if (persisted) Undo_EndBlock2(nullptr, "", 0);
Undo_EndBlock2(nullptr, "ReaSampler: import dropped file(s)",
UNDO_STATE_MISCCFG);
else
Undo_EndBlock2(nullptr, "", 0);
} else {
// All dropped files deduplicated: index unchanged, no undo point needed. Still
// assign so the user sees the sample is already in the bank.
ingestAssignActiveInstance(firstAssignBank, firstAssignId);
}
bankPanelRefresh(); bankPanelRefresh();
const std::string msg = const std::string msg =
"ReaSampler ingest: imported " + std::to_string(importedTotal) + "ReaSampler ingest: imported " + std::to_string(importedTotal) +
(importedTotal == 1 ? " file" : " files") + (importedTotal == 1 ? " file" : " files") + " into the bank.\n";
" and assigned the first to the active instance.\n";
ShowConsoleMsg(msg.c_str()); ShowConsoleMsg(msg.c_str());
} else if (importedTotal > 0) {
// All dropped files were already in the bank (deduplicated); nothing changed.
bankPanelRefresh();
ShowConsoleMsg("ReaSampler ingest: all dropped files already in the bank.\n");
} else { } else {
ShowConsoleMsg(("ReaSampler ingest: nothing imported from the drop -- " + ShowConsoleMsg(("ReaSampler ingest: nothing imported from the drop -- " +
(lastFailure.empty() ? std::string("no usable files") : lastFailure) + (lastFailure.empty() ? std::string("no usable files") : lastFailure) +