diff --git a/src/bank_panel.cpp b/src/bank_panel.cpp index 4b2aef6..0db2cda 100644 --- a/src/bank_panel.cpp +++ b/src/bank_panel.cpp @@ -991,6 +991,7 @@ std::string mintBankId() { } void doCreateBank() { + if (!book()) return; std::string name; if (!promptText("ReaSampler: create bank", "Bank name:", "", name)) return; const std::string id = mintBankId(); @@ -1006,6 +1007,7 @@ void doCreateBank() { } void doRenameBank(const std::string& bankId) { + if (!book()) return; const Bank* bk = book()->bank(bankId); if (!bk || bk->isPool()) return; const std::string current = bk->displayName; // copy before any mutation @@ -1024,6 +1026,7 @@ void doRenameBank(const std::string& bankId) { // member count AND offers evacuate as the one-click alternative (Yes=delete anyway, // No=evacuate-then-keep, Cancel=abort) — richer than B3's basic YESNO. void doDeleteBank(const std::string& bankId) { + if (!book()) return; const Bank* bk = book()->bank(bankId); if (!bk || bk->isPool()) return; const std::size_t members = bk->index.size(); // read BEFORE any mutation @@ -1049,12 +1052,14 @@ void doDeleteBank(const std::string& bankId) { } if (!book()->deleteBank(bankId)) return; persistBook(); - // shownBankId is reconciled by the next fingerprint pass; nudge focus to pool if - // no named banks remain so the selection has a valid home. + // shownBankId is reconciled by the next fingerprint pass. If no named banks remain, + // nudge focus to the pool so the selection has a valid home. + if (namedBanks().empty()) g_panel.focusedRegion = Region::Pool; invalidatePanel(); } void doEvacuateBank(const std::string& bankId) { + if (!book()) return; const Bank* bk = book()->bank(bankId); if (!bk || bk->isPool()) return; if (!book()->evacuate(bankId)) return; @@ -1063,6 +1068,7 @@ void doEvacuateBank(const std::string& bankId) { } void doActivateBank(const std::string& bankId) { + if (!book()) return; if (!book()->setActiveBank(bankId)) return; // rejects an unknown id persistBook(); invalidatePanel(); @@ -1073,6 +1079,7 @@ void doActivateBank(const std::string& bankId) { void transferSamples(const std::vector& sampleIds, const std::string& srcBankId, const std::string& destBankId, bool copy) { + if (!book()) return; if (sampleIds.empty() || srcBankId == destBankId) return; if (!book()->bank(srcBankId) || !book()->bank(destBankId)) return; for (const std::string& sid : sampleIds) { @@ -1108,9 +1115,8 @@ std::vector focusedSelectionIds() { // hands the chosen id straight back, so no hookcommand routing is involved. // Appends a string item (id) to `menu` at its end. Portable over Win32/SWELL: both -// accept InsertMenu(menu, pos, MF_BYPOSITION|MF_STRING, id, text) with pos past the -// end appending. -1 as an unsigned position appends on Win32; SWELL clamps a large -// pos to the end. +// accept InsertMenu(menu, pos, MF_BYPOSITION|MF_STRING, id, text) with a negative +// position appending. Win32 and SWELL both treat pos < 0 as an append. void menuAppend(HMENU menu, unsigned int id, const char* text, bool grayed = false) { UINT flags = MF_BYPOSITION | MF_STRING; if (grayed) flags |= MF_GRAYED; @@ -1135,6 +1141,7 @@ enum : unsigned int { // Shows the right-click context menu for a named-bank TAB: activate / rename / delete // / evacuate that bank, plus a create entry. Drives the id-keyed ops. void showTabMenu(int screenX, int screenY, const std::string& bankId) { + if (!book()) return; const Bank* bk = book()->bank(bankId); if (!bk || bk->isPool()) return; const bool isActive = book()->activeBankId() == bankId; @@ -1582,6 +1589,14 @@ WDL_DLGRET dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { SetFocus(hwnd); handleRightClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; + case WM_CAPTURECHANGED: + // Capture lost before a drag began (e.g. pointer left window pre-threshold + // and button released outside) — disarm so the state doesn't stay stale. + if (g_panel.dragArmed && !g_panel.dragging) { + g_panel.dragArmed = false; + invalidatePanel(); + } + return 0; case WM_DESTROY: if (GetCapture() == hwnd) ReleaseCapture(); stopAudition();