Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/library/dao/playlistdao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,45 @@ int PlaylistDAO::tracksInPlaylist(const int playlistId) const {
return count;
}

void PlaylistDAO::orderTracksByCurrPos(const int playlistId,
QList<std::pair<TrackId, int>>& newOrder) {
if (newOrder.isEmpty() ||
playlistId == kInvalidPlaylistId ||
isPlaylistLocked(playlistId) ||
newOrder.size() != tracksInPlaylist(playlistId)) {
return;
}

ScopedTransaction transaction(m_database);
QSqlQuery query(m_database);
query.prepare(QStringLiteral(
"UPDATE PlaylistTracks "
"SET position=:new_pos "
"WHERE position=:old_pos AND "
"track_id=:track_id AND "
"playlist_id=:pl_id"));
int newPos = 1;
for (auto [trackId, oldPos] : newOrder) {
VERIFY_OR_DEBUG_ASSERT(trackId.isValid()) {
return;
}
query.bindValue(":new_pos", newPos++);
query.bindValue(":old_pos", oldPos);
query.bindValue(":track_id", trackId.toVariant());
query.bindValue(":pl_id", playlistId);
if (!query.exec()) {
// We temporarily have duplicate positions, so abort the entire operation
// to not leave the playlist with an invalid state.
LOG_FAILED_QUERY(query);
return;
}
}

transaction.commit();

emit tracksMoved(QSet<int>{playlistId});
}

