From 6950df47b838dae4b0f14767dae500964822032a Mon Sep 17 00:00:00 2001 From: ronso0 Date: Mon, 1 Jan 2024 21:22:44 +0100 Subject: [PATCH 1/2] Playlist feature: add 'Shuffle playlist' sidebar action --- src/library/playlisttablemodel.cpp | 5 +- src/library/trackset/baseplaylistfeature.cpp | 17 +++--- src/library/trackset/baseplaylistfeature.h | 1 + src/library/trackset/playlistfeature.cpp | 55 ++++++++++++++++++++ src/library/trackset/playlistfeature.h | 3 ++ src/library/trackset/setlogfeature.cpp | 10 ++-- src/library/trackset/setlogfeature.h | 2 - 7 files changed, 75 insertions(+), 18 deletions(-) diff --git a/src/library/playlisttablemodel.cpp b/src/library/playlisttablemodel.cpp index c8ce2dead4f..6c48137e85a 100644 --- a/src/library/playlisttablemodel.cpp +++ b/src/library/playlisttablemodel.cpp @@ -291,6 +291,7 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo // this is used to exclude the already loaded track at pos #1 if used from running Auto-DJ excludePos = exclude.sibling(exclude.row(), positionColumn).data().toInt(); } + int numOfTracks = rowCount(); if (shuffle.count() > 1) { // if there is more then one track selected, shuffle selection only foreach (QModelIndex shuffleIndex, shuffle) { @@ -300,8 +301,7 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo } } } else { - // if there is only one track selected, shuffle all tracks - int numOfTracks = rowCount(); + // if there is no track or only one selected, shuffle all tracks for (int i = 0; i < numOfTracks; i++) { int oldPosition = index(i, positionColumn).data().toInt(); if (oldPosition != excludePos) { @@ -310,7 +310,6 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo } } // Set up list of all IDs - int numOfTracks = rowCount(); for (int i = 0; i < numOfTracks; i++) { int position = index(i, positionColumn).data().toInt(); TrackId trackId(index(i, idColumn).data()); diff --git a/src/library/trackset/baseplaylistfeature.cpp b/src/library/trackset/baseplaylistfeature.cpp index 9cd8acf3556..42ba048a40c 100644 --- a/src/library/trackset/baseplaylistfeature.cpp +++ b/src/library/trackset/baseplaylistfeature.cpp @@ -678,17 +678,18 @@ TreeItemModel* BasePlaylistFeature::sidebarModel() const { return m_pSidebarModel; } -void BasePlaylistFeature::bindLibraryWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - Q_UNUSED(keyboard); - WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget); - edit->setHtml(getRootViewHtml()); - edit->setOpenLinks(false); - connect(edit, +void BasePlaylistFeature::bindLibraryWidget(WLibrary* pLibraryWidget, + KeyboardEventFilter* pKeyboard) { + Q_UNUSED(pKeyboard); + WLibraryTextBrowser* pEdit = new WLibraryTextBrowser(pLibraryWidget); + pEdit->setHtml(getRootViewHtml()); + pEdit->setOpenLinks(false); + connect(pEdit, &WLibraryTextBrowser::anchorClicked, this, &BasePlaylistFeature::htmlLinkClicked); - libraryWidget->registerView(m_rootViewName, edit); + m_pLibraryWidget = QPointer(pLibraryWidget); + m_pLibraryWidget->registerView(m_rootViewName, pEdit); } void BasePlaylistFeature::bindSidebarWidget(WLibrarySidebar* pSidebarWidget) { diff --git a/src/library/trackset/baseplaylistfeature.h b/src/library/trackset/baseplaylistfeature.h index ce16c79e36c..761f94305be 100644 --- a/src/library/trackset/baseplaylistfeature.h +++ b/src/library/trackset/baseplaylistfeature.h @@ -96,6 +96,7 @@ class BasePlaylistFeature : public BaseTrackSetFeature { QModelIndex m_lastClickedIndex; QModelIndex m_lastRightClickedIndex; QPointer m_pSidebarWidget; + QPointer m_pLibraryWidget; QAction* m_pCreatePlaylistAction; QAction* m_pDeletePlaylistAction; diff --git a/src/library/trackset/playlistfeature.cpp b/src/library/trackset/playlistfeature.cpp index 38ff2575c67..ad1bd663df4 100644 --- a/src/library/trackset/playlistfeature.cpp +++ b/src/library/trackset/playlistfeature.cpp @@ -14,7 +14,9 @@ #include "sources/soundsourceproxy.h" #include "util/db/dbconnection.h" #include "util/duration.h" +#include "widget/wlibrary.h" #include "widget/wlibrarysidebar.h" +#include "widget/wtracktableview.h" namespace { @@ -43,6 +45,12 @@ PlaylistFeature::PlaylistFeature(Library* pLibrary, UserSettingsPointer pConfig) std::unique_ptr pRootItem = TreeItem::newRoot(this); m_pSidebarModel->setRootItem(std::move(pRootItem)); constructChildModel(kInvalidPlaylistId); + + m_pShufflePlaylistAction = new QAction(tr("Shuffle Playlist"), this); + connect(m_pShufflePlaylistAction, + &QAction::triggered, + this, + &PlaylistFeature::slotShufflePlaylist); } QVariant PlaylistFeature::title() { @@ -73,6 +81,10 @@ void PlaylistFeature::onRightClickChild( QMenu menu(m_pSidebarWidget); menu.addAction(m_pCreatePlaylistAction); menu.addSeparator(); + // TODO If playlist is selected and has more than one track selected + // show "Shuffle selected tracks", else show "Shuffle playlist"? + menu.addAction(m_pShufflePlaylistAction); + menu.addSeparator(); menu.addAction(m_pRenamePlaylistAction); menu.addAction(m_pDuplicatePlaylistAction); menu.addAction(m_pDeletePlaylistAction); @@ -222,6 +234,49 @@ QString PlaylistFeature::fetchPlaylistLabel(int playlistId) { return QString(); } +void PlaylistFeature::slotShufflePlaylist() { + int playlistId = playlistIdFromIndex(m_lastRightClickedIndex); + if (playlistId == kInvalidPlaylistId) { + return; + } + + if (m_playlistDao.isPlaylistLocked(playlistId)) { + qDebug() << "Can't shuffle locked playlist" << playlistId + << m_playlistDao.getPlaylistName(playlistId); + return; + } + + // Shuffle all tracks + // If the playlist is loaded/visible shuffle only selected tracks + QModelIndexList selection; + if (isChildIndexSelectedInSidebar(m_lastRightClickedIndex) && + m_pPlaylistTableModel->getPlaylist() == playlistId) { + if (m_pLibraryWidget) { + WTrackTableView* view = dynamic_cast( + m_pLibraryWidget->getActiveView()); + if (view != nullptr) { + selection = view->selectionModel()->selectedIndexes(); + } + } + m_pPlaylistTableModel->shuffleTracks(selection, QModelIndex()); + } else { + // Create a temp model so we don't need to select the playlist + // in the persistent model in order to shuffle it + QScopedPointer pPlaylistTableModel( + new PlaylistTableModel(this, + m_pLibrary->trackCollectionManager(), + "mixxx.db.model.playlist_shuffle")); + pPlaylistTableModel->selectPlaylist(playlistId); + pPlaylistTableModel->setSort( + pPlaylistTableModel->fieldIndex( + ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_POSITION), + Qt::AscendingOrder); + pPlaylistTableModel->select(); + + pPlaylistTableModel->shuffleTracks(selection, QModelIndex()); + } +} + /// Purpose: When inserting or removing playlists, /// we require the sidebar model not to reset. /// This method queries the database and does dynamic insertion diff --git a/src/library/trackset/playlistfeature.h b/src/library/trackset/playlistfeature.h index 44a5da9905d..1fff9bb1df2 100644 --- a/src/library/trackset/playlistfeature.h +++ b/src/library/trackset/playlistfeature.h @@ -35,6 +35,7 @@ class PlaylistFeature : public BasePlaylistFeature { void slotPlaylistTableChanged(int playlistId) override; void slotPlaylistContentOrLockChanged(const QSet& playlistIds) override; void slotPlaylistTableRenamed(int playlistId, const QString& newName) override; + void slotShufflePlaylist(); protected: QString fetchPlaylistLabel(int playlistId) override; @@ -44,4 +45,6 @@ class PlaylistFeature : public BasePlaylistFeature { private: QString getRootViewHtml() const override; + + QAction* m_pShufflePlaylistAction; }; diff --git a/src/library/trackset/setlogfeature.cpp b/src/library/trackset/setlogfeature.cpp index 56e83b22f9e..6213c185b55 100644 --- a/src/library/trackset/setlogfeature.cpp +++ b/src/library/trackset/setlogfeature.cpp @@ -107,13 +107,13 @@ QVariant SetlogFeature::title() { } void SetlogFeature::bindLibraryWidget( - WLibrary* libraryWidget, KeyboardEventFilter* keyboard) { - BasePlaylistFeature::bindLibraryWidget(libraryWidget, keyboard); + WLibrary* pLibraryWidget, KeyboardEventFilter* pKeyboard) { + BasePlaylistFeature::bindLibraryWidget(pLibraryWidget, pKeyboard); connect(&PlayerInfo::instance(), &PlayerInfo::currentPlayingTrackChanged, this, &SetlogFeature::slotPlayingTrackChanged); - m_libraryWidget = QPointer(libraryWidget); + m_pLibraryWidget = QPointer(pLibraryWidget); } void SetlogFeature::deleteAllUnlockedPlaylistsWithFewerTracks() { @@ -600,9 +600,9 @@ void SetlogFeature::slotPlayingTrackChanged(TrackPointer currentPlayingTrack) { // View needs a refresh bool hasActiveView = false; - if (m_libraryWidget) { + if (m_pLibraryWidget) { WTrackTableView* view = dynamic_cast( - m_libraryWidget->getActiveView()); + m_pLibraryWidget->getActiveView()); if (view != nullptr) { // We have a active view on the history. The user may have some // important active selection. For example putting track into crates diff --git a/src/library/trackset/setlogfeature.h b/src/library/trackset/setlogfeature.h index 38c005b9218..3aebf459c33 100644 --- a/src/library/trackset/setlogfeature.h +++ b/src/library/trackset/setlogfeature.h @@ -61,8 +61,6 @@ class SetlogFeature : public BasePlaylistFeature { int m_currentPlaylistId; int m_yearNodeId; - - QPointer m_libraryWidget; Library* m_pLibrary; UserSettingsPointer m_pConfig; }; From d567642f307bc1847cab84a9de565c8bb3f51a22 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Wed, 3 Jan 2024 01:03:22 +0100 Subject: [PATCH 2/2] playlist/crate features: use unique_ptr for temp trackset models --- src/library/trackset/baseplaylistfeature.cpp | 24 ++++++++++---------- src/library/trackset/crate/cratefeature.cpp | 16 ++++++------- src/library/trackset/playlistfeature.cpp | 6 ++--- src/library/trackset/setlogfeature.cpp | 12 +++++----- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/library/trackset/baseplaylistfeature.cpp b/src/library/trackset/baseplaylistfeature.cpp index 42ba048a40c..fabe4479708 100644 --- a/src/library/trackset/baseplaylistfeature.cpp +++ b/src/library/trackset/baseplaylistfeature.cpp @@ -466,10 +466,10 @@ void BasePlaylistFeature::slotImportPlaylistFile(const QString& playlistFile, // This is used as a proxy object to write to the database. // We cannot use m_pPlaylistTableModel since it might have another playlist selected which // is not the playlist that received the right-click. - QScopedPointer pPlaylistTableModel( - new PlaylistTableModel(this, + std::unique_ptr pPlaylistTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_export")); + "mixxx.db.model.playlist_export"); pPlaylistTableModel->selectPlaylist(playlistId); pPlaylistTableModel->setSort( pPlaylistTableModel->fieldIndex( @@ -565,10 +565,10 @@ void BasePlaylistFeature::slotExportPlaylist() { // Create a new table model since the main one might have an active search. // This will only export songs that we think exist on default - QScopedPointer pPlaylistTableModel( - new PlaylistTableModel(this, + std::unique_ptr pPlaylistTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_export")); + "mixxx.db.model.playlist_export"); emit saveModelState(); pPlaylistTableModel->selectPlaylist(playlistId); @@ -583,13 +583,13 @@ void BasePlaylistFeature::slotExportPlaylist() { kUseRelativePathOnExportConfigKey); if (fileLocation.endsWith(".csv", Qt::CaseInsensitive)) { - ParserCsv::writeCSVFile(fileLocation, pPlaylistTableModel.data(), useRelativePath); + ParserCsv::writeCSVFile(fileLocation, pPlaylistTableModel.get(), useRelativePath); } else if (fileLocation.endsWith(".txt", Qt::CaseInsensitive)) { if (m_playlistDao.getHiddenType(pPlaylistTableModel->getPlaylist()) == PlaylistDAO::PLHT_SET_LOG) { - ParserCsv::writeReadableTextFile(fileLocation, pPlaylistTableModel.data(), true); + ParserCsv::writeReadableTextFile(fileLocation, pPlaylistTableModel.get(), true); } else { - ParserCsv::writeReadableTextFile(fileLocation, pPlaylistTableModel.data(), false); + ParserCsv::writeReadableTextFile(fileLocation, pPlaylistTableModel.get(), false); } } else { // Create and populate a list of files of the playlist @@ -611,10 +611,10 @@ void BasePlaylistFeature::slotExportTrackFiles() { if (playlistId == kInvalidPlaylistId) { return; } - QScopedPointer pPlaylistTableModel( - new PlaylistTableModel(this, + std::unique_ptr pPlaylistTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_export")); + "mixxx.db.model.playlist_export"); emit saveModelState(); pPlaylistTableModel->selectPlaylist(playlistId); diff --git a/src/library/trackset/crate/cratefeature.cpp b/src/library/trackset/crate/cratefeature.cpp index 6b6fbbfce84..5876aec22a8 100644 --- a/src/library/trackset/crate/cratefeature.cpp +++ b/src/library/trackset/crate/cratefeature.cpp @@ -683,8 +683,8 @@ void CrateFeature::slotImportPlaylistFile(const QString& playlistFile, CrateId c } else { // Create a temporary table model since the main one might have another // crate selected which is not the crate that received the right-click. - QScopedPointer pCrateTableModel( - new CrateTableModel(this, m_pLibrary->trackCollectionManager())); + std::unique_ptr pCrateTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager()); pCrateTableModel->selectCrate(crateId); pCrateTableModel->select(); pCrateTableModel->addTracks(QModelIndex(), locations); @@ -808,15 +808,15 @@ void CrateFeature::slotExportPlaylist() { // Create list of files of the crate // Create a new table model since the main one might have an active search. - QScopedPointer pCrateTableModel( - new CrateTableModel(this, m_pLibrary->trackCollectionManager())); + std::unique_ptr pCrateTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager()); pCrateTableModel->selectCrate(crateId); pCrateTableModel->select(); if (fileLocation.endsWith(".csv", Qt::CaseInsensitive)) { - ParserCsv::writeCSVFile(fileLocation, pCrateTableModel.data(), useRelativePath); + ParserCsv::writeCSVFile(fileLocation, pCrateTableModel.get(), useRelativePath); } else if (fileLocation.endsWith(".txt", Qt::CaseInsensitive)) { - ParserCsv::writeReadableTextFile(fileLocation, pCrateTableModel.data(), false); + ParserCsv::writeReadableTextFile(fileLocation, pCrateTableModel.get(), false); } else { // populate a list of files of the crate QList playlistItems; @@ -834,8 +834,8 @@ void CrateFeature::slotExportPlaylist() { void CrateFeature::slotExportTrackFiles() { // Create a new table model since the main one might have an active search. - QScopedPointer pCrateTableModel( - new CrateTableModel(this, m_pLibrary->trackCollectionManager())); + std::unique_ptr pCrateTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager()); pCrateTableModel->selectCrate(m_crateTableModel.selectedCrate()); pCrateTableModel->select(); diff --git a/src/library/trackset/playlistfeature.cpp b/src/library/trackset/playlistfeature.cpp index ad1bd663df4..d76e0923a83 100644 --- a/src/library/trackset/playlistfeature.cpp +++ b/src/library/trackset/playlistfeature.cpp @@ -262,10 +262,10 @@ void PlaylistFeature::slotShufflePlaylist() { } else { // Create a temp model so we don't need to select the playlist // in the persistent model in order to shuffle it - QScopedPointer pPlaylistTableModel( - new PlaylistTableModel(this, + std::unique_ptr pPlaylistTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_shuffle")); + "mixxx.db.model.playlist_shuffle"); pPlaylistTableModel->selectPlaylist(playlistId); pPlaylistTableModel->setSort( pPlaylistTableModel->fieldIndex( diff --git a/src/library/trackset/setlogfeature.cpp b/src/library/trackset/setlogfeature.cpp index 6213c185b55..9d71e97c7c0 100644 --- a/src/library/trackset/setlogfeature.cpp +++ b/src/library/trackset/setlogfeature.cpp @@ -390,10 +390,10 @@ void SetlogFeature::slotJoinWithPrevious() { // Right-clicked playlist may not be loaded. Use a temporary model to // keep sidebar selection and table view in sync - QScopedPointer pPlaylistTableModel( - new PlaylistTableModel(this, + std::unique_ptr pPlaylistTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_export")); + "mixxx.db.model.playlist_export"); pPlaylistTableModel->selectPlaylist(previousPlaylistId); if (clickedPlaylistId == m_currentPlaylistId) { @@ -438,10 +438,10 @@ void SetlogFeature::slotMarkAllTracksPlayed() { // Right-clicked playlist may not be loaded. Use a temporary model to // keep sidebar selection and table view in sync - QScopedPointer pPlaylistTableModel( - new PlaylistTableModel(this, + std::unique_ptr pPlaylistTableModel = + std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_export")); + "mixxx.db.model.playlist_export"); pPlaylistTableModel->selectPlaylist(clickedPlaylistId); // mark all the Tracks in the previous Playlist as played pPlaylistTableModel->select();