diff --git a/src/effects/backends/builtin/metronomeeffect.cpp b/src/effects/backends/builtin/metronomeeffect.cpp index 118637e35e7..17061e9789c 100644 --- a/src/effects/backends/builtin/metronomeeffect.cpp +++ b/src/effects/backends/builtin/metronomeeffect.cpp @@ -131,7 +131,10 @@ void MetronomeEffect::processChannel( nextClickStart = bufferEnd - beatToBufferEnd; } } else { - // no transport, nothing to do. + // no transport, continue until the current click has been fully played + if (gs->m_framesSinceClickStart < clickSize) { + gs->m_framesSinceClickStart += engineParameters.framesPerBuffer(); + } return; } } else { diff --git a/src/library/dlgtagfetcher.cpp b/src/library/dlgtagfetcher.cpp index 3eff740882d..59361de64b8 100644 --- a/src/library/dlgtagfetcher.cpp +++ b/src/library/dlgtagfetcher.cpp @@ -246,30 +246,31 @@ void DlgTagFetcher::slotPrev() { } void DlgTagFetcher::loadTrack(const TrackPointer& pTrack) { + tags->clear(); + m_data = Data(); if (m_pTrack) { - tags->clear(); disconnect(m_pTrack.get(), &Track::changed, this, &DlgTagFetcher::slotTrackChanged); - m_data = Data(); } - tags->clear(); m_pWFetchedCoverArtLabel->setCoverArt(CoverInfo{}, QPixmap{}); m_coverCache.clear(); - m_pTrack = pTrack; - if (!m_pTrack) { - return; - } - btnRetry->setDisabled(true); btnApply->setDisabled(true); checkBoxTags->setDisabled(true); checkBoxCover->setDisabled(true); statusMessage->setVisible(false); + + m_pTrack = pTrack; + if (!m_pTrack) { + loadingProgressBar->setVisible(false); + return; + } + loadingProgressBar->setVisible(true); loadingProgressBar->setValue(kMinimumValueOfQProgressBar); loadingProgressBar->setToolTip(QString()); diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index badfcbe353e..47b32bcc021 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -621,6 +621,8 @@ void DlgTrackInfo::saveTrack() { void DlgTrackInfo::clear() { const QSignalBlocker signalBlocker(this); + setWindowTitle(QString()); + if (m_pLoadedTrack) { disconnect(m_pLoadedTrack.get(), &Track::changed, diff --git a/src/widget/wtrackmenu.cpp b/src/widget/wtrackmenu.cpp index 2854c6fa408..b434c37a0a3 100644 --- a/src/widget/wtrackmenu.cpp +++ b/src/widget/wtrackmenu.cpp @@ -232,6 +232,10 @@ void WTrackMenu::createMenus() { m_pSearchRelatedMenu->clear(); const auto pTrack = getFirstTrackPointer(); if (pTrack) { + // Ensure it's enabled, else we can't add actions. + VERIFY_OR_DEBUG_ASSERT(m_pSearchRelatedMenu->isEnabled()) { + m_pSearchRelatedMenu->setEnabled(true); + } m_pSearchRelatedMenu->addActionsForTrack(*pTrack); } m_pSearchRelatedMenu->setEnabled( @@ -759,11 +763,9 @@ std::pair WTrackMenu::getTrackBpmLockStates() const { break; } } - } else { - if (m_pTrack) { - anyBpmLocked = m_pTrack->isBpmLocked(); - anyBpmNotLocked = !anyBpmLocked; - } + } else if (m_pTrack) { + anyBpmLocked = m_pTrack->isBpmLocked(); + anyBpmNotLocked = !anyBpmLocked; } return std::pair(anyBpmLocked, anyBpmNotLocked); } @@ -885,8 +887,11 @@ CoverInfo WTrackMenu::getCoverInfoOfLastTrack() const { .data() .toString(); return coverInfo; - } else { + } else if (m_pTrack) { return m_pTrack->getCoverInfoWithLocation(); + } else { + // No track, no track model + return CoverInfo(); } } @@ -898,10 +903,25 @@ void WTrackMenu::updateMenus() { // Gray out some stuff if multiple songs were selected. const bool singleTrackSelected = getTrackCount() == 1; + if (featureIsEnabled(Feature::SearchRelated)) { + // Enable only if we have one valid track pointer. + // this prevents the cursor getting stuck on this menu in case it gets + // disabled when encountering a track nullptr in lambda function + // connected to aboutToShow() signal (see createMenus()). + // Note: track nullptr can happen when TrackDAO returns nullptr because + // the selected track references a file referenced by another cached track. + DEBUG_ASSERT(m_pSearchRelatedMenu); + const auto pTrack = getFirstTrackPointer(); + m_pSearchRelatedMenu->setEnabled(pTrack != nullptr); + // TODO Only enable for single track? + } + if (featureIsEnabled(Feature::LoadTo)) { + // Enable menus only for single track int iNumDecks = static_cast(m_pNumDecks.get()); m_pDeckMenu->clear(); - if (iNumDecks > 0) { + m_pDeckMenu->setEnabled(singleTrackSelected); + if (singleTrackSelected && iNumDecks > 0) { for (int i = 1; i <= iNumDecks; ++i) { // PlayerManager::groupForDeck is 0-indexed. QString deckGroup = PlayerManager::groupForDeck(i - 1); @@ -936,8 +956,9 @@ void WTrackMenu::updateMenus() { int iNumSamplers = static_cast(m_pNumSamplers.get()); const int maxSamplersPerMenu = 16; - if (iNumSamplers > 0) { - m_pSamplerMenu->clear(); + m_pSamplerMenu->clear(); + m_pSamplerMenu->setEnabled(singleTrackSelected); + if (singleTrackSelected && iNumSamplers > 0) { QMenu* pMenu = m_pSamplerMenu; int samplersInMenu = 0; for (int i = 1; i <= iNumSamplers; ++i) { @@ -1039,13 +1060,15 @@ void WTrackMenu::updateMenus() { } else if (m_pTrack) { pTrack = m_pTrack; } - const double bpm = pTrack->getBpm(); - appendBpmPreviewtoBpmAction(m_pBpmDoubleAction, bpm); - appendBpmPreviewtoBpmAction(m_pBpmHalveAction, bpm); - appendBpmPreviewtoBpmAction(m_pBpmTwoThirdsAction, bpm); - appendBpmPreviewtoBpmAction(m_pBpmThreeFourthsAction, bpm); - appendBpmPreviewtoBpmAction(m_pBpmFourThirdsAction, bpm); - appendBpmPreviewtoBpmAction(m_pBpmThreeHalvesAction, bpm); + if (pTrack) { + const double bpm = pTrack->getBpm(); + appendBpmPreviewtoBpmAction(m_pBpmDoubleAction, bpm); + appendBpmPreviewtoBpmAction(m_pBpmHalveAction, bpm); + appendBpmPreviewtoBpmAction(m_pBpmTwoThirdsAction, bpm); + appendBpmPreviewtoBpmAction(m_pBpmThreeFourthsAction, bpm); + appendBpmPreviewtoBpmAction(m_pBpmFourThirdsAction, bpm); + appendBpmPreviewtoBpmAction(m_pBpmThreeHalvesAction, bpm); + } } } } @@ -2536,8 +2559,11 @@ void WTrackMenu::slotShowDlgTrackInfo() { m_pDlgTrackInfo.release()->deleteLater(); } }); - // Method getFirstTrackPointer() is not applicable here, the dialog - // needs an index reference for the Next/Prev navigation. + // Method getFirstTrackPointer() is not applicable here! + // DlgTrackInfo relies on a track model for certain operations, + // for example show/hide the Next/Prev buttons. + // It can be loaded with either an index (must have a model), + // or a TrackPointer (must NOT have a model then). if (m_pTrackModel) { m_pDlgTrackInfo->loadTrack(m_trackIndexList.at(0)); } else { diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index dd1834944b4..8b2eba3de10 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -1069,6 +1069,12 @@ void WTrackTableView::keyPressEvent(QKeyEvent* event) { if (event->modifiers().testFlag(Qt::NoModifier)) { slotMouseDoubleClicked(currentIndex()); } else if ((event->modifiers() & kPropertiesShortcutModifier)) { + TrackModel* pTrackModel = getTrackModel(); + if (!pTrackModel || + !pTrackModel->hasCapabilities( + TrackModel::Capability::EditMetadata)) { + return; + } const QModelIndexList indices = getSelectedRows(); if (indices.isEmpty()) { return;