diff --git a/src/bank_panel.cpp b/src/bank_panel.cpp index 2b9e343..cf2fc52 100644 --- a/src/bank_panel.cpp +++ b/src/bank_panel.cpp @@ -3199,10 +3199,11 @@ void handleRightClick(int x, int y) { // --- Dialog proc + docking ---------------------------------------------------- // 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 -// the first. Always DragFinish's the HDROP (frees the shell-allocated drop buffer) on every -// path. DragQueryFile(hDrop, 0xFFFFFFFF, ...) returns the file count; then each path is -// queried by index. Both Win32 and SWELL expose DragQueryFile/DragFinish with this contract. +// them to the S8 ingest path. Multi-file drop: ingestDroppedFiles imports all into the active +// bank (bank-fill only — no assignment to any live instance). Always DragFinish's the HDROP +// (frees the shell-allocated drop buffer) on every path. DragQueryFile(hDrop, 0xFFFFFFFF, ...) +// 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) { std::vector paths; 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) { case WM_DROPFILES: // 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(wParam)); return 0; case WM_PAINT: { diff --git a/src/ingest.cpp b/src/ingest.cpp index 1b00973..cac3177 100644 --- a/src/ingest.cpp +++ b/src/ingest.cpp @@ -532,12 +532,11 @@ void ingestAssignActiveInstance(const std::string& bankId, const std::string& sa void ingestDroppedFiles(const std::vector& absolutePaths) { if (!g_session || absolutePaths.empty()) return; - // Import ALL dropped files; assign the FIRST successfully-imported one (documented - // multi-file policy). Batch the persist + undo point: many imports are ONE undo entry. - std::string firstAssignId; - std::string firstAssignBank; - int importedNew = 0; - int importedTotal = 0; // includes dedup collapses that still yielded an id to assign + // Import ALL dropped files into the active bank — bank-fill only. No assignment_request + // is written on this path; the drop has no effect on what any live instance plays. + // Batch the persist + undo point: many imports are ONE undo entry. + int importedNew = 0; + int importedTotal = 0; std::string lastFailure; for (const std::string& path : absolutePaths) { @@ -548,44 +547,31 @@ void ingestDroppedFiles(const std::vector& absolutePaths) { } ++importedTotal; 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 - // drop that only re-hit existing content mutated nothing on the index). The assign - // 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. + // drop that only re-hit existing content mutated nothing on the index). // 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). - if (!firstAssignId.empty()) { - if (importedNew > 0) { - Undo_BeginBlock2(nullptr); - // S9: one coalesced bump for the whole drop (>=1 new sample landed) inside the - // block so the generation refreshes the assigned instance and undo rolls it back. - g_session->bumpBankGeneration(); - const bool persisted = g_session->saveToActiveProject(); - // Assign inside the block: undo restores both keys atomically. - ingestAssignActiveInstance(firstAssignBank, firstAssignId); - if (persisted) - 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); - } + if (importedNew > 0) { + Undo_BeginBlock2(nullptr); + // S9: one coalesced generation bump for the whole drop (>=1 new sample landed) so + // open VST3 browser instances refresh to show the newly available sounds. + g_session->bumpBankGeneration(); + const bool persisted = g_session->saveToActiveProject(); + if (persisted) + Undo_EndBlock2(nullptr, "ReaSampler: import dropped file(s)", UNDO_STATE_MISCCFG); + else + Undo_EndBlock2(nullptr, "", 0); bankPanelRefresh(); const std::string msg = "ReaSampler ingest: imported " + std::to_string(importedTotal) + - (importedTotal == 1 ? " file" : " files") + - " and assigned the first to the active instance.\n"; + (importedTotal == 1 ? " file" : " files") + " into the bank.\n"; 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 { ShowConsoleMsg(("ReaSampler ingest: nothing imported from the drop -- " + (lastFailure.empty() ? std::string("no usable files") : lastFailure) +