From 947b40e8b478f254418d2e23469e869213f0faee Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 28 Oct 2020 18:43:19 +0100 Subject: [PATCH] WTrackMenu: add action, slot and TrackPointerOperation for RemoveFromDisk --- src/widget/wtrackmenu.cpp | 157 ++++++++++++++++++++++++++++++++++++++ src/widget/wtrackmenu.h | 2 + 2 files changed, 159 insertions(+) diff --git a/src/widget/wtrackmenu.cpp b/src/widget/wtrackmenu.cpp index a96bc75f9778..7b5d70ce40e5 100644 --- a/src/widget/wtrackmenu.cpp +++ b/src/widget/wtrackmenu.cpp @@ -207,6 +207,14 @@ void WTrackMenu::createActions() { connect(m_pPurgeAct, &QAction::triggered, this, &WTrackMenu::slotPurge); } + if (featureIsEnabled(Feature::RemoveFromDisk)) { + m_pRemoveFromDiskAct = new QAction(tr("Remove from disk"), this); + connect(m_pRemoveFromDiskAct, + &QAction::triggered, + this, + &WTrackMenu::slotRemoveFromDisk); + } + if (featureIsEnabled(Feature::Properties)) { m_pPropertiesAct = new QAction(tr("Properties"), this); connect(m_pPropertiesAct, &QAction::triggered, this, &WTrackMenu::slotShowDlgTrackInfo); @@ -465,6 +473,11 @@ void WTrackMenu::setupActions() { } } + if (featureIsEnabled(Feature::RemoveFromDisk) && + m_pTrackModel->hasCapabilities(TrackModel::Capability::RemoveFromDisk)) { + addAction(m_pRemoveFromDiskAct); + } + if (featureIsEnabled(Feature::FileBrowser)) { addAction(m_pFileBrowserAct); } @@ -714,6 +727,13 @@ void WTrackMenu::updateMenus() { } } + if (featureIsEnabled(Feature::RemoveFromDisk)) { + bool locked = m_pTrackModel->hasCapabilities(TrackModel::Capability::Locked); + if (m_pTrackModel->hasCapabilities(TrackModel::Capability::RemoveFromDisk)) { + m_pRemoveFromDiskAct->setEnabled(!locked); + } + } + if (featureIsEnabled(Feature::Properties)) { m_pPropertiesAct->setEnabled(singleTrackSelected); } @@ -1528,6 +1548,143 @@ void WTrackMenu::slotClearAllMetadata() { &trackOperator); } +namespace { + +class RemoveTrackFilesFromDiskTrackPointerOperation : public mixxx::TrackPointerOperation { + public: + mutable QList tr_tracksToPurge; + mutable QList tr_tracksToKeep; + + private: + void doApply( + const TrackPointer& pTrack) const override { + QString location = pTrack->getLocation(); + if (location.isNull()) { + return; + } + QFile file(location); + auto trackRef = TrackRef::fromFileInfo( + pTrack->getFileInfo(), + pTrack->getId()); + VERIFY_OR_DEBUG_ASSERT(trackRef.isValid()) { + return; + } + if (file.exists() && !file.remove()) { + // deletion failed, log warning. + qWarning() + << "Queued file" + << location + << "could not be deleted. Track is not purged"; + tr_tracksToKeep.append(location); + return; + } + } +}; + +} // anonymous namespace + +void WTrackMenu::slotRemoveFromDisk() { + // Remove duplicates to prevent delete errors later on. + // Collect file locations for the Delete warning. + QList trackRefsToDelete; + QList trackLocations; + trackRefsToDelete.reserve(getTrackCount()); + trackLocations.reserve(trackRefsToDelete.size()); + for (const auto& trackRef : getTrackRefs()) { + DEBUG_ASSERT(trackRef.hasLocation()); + if (!trackRefsToDelete.contains(trackRef)) { + trackRefsToDelete.append(trackRef); + trackLocations.append(trackRef.getLocation()); + } + } + + // TODO Use a dialog with a QListView that can be scrolled horizontally? + // Or improve linebreaks and formatting in the current plain textview. + // TODO Should each item may have a checkbox to allow removing it from the delete list + // or is it okay for the user to Cancel and adjust the selection in the tracks table? + // TODO Allow to keep all selected tracks' references + + // Show Delete warning. + // Primitive hack to populate a pseudo list. + QMessageBox msgBoxDelete(QMessageBox::Critical, + QObject::tr("Delete Files"), + nullptr); + msgBoxDelete.setInformativeText( + QObject::tr("Permanently delete these %1 files from disk?" + " " + "
" + "%2
" + "
" + "This can not be undone!
") + .arg(QString::number(trackLocations.length()), + QString(QStringLiteral("• ")) + + trackLocations.join( + QStringLiteral("
• ")))); + // TODO use Qt::MarkdownText for Qt >= 5.14 + msgBoxDelete.setTextFormat(Qt::RichText); + QAbstractButton* deleteBtn = msgBoxDelete.addButton( + tr("Delete Files!"), QMessageBox::AcceptRole); + msgBoxDelete.addButton(QMessageBox::Cancel); + msgBoxDelete.setDefaultButton(QMessageBox::Cancel); + msgBoxDelete.exec(); + if (msgBoxDelete.clickedButton() != deleteBtn) { + return; + } + + // Set up and initiate the track batch operation + const auto progressLabelText = + tr("Removing %n track file(s) from disk Removing %n track file(s) from disk ", + "ooooooooooooooooooooooooooooOooooooooooooooooooooooo", + getTrackCount()); + const auto trackOperator = + RemoveTrackFilesFromDiskTrackPointerOperation(); + applyTrackPointerOperation( + progressLabelText, + &trackOperator); + + if (trackOperator.tr_tracksToPurge.length() > 0) { + // Purge only those tracks whose files were actually deleted. + m_pLibrary->trackCollections()->purgeTracks(trackOperator.tr_tracksToPurge); + // Optional message box: + QMessageBox msgBoxPurgeTracks(QMessageBox::Information, + QObject::tr("Yeaiij!"), + nullptr); + msgBoxPurgeTracks.setInformativeText( + QObject::tr( + "

" + "%1 annoying tracks were deleted from disk and" + "purged from the Mixxx database." + "


") + .arg(QString::number(trackOperator.tr_tracksToPurge.length()))); + msgBoxPurgeTracks.setTextFormat(Qt::RichText); + msgBoxPurgeTracks.exec(); + } + + // Show list of tracks that are not going to be purged. + if (trackOperator.tr_tracksToKeep.length() >= 0) { + return; + } + + QMessageBox msgBoxKeepTracks(QMessageBox::Warning, + QObject::tr("Some Files Were Not Deleted"), + nullptr); + msgBoxKeepTracks.setInformativeText( + QObject::tr("

" + "The following %1 files could not be deleted from disk." + "Make sure you have write access to those files and " + "try again later." + "
" + "%2" + "
") + .arg(QString::number( + trackOperator.tr_tracksToKeep.length()), + QString(QStringLiteral("• ")) + + trackOperator.tr_tracksToKeep.join( + QStringLiteral("
• ")))); + msgBoxKeepTracks.setTextFormat(Qt::RichText); + msgBoxKeepTracks.exec(); +} + void WTrackMenu::slotShowDlgTrackInfo() { if (isEmpty()) { return; diff --git a/src/widget/wtrackmenu.h b/src/widget/wtrackmenu.h index bca46ce4743b..cd23ca368632 100644 --- a/src/widget/wtrackmenu.h +++ b/src/widget/wtrackmenu.h @@ -129,6 +129,7 @@ class WTrackMenu : public QMenu { void slotHide(); void slotUnhide(); void slotPurge(); + void slotRemoveFromDisk(); private: // This getter verifies that m_pTrackModel is set when @@ -224,6 +225,7 @@ class WTrackMenu : public QMenu { QAction* m_pHideAct{}; QAction* m_pUnhideAct{}; QAction* m_pPurgeAct{}; + QAction* m_pRemoveFromDiskAct{}; // Show track-editor action QAction* m_pPropertiesAct{};