Skip to content

Commit 3c35370

Browse files
authored
Merge pull request mixxxdj#15319 from ronso0/playlist-adopt-order-for-position
Playlists: allow to adopt current order (sorted) as playlist order
2 parents da6db1e + e58c5f7 commit 3c35370

6 files changed

Lines changed: 97 additions & 5 deletions

File tree

src/library/dao/playlistdao.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,45 @@ int PlaylistDAO::tracksInPlaylist(const int playlistId) const {
11211121
return count;
11221122
}
11231123

1124+
void PlaylistDAO::orderTracksByCurrPos(const int playlistId,
1125+
QList<std::pair<TrackId, int>>& newOrder) {
1126+
if (newOrder.isEmpty() ||
1127+
playlistId == kInvalidPlaylistId ||
1128+
isPlaylistLocked(playlistId) ||
1129+
newOrder.size() != tracksInPlaylist(playlistId)) {
1130+
return;
1131+
}
1132+
1133+
ScopedTransaction transaction(m_database);
1134+
QSqlQuery query(m_database);
1135+
query.prepare(QStringLiteral(
1136+
"UPDATE PlaylistTracks "
1137+
"SET position=:new_pos "
1138+
"WHERE position=:old_pos AND "
1139+
"track_id=:track_id AND "
1140+
"playlist_id=:pl_id"));
1141+
int newPos = 1;
1142+
for (auto [trackId, oldPos] : newOrder) {
1143+
VERIFY_OR_DEBUG_ASSERT(trackId.isValid()) {
1144+
return;
1145+
}
1146+
query.bindValue(":new_pos", newPos++);
1147+
query.bindValue(":old_pos", oldPos);
1148+
query.bindValue(":track_id", trackId.toVariant());
1149+
query.bindValue(":pl_id", playlistId);
1150+
if (!query.exec()) {
1151+
// We temporarily have duplicate positions, so abort the entire operation
1152+
// to not leave the playlist with an invalid state.
1153+
LOG_FAILED_QUERY(query);
1154+
return;
1155+
}
1156+
}
1157+
1158+
transaction.commit();
1159+
1160+
emit tracksMoved(QSet<int>{playlistId});
1161+
}
1162+
11241163
void PlaylistDAO::moveTrack(const int playlistId, const int oldPosition, const int newPosition) {
11251164
ScopedTransaction transaction(m_database);
11261165
QSqlQuery query(m_database);

src/library/dao/playlistdao.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class PlaylistDAO : public QObject, public virtual DAO {
111111
bool copyPlaylistTracks(const int sourcePlaylistID, const int targetPlaylistID);
112112
// Returns the number of tracks in the given playlist.
113113
int tracksInPlaylist(const int playlistId) const;
114+
// This receives a track list that represents the current order (sorted by BPM for example)
115+
// and adopts this order for `position` in the playlist.
116+
// Returns true on success.
117+
void orderTracksByCurrPos(const int playlistId, QList<std::pair<TrackId, int>>& newOrder);
114118
// moved Track to a new position
115119
void moveTrack(const int playlistId,
116120
const int oldPosition, const int newPosition);

src/library/playlisttablemodel.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo
315315
int numOfTracks = rowCount();
316316
if (shuffle.count() > 1) {
317317
// if there is more then one track selected, shuffle selection only
318-
foreach (QModelIndex shuffleIndex, shuffle) {
318+
for (const QModelIndex& shuffleIndex : std::as_const(shuffle)) {
319319
int oldPosition = shuffleIndex.sibling(shuffleIndex.row(), positionColumn).data().toInt();
320320
if (oldPosition != excludePos) {
321321
positions.append(oldPosition);
@@ -339,6 +339,23 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo
339339
m_pTrackCollectionManager->internalCollection()->getPlaylistDAO().shuffleTracks(m_iPlaylistId, positions, allIds);
340340
}
341341

342+
void PlaylistTableModel::orderTracksByCurrPos() {
343+
QList<std::pair<TrackId, int>> idPosList;
344+
int numOfTracks = rowCount();
345+
idPosList.reserve(numOfTracks);
346+
const int positionColumn = fieldIndex(ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_POSITION);
347+
const int idColumn = fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_ID);
348+
// Set up list of all IDs
349+
for (int i = 0; i < numOfTracks; i++) {
350+
TrackId trackId(index(i, idColumn).data());
351+
int oldPosition = index(i, positionColumn).data().toInt();
352+
idPosList.append(std::make_pair(trackId, oldPosition));
353+
}
354+
m_pTrackCollectionManager->internalCollection()
355+
->getPlaylistDAO()
356+
.orderTracksByCurrPos(m_iPlaylistId, idPosList);
357+
}
358+
342359
const QList<int> PlaylistTableModel::getSelectedPositions(const QModelIndexList& indices) const {
343360
if (indices.isEmpty()) {
344361
return {};

src/library/playlisttablemodel.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ class PlaylistTableModel final : public TrackSetTableModel {
2121
bool appendTrack(TrackId trackId);
2222
void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) override;
2323
void removeTrack(const QModelIndex& index);
24-
void shuffleTracks(const QModelIndexList& shuffle, const QModelIndex& exclude);
24+
void shuffleTracks(const QModelIndexList& shuffle = QModelIndexList(),
25+
const QModelIndex& exclude = QModelIndex());
26+
void orderTracksByCurrPos();
2527

2628
bool isColumnInternal(int column) final;
2729
bool isColumnHiddenByDefault(int column) final;

src/library/trackset/playlistfeature.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ PlaylistFeature::PlaylistFeature(Library* pLibrary, UserSettingsPointer pConfig)
3939
this,
4040
&PlaylistFeature::slotShufflePlaylist);
4141

42+
m_pOrderByCurrentPosAction = make_parented<QAction>(tr("Adopt current order"), this);
43+
connect(m_pOrderByCurrentPosAction,
44+
&QAction::triggered,
45+
this,
46+
&PlaylistFeature::slotOrderTracksByCurrentPosition);
47+
4248
m_pUnlockPlaylistsAction =
4349
make_parented<QAction>(tr("Unlock all playlists"), this);
4450
connect(m_pUnlockPlaylistsAction,
@@ -81,6 +87,8 @@ void PlaylistFeature::onRightClickChild(
8187
int playlistId = playlistIdFromIndex(index);
8288

8389
bool locked = m_playlistDao.isPlaylistLocked(playlistId);
90+
m_pShufflePlaylistAction->setEnabled(!locked);
91+
m_pOrderByCurrentPosAction->setEnabled(!locked && isChildIndexSelectedInSidebar(index));
8492
m_pDeletePlaylistAction->setEnabled(!locked);
8593
m_pRenamePlaylistAction->setEnabled(!locked);
8694

@@ -92,6 +100,7 @@ void PlaylistFeature::onRightClickChild(
92100
// TODO If playlist is selected and has more than one track selected
93101
// show "Shuffle selected tracks", else show "Shuffle playlist"?
94102
menu.addAction(m_pShufflePlaylistAction);
103+
menu.addAction(m_pOrderByCurrentPosAction);
95104
menu.addSeparator();
96105
menu.addAction(m_pRenamePlaylistAction);
97106
menu.addAction(m_pDuplicatePlaylistAction);
@@ -228,17 +237,17 @@ void PlaylistFeature::slotShufflePlaylist() {
228237

229238
// Shuffle all tracks
230239
// If the playlist is loaded/visible shuffle only selected tracks
231-
QModelIndexList selection;
232240
if (isChildIndexSelectedInSidebar(m_lastRightClickedIndex) &&
233241
m_pPlaylistTableModel->getPlaylist() == playlistId) {
242+
QModelIndexList selection;
234243
if (m_pLibraryWidget) {
235244
WTrackTableView* view = dynamic_cast<WTrackTableView*>(
236245
m_pLibraryWidget->getActiveView());
237246
if (view != nullptr) {
238247
selection = view->selectionModel()->selectedIndexes();
239248
}
240249
}
241-
m_pPlaylistTableModel->shuffleTracks(selection, QModelIndex());
250+
m_pPlaylistTableModel->shuffleTracks(selection);
242251
} else {
243252
// Create a temp model so we don't need to select the playlist
244253
// in the persistent model in order to shuffle it
@@ -253,8 +262,27 @@ void PlaylistFeature::slotShufflePlaylist() {
253262
Qt::AscendingOrder);
254263
pPlaylistTableModel->select();
255264

256-
pPlaylistTableModel->shuffleTracks(selection, QModelIndex());
265+
pPlaylistTableModel->shuffleTracks();
266+
}
267+
}
268+
269+
void PlaylistFeature::slotOrderTracksByCurrentPosition() {
270+
int playlistId = playlistIdFromIndex(m_lastRightClickedIndex);
271+
if (playlistId == kInvalidPlaylistId) {
272+
return;
273+
}
274+
275+
if (m_playlistDao.isPlaylistLocked(playlistId)) {
276+
qDebug() << "Can't adopt current sorting for locked playlist" << playlistId
277+
<< m_playlistDao.getPlaylistName(playlistId);
278+
return;
279+
}
280+
// Note(ronso0) I propose to proceed only if the playlist is selected and loaded.
281+
// without playlist content visible we don't have a preview.
282+
if (!isChildIndexSelectedInSidebar(m_lastRightClickedIndex)) {
283+
return;
257284
}
285+
m_pPlaylistTableModel->orderTracksByCurrPos();
258286
}
259287

260288
void PlaylistFeature::slotUnlockAllPlaylists() {

src/library/trackset/playlistfeature.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class PlaylistFeature : public BasePlaylistFeature {
3737
void slotPlaylistContentOrLockChanged(const QSet<int>& playlistIds) override;
3838
void slotPlaylistTableRenamed(int playlistId, const QString& newName) override;
3939
void slotShufflePlaylist();
40+
void slotOrderTracksByCurrentPosition();
4041
void slotUnlockAllPlaylists();
4142
void slotDeleteAllUnlockedPlaylists();
4243

@@ -49,6 +50,7 @@ class PlaylistFeature : public BasePlaylistFeature {
4950
QString getRootViewHtml() const override;
5051

5152
parented_ptr<QAction> m_pShufflePlaylistAction;
53+
parented_ptr<QAction> m_pOrderByCurrentPosAction;
5254
parented_ptr<QAction> m_pUnlockPlaylistsAction;
5355
parented_ptr<QAction> m_pDeleteAllUnlockedPlaylistsAction;
5456
};

0 commit comments

Comments
 (0)