diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 4750aa6cfc..78df2499fc 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -122,6 +122,8 @@ constexpr qint64 kMaxScrobblePointNsecs = 240LL * kNsecPerSec; constexpr int kMaxPlayedIndexes = 100; +const PlaylistItemPtr empty_item; + } // namespace Playlist::Playlist(const SharedPtr task_manager, @@ -961,12 +963,12 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, const } -void Playlist::InsertUrls(const QList &urls, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next) { +void Playlist::InsertUrls(const QList &urls, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next, const bool emit_signal) { SongLoaderInserter *inserter = new SongLoaderInserter(task_manager_, tagreader_client_, url_handlers_, collection_backend_); QObject::connect(inserter, &SongLoaderInserter::Error, this, &Playlist::Error); - inserter->Load(this, pos, play_now, enqueue, enqueue_next, urls); + inserter->Load(this, pos, play_now, enqueue, enqueue_next, emit_signal, urls); } @@ -1145,7 +1147,7 @@ void Playlist::MoveItemsWithoutUndo(int start, const QList &dest_rows) { } -void Playlist::InsertItems(const PlaylistItemPtrList &itemsIn, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next) { +void Playlist::InsertItems(const PlaylistItemPtrList &itemsIn, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next, const bool emit_signal) { if (itemsIn.isEmpty()) { return; @@ -1157,23 +1159,34 @@ void Playlist::InsertItems(const PlaylistItemPtrList &itemsIn, const int pos, co if (items.count() > kUndoItemLimit) { // Too big to keep in the undo stack. Also clear the stack because it might have been invalidated. - InsertItemsWithoutUndo(items, pos, enqueue, enqueue_next); + InsertItemsWithoutUndo(items, pos, enqueue, enqueue_next, emit_signal); undo_stack_->clear(); } else { - undo_stack_->push(new PlaylistUndoCommandInsertItems(this, items, pos, enqueue, enqueue_next)); + undo_stack_->push(new PlaylistUndoCommandInsertItems(this, items, pos, enqueue, enqueue_next, emit_signal)); } if (play_now) Q_EMIT PlayRequested(index(start, 0), AutoScroll::Maybe); } -void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const int pos, const bool enqueue, const bool enqueue_next) { +void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const int pos, const bool enqueue, const bool enqueue_next, const bool emit_signal) { if (items.isEmpty()) return; + QUuid after_track_id; const int start = pos == -1 ? static_cast(items_.count()) : pos; const int end = start + static_cast(items.count()) - 1; + QList tracks_id_added; + + if (items_.length() > 0) { + if (static_cast(pos) < items_.length()) { + after_track_id = items_[pos]->uuid(); + } + else { + after_track_id = items_[items_.length() - 1]->uuid(); + } + } bool has_generated_uuids = false; beginInsertRows(QModelIndex(), start, end); @@ -1182,6 +1195,8 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const in items_.insert(i, item); virtual_items_ << static_cast(virtual_items_.count()); + tracks_id_added << item->uuid(); + if (Song::IsLinkedCollectionSource(item->source())) { const int id = item->EffectiveMetadata().id(); if (id != -1) { @@ -1202,6 +1217,8 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const in } endInsertRows(); + if (emit_signal) Q_EMIT PlaylistItemsAdded(id_, tracks_id_added, after_track_id); + if (enqueue) { QModelIndexList indexes; for (int i = start; i <= end; ++i) { @@ -1241,7 +1258,7 @@ void Playlist::InsertSongs(const SongList &songs, const int pos, const bool play InsertSongItems(songs, pos, play_now, enqueue, enqueue_next); } -void Playlist::InsertSongsOrCollectionItems(const SongList &songs, const QString &playlist_name, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next) { +void Playlist::InsertSongsOrCollectionItems(const SongList &songs, const QString &playlist_name, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next, const bool emit_signal) { if (!playlist_name.isEmpty()) { Q_EMIT Rename(id_, playlist_name); @@ -1253,7 +1270,7 @@ void Playlist::InsertSongsOrCollectionItems(const SongList &songs, const QString items << PlaylistItem::NewFromSong(song); } - InsertItems(items, pos, play_now, enqueue, enqueue_next); + InsertItems(items, pos, play_now, enqueue, enqueue_next, emit_signal); } @@ -1783,6 +1800,18 @@ bool Playlist::removeRows(const int row, const int count, const QModelIndex &par } +bool Playlist::removeRowSignal(const int row) { + + if (row < 0 || row >= items_.size()) { + return false; + } + + undo_stack_->push(new PlaylistUndoCommandRemoveItems(this, row, 1, true)); + + return true; + +} + bool Playlist::removeRows(QList &rows) { if (rows.isEmpty()) { @@ -1812,7 +1841,7 @@ bool Playlist::removeRows(QList &rows) { } -PlaylistItemPtrList Playlist::RemoveItemsWithoutUndo(const int row, const int count) { +PlaylistItemPtrList Playlist::RemoveItemsWithoutUndo(const int row, const int count, const bool emit_signal) { if (row < 0 || row >= items_.size() || row + count > items_.size()) { return PlaylistItemPtrList(); @@ -1821,10 +1850,12 @@ PlaylistItemPtrList Playlist::RemoveItemsWithoutUndo(const int row, const int co // Remove items beginRemoveRows(QModelIndex(), row, row + count - 1); PlaylistItemPtrList items; + QList tracks_id_removed; items.reserve(count); for (int i = 0; i < count; ++i) { PlaylistItemPtr item(items_.takeAt(row)); items << item; + tracks_id_removed << item->uuid(); const int id = item->EffectiveMetadata().id(); const int source_id = item->EffectiveMetadata().source_id(); if (id != -1 && collection_items_[source_id].contains(id, item)) { @@ -1832,6 +1863,8 @@ PlaylistItemPtrList Playlist::RemoveItemsWithoutUndo(const int row, const int co } } + if (emit_signal) Q_EMIT PlaylistItemsRemoved(id_, tracks_id_removed); + // Update virtual items for (int i = row; i < items_.count() + count; ++i) { Q_ASSERT(virtual_items_.count(i) == 1); @@ -1912,6 +1945,30 @@ bool Playlist::stop_after_current() const { } +const PlaylistItemPtr &Playlist::find_item_id(const QUuid &id) const { + + for (const PlaylistItemPtr &item : items_) { + if (item->uuid() == id) { + return item; + } + } + return empty_item; + +} + +int Playlist::find_pos_id(const QUuid &id) const { + + int pos_found = 0; + for (const PlaylistItemPtr &item : items_) { + if (item->uuid() == id) { + return pos_found; + } + ++pos_found; + } + return -1; + +} + PlaylistItemPtr Playlist::current_item() const { // QList[] runs in constant time, so no need to cache current_item diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index 31b6892cf1..38a7a406c4 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -205,6 +205,8 @@ class Playlist : public QAbstractListModel { QString special_type() const { return special_type_; } void set_special_type(const QString &v) { special_type_ = v; } + const PlaylistItemPtr &find_item_id(const QUuid &id) const; + int find_pos_id(const QUuid &id) const; const PlaylistItemPtr &item_at(const int index) const { return items_[index]; } bool has_item_at(const int index) const { return index >= 0 && index < rowCount(); } @@ -233,10 +235,10 @@ class Playlist : public QAbstractListModel { void UpdateScrobblePoint(const qint64 seek_point_nanosec = 0); // Changing the playlist - void InsertItems(const PlaylistItemPtrList &itemsIn, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); + void InsertItems(const PlaylistItemPtrList &itemsIn, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false, const bool emit_signal = false); void InsertCollectionItems(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void InsertSongs(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); - void InsertSongsOrCollectionItems(const SongList &songs, const QString &playlist_name = QString(), const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); + void InsertSongsOrCollectionItems(const SongList &songs, const QString &playlist_name = QString(), const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false, const bool emit_signal = false); void InsertSmartPlaylist(PlaylistGeneratorPtr gen, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void InsertStreamingItems(StreamingServicePtr service, const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); void InsertRadioItems(const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); @@ -263,6 +265,7 @@ class Playlist : public QAbstractListModel { #endif // QAbstractListModel + PlaylistItemPtrList items() const { return items_; } int rowCount(const QModelIndex& = QModelIndex()) const override { return items_.count(); } int columnCount(const QModelIndex& = QModelIndex()) const override { return static_cast(ColumnCount); } QVariant data(const QModelIndex &idx, const int role = Qt::DisplayRole) const override; @@ -275,6 +278,7 @@ class Playlist : public QAbstractListModel { bool dropMimeData(const QMimeData *data, Qt::DropAction action, const int row, const int column, const QModelIndex &parent_index) override; void sort(const int column_number, const Qt::SortOrder order) override; bool removeRows(const int row, const int count, const QModelIndex &parent = QModelIndex()) override; + bool removeRowSignal(const int row); static Columns ChangedColumns(const Song &metadata1, const Song &metadata2); static bool MinorMetadataChange(const Song &old_metadata, const Song &new_metadata); @@ -309,7 +313,7 @@ class Playlist : public QAbstractListModel { void SetColumnAlignment(const ColumnAlignmentMap &alignment); - void InsertUrls(const QList &urls, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false); + void InsertUrls(const QList &urls, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool enqueue_next = false, const bool emit_signal = false); // Removes items with given indices from the playlist. This operation is not undoable. void RemoveItemsWithoutUndo(const QList &indicesIn); @@ -328,6 +332,11 @@ class Playlist : public QAbstractListModel { void PlayRequested(const QModelIndex idx, const Playlist::AutoScroll autoscroll); void MaybeAutoscroll(const Playlist::AutoScroll autoscroll); + // Signal for tracks added to playlist, mainly used by mpris + void PlaylistItemsAdded(const int playlist_id, const QList &tracks_id, const QUuid after_track_id); + // Signal for tracks removed from playlist, mainly used by mpris + void PlaylistItemsRemoved(const int playlist_id, const QList &tracks_id); + // Signals that the underlying list of items was changed, meaning that something was added to it, removed from it or the ordering changed. void PlaylistChanged(); void DynamicModeChanged(bool dynamic); @@ -349,8 +358,8 @@ class Playlist : public QAbstractListModel { void InsertSongItems(const SongList &songs, const int pos, const bool play_now, const bool enqueue, const bool enqueue_next = false); // Modify the playlist without changing the undo stack. These are used by our friends in PlaylistUndoCommands - void InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const int pos, const bool enqueue = false, const bool enqueue_next = false); - PlaylistItemPtrList RemoveItemsWithoutUndo(const int row, const int count); + void InsertItemsWithoutUndo(const PlaylistItemPtrList &items, const int pos, const bool enqueue = false, const bool enqueue_next = false, const bool emit_signal = false); + PlaylistItemPtrList RemoveItemsWithoutUndo(const int row, const int count, const bool emit_signal = false); void MoveItemsWithoutUndo(const QList &source_rows, int pos); void MoveItemWithoutUndo(const int source, const int dest); void MoveItemsWithoutUndo(int start, const QList &dest_rows); diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 01d60e29b3..5d4751df07 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -161,6 +161,8 @@ Playlist *PlaylistManager::AddPlaylist(const int id, const QString &name, const QObject::connect(ret, &Playlist::CurrentSongChanged, this, &PlaylistManager::CurrentSongChanged); QObject::connect(ret, &Playlist::CurrentSongMetadataChanged, this, &PlaylistManager::CurrentSongMetadataChanged); + QObject::connect(ret, &Playlist::PlaylistItemsAdded, this, &PlaylistManager::PlaylistItemsAdded); + QObject::connect(ret, &Playlist::PlaylistItemsRemoved, this, &PlaylistManager::PlaylistItemsRemoved); QObject::connect(ret, &Playlist::PlaylistChanged, this, &PlaylistManager::OneOfPlaylistsChanged); QObject::connect(ret, &Playlist::PlaylistChanged, this, &PlaylistManager::UpdateSummaryText); QObject::connect(ret, &Playlist::EditingFinished, this, &PlaylistManager::EditingFinished); @@ -499,11 +501,11 @@ void PlaylistManager::SongChangeRequestProcessed(const QUrl &url, const bool val } -void PlaylistManager::InsertUrls(const int id, const QList &urls, const int pos, const bool play_now, const bool enqueue) { +void PlaylistManager::InsertUrls(const int id, const QList &urls, const int pos, const bool play_now, const bool enqueue, const bool emit_signal) { Q_ASSERT(playlists_.contains(id)); - playlists_.constFind(id)->p->InsertUrls(urls, pos, play_now, enqueue); + playlists_.constFind(id)->p->InsertUrls(urls, pos, play_now, enqueue, emit_signal); } diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index be568aed22..82ebc5d825 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -124,7 +124,7 @@ class PlaylistManager : public PlaylistManagerInterface { void SongChangeRequestProcessed(const QUrl &url, const bool valid) override; - void InsertUrls(const int id, const QList &urls, const int pos = -1, const bool play_now = false, const bool enqueue = false); + void InsertUrls(const int id, const QList &urls, const int pos = -1, const bool play_now = false, const bool enqueue = false, const bool emit_signal = false); void InsertSongs(const int id, const SongList &songs, const int pos = -1, const bool play_now = false, const bool enqueue = false); // Removes items with given indices from the playlist. This operation is not undoable. void RemoveItemsWithoutUndo(const int id, const QList &indices); diff --git a/src/playlist/playlistmanagerinterface.h b/src/playlist/playlistmanagerinterface.h index f22b8c299c..f936e401d0 100644 --- a/src/playlist/playlistmanagerinterface.h +++ b/src/playlist/playlistmanagerinterface.h @@ -126,6 +126,8 @@ class PlaylistManagerInterface : public QObject { // Forwarded from individual playlists void CurrentSongChanged(const Song &song); void CurrentSongMetadataChanged(const Song &song); + void PlaylistItemsAdded(const int playlist_id, const QList &tracks_id, const QUuid after_track_id); + void PlaylistItemsRemoved(const int playlist_id, const QList &tracks_id); // Signals that one of manager's playlists has changed (new items, new ordering etc.) - the argument shows which. void PlaylistChanged(Playlist *playlist); diff --git a/src/playlist/playlistundocommandinsertitems.cpp b/src/playlist/playlistundocommandinsertitems.cpp index 71b184e28c..7343274c34 100644 --- a/src/playlist/playlistundocommandinsertitems.cpp +++ b/src/playlist/playlistundocommandinsertitems.cpp @@ -22,19 +22,20 @@ #include "playlistundocommandinsertitems.h" #include "playlist.h" -PlaylistUndoCommandInsertItems::PlaylistUndoCommandInsertItems(Playlist *playlist, const PlaylistItemPtrList &items, const int pos, const bool enqueue, const bool enqueue_next) +PlaylistUndoCommandInsertItems::PlaylistUndoCommandInsertItems(Playlist *playlist, const PlaylistItemPtrList &items, const int pos, const bool enqueue, const bool enqueue_next, const bool emit_signal) : PlaylistUndoCommandBase(playlist), items_(items), pos_(pos), enqueue_(enqueue), - enqueue_next_(enqueue_next) { + enqueue_next_(enqueue_next), + emit_signal_(emit_signal) { setText(QObject::tr("add %n songs", "", static_cast(items_.count()))); } void PlaylistUndoCommandInsertItems::redo() { - playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_, enqueue_next_); + playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_, enqueue_next_, emit_signal_); } void PlaylistUndoCommandInsertItems::undo() { diff --git a/src/playlist/playlistundocommandinsertitems.h b/src/playlist/playlistundocommandinsertitems.h index 8550d4c4b3..5b18a4c0ac 100644 --- a/src/playlist/playlistundocommandinsertitems.h +++ b/src/playlist/playlistundocommandinsertitems.h @@ -25,7 +25,7 @@ class PlaylistUndoCommandInsertItems : public PlaylistUndoCommandBase { public: - explicit PlaylistUndoCommandInsertItems(Playlist *playlist, const PlaylistItemPtrList &items, const int pos, const bool enqueue = false, const bool enqueue_next = false); + explicit PlaylistUndoCommandInsertItems(Playlist *playlist, const PlaylistItemPtrList &items, const int pos, const bool enqueue = false, const bool enqueue_next = false, const bool emit_signal = false); void undo() override; void redo() override; @@ -39,6 +39,7 @@ class PlaylistUndoCommandInsertItems : public PlaylistUndoCommandBase { int pos_; bool enqueue_; bool enqueue_next_; + bool emit_signal_; }; #endif // PLAYLISTUNDOCOMMANDINSERTITEMS_H diff --git a/src/playlist/playlistundocommandremoveitems.cpp b/src/playlist/playlistundocommandremoveitems.cpp index f725c6cedb..fb285d9c5d 100644 --- a/src/playlist/playlistundocommandremoveitems.cpp +++ b/src/playlist/playlistundocommandremoveitems.cpp @@ -22,7 +22,7 @@ #include "playlist.h" #include "playlistundocommandremoveitems.h" -PlaylistUndoCommandRemoveItems::PlaylistUndoCommandRemoveItems(Playlist *playlist, const int pos, const int count) : PlaylistUndoCommandBase(playlist) { +PlaylistUndoCommandRemoveItems::PlaylistUndoCommandRemoveItems(Playlist *playlist, const int pos, const int count, const bool emit_signal) : PlaylistUndoCommandBase(playlist), emit_signal_(emit_signal) { setText(QObject::tr("remove %n songs", "", count)); ranges_ << Range(pos, count); @@ -31,7 +31,7 @@ PlaylistUndoCommandRemoveItems::PlaylistUndoCommandRemoveItems(Playlist *playlis void PlaylistUndoCommandRemoveItems::redo() { for (int i = 0; i < ranges_.count(); ++i) { - ranges_[i].items_ = playlist_->RemoveItemsWithoutUndo(ranges_[i].pos_, ranges_[i].count_); + ranges_[i].items_ = playlist_->RemoveItemsWithoutUndo(ranges_[i].pos_, ranges_[i].count_, emit_signal_); } } diff --git a/src/playlist/playlistundocommandremoveitems.h b/src/playlist/playlistundocommandremoveitems.h index 0eadf5c686..4f0797dffa 100644 --- a/src/playlist/playlistundocommandremoveitems.h +++ b/src/playlist/playlistundocommandremoveitems.h @@ -27,7 +27,7 @@ class PlaylistUndoCommandRemoveItems : public PlaylistUndoCommandBase { public: - explicit PlaylistUndoCommandRemoveItems(Playlist *playlist, const int pos, const int count); + explicit PlaylistUndoCommandRemoveItems(Playlist *playlist, const int pos, const int count, const bool emit_signal = false); int id() const override { return static_cast(PlaylistUndoCommandBase::Type::RemoveItems); } @@ -44,6 +44,7 @@ class PlaylistUndoCommandRemoveItems : public PlaylistUndoCommandBase { }; QList ranges_; + bool emit_signal_; }; #endif // PLAYLISTUNDOCOMMANDREMOVEITEMS_H diff --git a/src/playlist/songloaderinserter.cpp b/src/playlist/songloaderinserter.cpp index a1e467faa3..de3ffdf3d1 100644 --- a/src/playlist/songloaderinserter.cpp +++ b/src/playlist/songloaderinserter.cpp @@ -54,13 +54,14 @@ SongLoaderInserter::SongLoaderInserter(const SharedPtr task_manager SongLoaderInserter::~SongLoaderInserter() { qDeleteAll(pending_); } -void SongLoaderInserter::Load(Playlist *destination, const int row, const bool play_now, const bool enqueue, const bool enqueue_next, const QList &urls) { +void SongLoaderInserter::Load(Playlist *destination, const int row, const bool play_now, const bool enqueue, const bool enqueue_next, const bool emit_signal, const QList &urls) { destination_ = destination; row_ = row; play_now_ = play_now; enqueue_ = enqueue; enqueue_next_ = enqueue_next; + emit_signal_ = emit_signal; QObject::connect(destination, &Playlist::destroyed, this, &SongLoaderInserter::DestinationDestroyed); QObject::connect(this, &SongLoaderInserter::PreloadFinished, this, &SongLoaderInserter::InsertSongs); @@ -176,7 +177,7 @@ void SongLoaderInserter::InsertSongs() { // Insert songs (that haven't been completely loaded) to allow user to see and play them while not loaded completely if (destination_) { - destination_->InsertSongsOrCollectionItems(songs_, playlist_name_, row_, play_now_, enqueue_, enqueue_next_); + destination_->InsertSongsOrCollectionItems(songs_, playlist_name_, row_, play_now_, enqueue_, enqueue_next_, emit_signal_); } } diff --git a/src/playlist/songloaderinserter.h b/src/playlist/songloaderinserter.h index 5aaf2b8568..8e7d4c6276 100644 --- a/src/playlist/songloaderinserter.h +++ b/src/playlist/songloaderinserter.h @@ -52,7 +52,7 @@ class SongLoaderInserter : public QObject { ~SongLoaderInserter() override; - void Load(Playlist *destination, const int row, const bool play_now, const bool enqueue, const bool enqueue_next, const QList &urls); + void Load(Playlist *destination, const int row, const bool play_now, const bool enqueue, const bool enqueue_next, const bool emit_signal, const QList &urls); void LoadAudioCD(Playlist *destination, const int row, const bool play_now, const bool enqueue, const bool enqueue_next); Q_SIGNALS: @@ -81,6 +81,7 @@ class SongLoaderInserter : public QObject { bool play_now_; bool enqueue_; bool enqueue_next_; + bool emit_signal_; SongList songs_; QString playlist_name_;