void PlaylistDAO::moveTrack(const int playlistId, const int oldPosition, const int newPosition) {
ScopedTransaction transaction(m_database);
QSqlQuery query(m_database);
Expand Down
4 changes: 4 additions & 0 deletions src/library/dao/playlistdao.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class PlaylistDAO : public QObject, public virtual DAO {
bool copyPlaylistTracks(const int sourcePlaylistID, const int targetPlaylistID);
// Returns the number of tracks in the given playlist.
int tracksInPlaylist(const int playlistId) const;
// This receives a track list that represents the current order (sorted by BPM for example)
// and adopts this order for `position` in the playlist.
// Returns true on success.
void orderTracksByCurrPos(const int playlistId, QList<std::pair<TrackId, int>>& newOrder);
// moved Track to a new position
void moveTrack(const int playlistId,
const int oldPosition, const int newPosition);
Expand Down
19 changes: 18 additions & 1 deletion src/library/playlisttablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo
int numOfTracks = rowCount();
if (shuffle.count() > 1) {
// if there is more then one track selected, shuffle selection only
foreach (QModelIndex shuffleIndex, shuffle) {
for (const QModelIndex& shuffleIndex : std::as_const(shuffle)) {
int oldPosition = shuffleIndex.sibling(shuffleIndex.row(), positionColumn).data().toInt();
if (oldPosition != excludePos) {
positions.append(oldPosition);
Expand All @@ -339,6 +339,23 @@ void PlaylistTableModel::shuffleTracks(const QModelIndexList& shuffle, const QMo
m_pTrackCollectionManager->internalCollection()->getPlaylistDAO().shuffleTracks(m_iPlaylistId, positions, allIds);
}

void PlaylistTableModel::orderTracksByCurrPos() {
QList<std::pair<TrackId, int>> idPosList;
int numOfTracks = rowCount();
idPosList.reserve(numOfTracks);
const int positionColumn = fieldIndex(ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_POSITION);
const int idColumn = fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_ID);
// Set up list of all IDs
for (int i = 0; i < numOfTracks; i++) {
TrackId trackId(index(i, idColumn).data());
int oldPosition = index(i, positionColumn).data().toInt();
idPosList.append(std::make_pair(trackId, oldPosition));
}
m_pTrackCollectionManager->internalCollection()
->getPlaylistDAO()
.orderTracksByCurrPos(m_iPlaylistId, idPosList);
}

const QList<int> PlaylistTableModel::getSelectedPositions(const QModelIndexList& indices) const {
if (indices.isEmpty()) {
return {};
Expand Down
4 changes: 3 additions & 1 deletion src/library/playlisttablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ class PlaylistTableModel final : public TrackSetTableModel {
bool appendTrack(TrackId trackId);
void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) override;
void removeTrack(const QModelIndex& index);
void shuffleTracks(const QModelIndexList& shuffle, const QModelIndex& exclude);
void shuffleTracks(const QModelIndexList& shuffle = QModelIndexList(),
const QModelIndex& exclude = QModelIndex());
void orderTracksByCurrPos();

bool isColumnInternal(int column) final;
bool isColumnHiddenByDefault(int column) final;
Expand Down
34 changes: 31 additions & 3 deletions src/library/trackset/playlistfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ PlaylistFeature::PlaylistFeature(Library* pLibrary, UserSettingsPointer pConfig)
this,
&PlaylistFeature::slotShufflePlaylist);

m_pOrderByCurrentPosAction = make_parented<QAction>(tr("Adopt current order"), this);
connect(m_pOrderByCurrentPosAction,
&QAction::triggered,
this,
&PlaylistFeature::slotOrderTracksByCurrentPosition);

m_pUnlockPlaylistsAction =
make_parented<QAction>(tr("Unlock all playlists"), this);
connect(m_pUnlockPlaylistsAction,
Expand Down Expand Up @@ -81,6 +87,8 @@ void PlaylistFeature::onRightClickChild(
int playlistId = playlistIdFromIndex(index);

bool locked = m_playlistDao.isPlaylistLocked(playlistId);
m_pShufflePlaylistAction->setEnabled(!locked);
m_pOrderByCurrentPosAction->setEnabled(!locked && isChildIndexSelectedInSidebar(index));
m_pDeletePlaylistAction->setEnabled(!locked);
m_pRenamePlaylistAction->setEnabled(!locked);

Expand All @@ -92,6 +100,7 @@ void PlaylistFeature::onRightClickChild(
// 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.addAction(m_pOrderByCurrentPosAction);
menu.addSeparator();
menu.addAction(m_pRenamePlaylistAction);
menu.addAction(m_pDuplicatePlaylistAction);
Expand Down Expand Up @@ -228,17 +237,17 @@ void PlaylistFeature::slotShufflePlaylist() {

// Shuffle all tracks
// If the playlist is loaded/visible shuffle only selected tracks
QModelIndexList selection;
if (isChildIndexSelectedInSidebar(m_lastRightClickedIndex) &&
m_pPlaylistTableModel->getPlaylist() == playlistId) {
QModelIndexList selection;
if (m_pLibraryWidget) {
WTrackTableView* view = dynamic_cast<WTrackTableView*>(
m_pLibraryWidget->getActiveView());
if (view != nullptr) {
selection = view->selectionModel()->selectedIndexes();
}
}
m_pPlaylistTableModel->shuffleTracks(selection, QModelIndex());
m_pPlaylistTableModel->shuffleTracks(selection);
} else {
// Create a temp model so we don't need to select the playlist
// in the persistent model in order to shuffle it
Expand All @@ -253,8 +262,27 @@ void PlaylistFeature::slotShufflePlaylist() {
Qt::AscendingOrder);
pPlaylistTableModel->select();

pPlaylistTableModel->shuffleTracks(selection, QModelIndex());
pPlaylistTableModel->shuffleTracks();
}
}

void PlaylistFeature::slotOrderTracksByCurrentPosition() {
int playlistId = playlistIdFromIndex(m_lastRightClickedIndex);
if (playlistId == kInvalidPlaylistId) {
return;
}

if (m_playlistDao.isPlaylistLocked(playlistId)) {
qDebug() << "Can't adopt current sorting for locked playlist" << playlistId
<< m_playlistDao.getPlaylistName(playlistId);
return;
}
// Note(ronso0) I propose to proceed only if the playlist is selected and loaded.
// without playlist content visible we don't have a preview.
if (!isChildIndexSelectedInSidebar(m_lastRightClickedIndex)) {
return;
}
m_pPlaylistTableModel->orderTracksByCurrPos();
}

void PlaylistFeature::slotUnlockAllPlaylists() {
Expand Down
2 changes: 2 additions & 0 deletions src/library/trackset/playlistfeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class PlaylistFeature : public BasePlaylistFeature {
void slotPlaylistContentOrLockChanged(const QSet<int>& playlistIds) override;
void slotPlaylistTableRenamed(int playlistId, const QString& newName) override;
void slotShufflePlaylist();
void slotOrderTracksByCurrentPosition();
void slotUnlockAllPlaylists();
void slotDeleteAllUnlockedPlaylists();

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

parented_ptr<QAction> m_pShufflePlaylistAction;
parented_ptr<QAction> m_pOrderByCurrentPosAction;
parented_ptr<QAction> m_pUnlockPlaylistsAction;
parented_ptr<QAction> m_pDeleteAllUnlockedPlaylistsAction;
};
Loading