From 3626d8496470b98c3492b03fb106f63d962436d0 Mon Sep 17 00:00:00 2001 From: Mark Furneaux Date: Sun, 18 Mar 2018 22:44:00 -0400 Subject: [PATCH] Add play next from library --- src/core/mimedata.h | 6 +- src/library/libraryview.cpp | 11 ++++ src/library/libraryview.h | 2 + src/playlist/playlist.cpp | 76 ++++++++++++++---------- src/playlist/playlist.h | 20 +++---- src/playlist/playlistundocommands.cpp | 6 +- src/playlist/playlistundocommands.h | 4 +- src/playlist/songloaderinserter.cpp | 6 +- src/playlist/songloaderinserter.h | 5 +- src/smartplaylists/generatorinserter.cpp | 3 +- src/smartplaylists/generatorinserter.h | 3 +- src/ui/mainwindow.cpp | 2 + 12 files changed, 89 insertions(+), 55 deletions(-) diff --git a/src/core/mimedata.h b/src/core/mimedata.h index 6c4e88420..471574807 100644 --- a/src/core/mimedata.h +++ b/src/core/mimedata.h @@ -27,12 +27,13 @@ class MimeData : public QMimeData { Q_OBJECT public: - MimeData(bool clear = false, bool play_now = false, bool enqueue = false, + MimeData(bool clear = false, bool play_now = false, bool enqueue = false, bool enqueue_next_now = false, bool open_in_new_playlist = false) : override_user_settings_(false), clear_first_(clear), play_now_(play_now), enqueue_now_(enqueue), + enqueue_next_now_(enqueue_next_now), open_in_new_playlist_(open_in_new_playlist), name_for_new_playlist_(QString()), from_doubleclick_(false) {} @@ -53,6 +54,9 @@ class MimeData : public QMimeData { // If this is set then the items are added to the queue after being inserted. bool enqueue_now_; + // If this is set then the items are added to the beginning of the queue after being inserted. + bool enqueue_next_now_; + // If this is set then the items are inserted into a newly created playlist. bool open_in_new_playlist_; diff --git a/src/library/libraryview.cpp b/src/library/libraryview.cpp index 9297d0b4e..1def6bfb8 100644 --- a/src/library/libraryview.cpp +++ b/src/library/libraryview.cpp @@ -392,6 +392,9 @@ void LibraryView::contextMenuEvent(QContextMenuEvent* e) { add_to_playlist_enqueue_ = context_menu_->addAction( IconLoader::Load("go-next", IconLoader::Base), tr("Queue track"), this, SLOT(AddToPlaylistEnqueue())); + add_to_playlist_enqueue_next_ = context_menu_->addAction( + IconLoader::Load("go-next", IconLoader::Base), tr("Play next"), this, + SLOT(AddToPlaylistEnqueueNext())); context_menu_->addSeparator(); search_for_this_ = context_menu_->addAction( IconLoader::Load("system-search", IconLoader::Base), @@ -616,6 +619,14 @@ void LibraryView::AddToPlaylistEnqueue() { emit AddToPlaylistSignal(data); } +void LibraryView::AddToPlaylistEnqueueNext() { + QMimeData* data = model()->mimeData(selectedIndexes()); + if (MimeData* mime_data = qobject_cast(data)) { + mime_data->enqueue_next_now_ = true; + } + emit AddToPlaylistSignal(data); +} + void LibraryView::OpenInNewPlaylist() { QMimeData* data = model()->mimeData(selectedIndexes()); if (MimeData* mime_data = qobject_cast(data)) { diff --git a/src/library/libraryview.h b/src/library/libraryview.h index a747d0aa0..46945cb2d 100644 --- a/src/library/libraryview.h +++ b/src/library/libraryview.h @@ -92,6 +92,7 @@ signals: void Load(); void AddToPlaylist(); void AddToPlaylistEnqueue(); + void AddToPlaylistEnqueueNext(); void OpenInNewPlaylist(); void Organise(); void CopyToDevice(); @@ -131,6 +132,7 @@ signals: QAction* load_; QAction* add_to_playlist_; QAction* add_to_playlist_enqueue_; + QAction* add_to_playlist_enqueue_next_; QAction* open_in_new_playlist_; QAction* organise_; QAction* copy_to_device_; diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 80a17807e..ac69dd2c6 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -152,14 +152,14 @@ Playlist::~Playlist() { template void Playlist::InsertSongItems(const SongList& songs, int pos, bool play_now, - bool enqueue) { + bool enqueue, bool enqueue_next) { PlaylistItemList items; for (const Song& song : songs) { items << PlaylistItemPtr(new T(song)); } - InsertItems(items, pos, play_now, enqueue); + InsertItems(items, pos, play_now, enqueue, enqueue_next); } QVariant Playlist::headerData(int section, Qt::Orientation, int role) const { @@ -703,7 +703,7 @@ void Playlist::InsertDynamicItems(int count) { connect(inserter, SIGNAL(PlayRequested(QModelIndex)), SIGNAL(PlayRequested(QModelIndex))); - inserter->Load(this, -1, false, false, dynamic_playlist_, count); + inserter->Load(this, -1, false, false, false, dynamic_playlist_, count); } Qt::ItemFlags Playlist::flags(const QModelIndex& index) const { @@ -733,12 +733,14 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, bool play_now = false; bool enqueue_now = false; + bool enqueue_next_now = false; if (const MimeData* mime_data = qobject_cast(data)) { if (mime_data->clear_first_) { Clear(); } play_now = mime_data->play_now_; enqueue_now = mime_data->enqueue_now_; + enqueue_next_now = mime_data->enqueue_next_now_; } if (const SongMimeData* song_data = qobject_cast(data)) { @@ -748,33 +750,33 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, if (song_data->backend && song_data->backend->songs_table() == Library::kSongsTable) InsertSongItems(song_data->songs, row, play_now, - enqueue_now); + enqueue_now, enqueue_next_now); else if (song_data->backend && song_data->backend->songs_table() == MagnatuneService::kSongsTable) InsertSongItems(song_data->songs, row, play_now, - enqueue_now); + enqueue_now, enqueue_next_now); else if (song_data->backend && song_data->backend->songs_table() == JamendoService::kSongsTable) InsertSongItems(song_data->songs, row, play_now, - enqueue_now); + enqueue_now, enqueue_next_now); else InsertSongItems(song_data->songs, row, play_now, - enqueue_now); + enqueue_now, enqueue_next_now); } else if (const InternetMimeData* internet_data = qobject_cast(data)) { // Dragged from the Internet pane InsertInternetItems(internet_data->model, internet_data->indexes, row, - play_now, enqueue_now); + play_now, enqueue_now, enqueue_next_now); } else if (const InternetSongMimeData* internet_song_data = qobject_cast(data)) { InsertInternetItems(internet_song_data->service, internet_song_data->songs, - row, play_now, enqueue_now); + row, play_now, enqueue_now, enqueue_next_now); } else if (const GeneratorMimeData* generator_data = qobject_cast(data)) { - InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now); + InsertSmartPlaylist(generator_data->generator_, row, play_now, enqueue_now, enqueue_next_now); } else if (const PlaylistItemMimeData* item_data = qobject_cast(data)) { - InsertItems(item_data->items_, row, play_now, enqueue_now); + InsertItems(item_data->items_, row, play_now, enqueue_now, enqueue_next_now); } else if (data->hasFormat(kRowsMimetype)) { // Dragged from the playlist // Rearranging it is tricky... @@ -809,7 +811,7 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, if (items.count() > kUndoItemLimit) { // Too big to keep in the undo stack. Also clear the stack because it // might have been invalidated. - InsertItemsWithoutUndo(items, row, false); + InsertItemsWithoutUndo(items, row, false, false); undo_stack_->clear(); } else { undo_stack_->push( @@ -828,26 +830,26 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action, SongLoaderInserter* inserter = new SongLoaderInserter( task_manager_, library_, backend_->app()->player()); connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString))); - inserter->LoadAudioCD(this, row, play_now, enqueue_now); + inserter->LoadAudioCD(this, row, play_now, enqueue_now, enqueue_next_now); } else if (data->hasUrls()) { // URL list dragged from the file list or some other app - InsertUrls(data->urls(), row, play_now, enqueue_now); + InsertUrls(data->urls(), row, play_now, enqueue_now, enqueue_next_now); } return true; } void Playlist::InsertUrls(const QList& urls, int pos, bool play_now, - bool enqueue) { + bool enqueue, bool enqueue_next) { SongLoaderInserter* inserter = new SongLoaderInserter( task_manager_, library_, backend_->app()->player()); connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString))); - inserter->Load(this, pos, play_now, enqueue, urls); + inserter->Load(this, pos, play_now, enqueue, enqueue_next, urls); } void Playlist::InsertSmartPlaylist(GeneratorPtr generator, int pos, - bool play_now, bool enqueue) { + bool play_now, bool enqueue, bool enqueue_next) { // Hack: If the generator hasn't got a library set then use the main one if (!generator->library()) { generator->set_library(library_); @@ -857,7 +859,7 @@ void Playlist::InsertSmartPlaylist(GeneratorPtr generator, int pos, new GeneratorInserter(task_manager_, library_, this); connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString))); - inserter->Load(this, pos, play_now, enqueue, generator); + inserter->Load(this, pos, play_now, enqueue, enqueue_next, generator); if (generator->is_dynamic()) { TurnOnDynamicPlaylist(generator); @@ -976,7 +978,7 @@ void Playlist::MoveItemsWithoutUndo(int start, const QList& dest_rows) { } void Playlist::InsertItems(const PlaylistItemList& itemsIn, int pos, - bool play_now, bool enqueue) { + bool play_now, bool enqueue, bool enqueue_next) { if (itemsIn.isEmpty()) return; PlaylistItemList items = itemsIn; @@ -1026,18 +1028,18 @@ void Playlist::InsertItems(const PlaylistItemList& itemsIn, int pos, 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); + InsertItemsWithoutUndo(items, pos, enqueue, enqueue_next); undo_stack_->clear(); } else { undo_stack_->push( - new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue)); + new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue, enqueue_next)); } if (play_now) emit PlayRequested(index(start, 0)); } void Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items, int pos, - bool enqueue) { + bool enqueue, bool enqueue_next) { if (items.isEmpty()) return; const int start = pos == -1 ? items_.count() : pos; @@ -1072,22 +1074,30 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items, int pos, queue_->ToggleTracks(indexes); } + if (enqueue_next) { + QModelIndexList indexes; + for (int i = start; i <= end; ++i) { + indexes << index(i, 0); + } + queue_->InsertFirst(indexes); + } + Save(); ReshuffleIndices(); } void Playlist::InsertLibraryItems(const SongList& songs, int pos, bool play_now, - bool enqueue) { - InsertSongItems(songs, pos, play_now, enqueue); + bool enqueue, bool enqueue_next) { + InsertSongItems(songs, pos, play_now, enqueue, enqueue_next); } void Playlist::InsertSongs(const SongList& songs, int pos, bool play_now, - bool enqueue) { - InsertSongItems(songs, pos, play_now, enqueue); + bool enqueue, bool enqueue_next) { + InsertSongItems(songs, pos, play_now, enqueue, enqueue_next); } void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos, - bool play_now, bool enqueue) { + bool play_now, bool enqueue, bool enqueue_next) { PlaylistItemList items; for (const Song& song : songs) { if (song.is_library_song()) { @@ -1096,12 +1106,12 @@ void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos, items << PlaylistItemPtr(new SongPlaylistItem(song)); } } - InsertItems(items, pos, play_now, enqueue); + InsertItems(items, pos, play_now, enqueue, enqueue_next); } void Playlist::InsertInternetItems(const InternetModel* model, const QModelIndexList& items, int pos, - bool play_now, bool enqueue) { + bool play_now, bool enqueue, bool enqueue_next) { PlaylistItemList playlist_items; QList song_urls; @@ -1120,23 +1130,23 @@ void Playlist::InsertInternetItems(const InternetModel* model, } if (!song_urls.isEmpty()) { - InsertUrls(song_urls, pos, play_now, enqueue); + InsertUrls(song_urls, pos, play_now, enqueue, enqueue_next); play_now = false; } - InsertItems(playlist_items, pos, play_now, enqueue); + InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next); } void Playlist::InsertInternetItems(InternetService* service, const SongList& songs, int pos, - bool play_now, bool enqueue) { + bool play_now, bool enqueue, bool enqueue_next) { PlaylistItemList playlist_items; for (const Song& song : songs) { playlist_items << shared_ptr( new InternetPlaylistItem(service, song)); } - InsertItems(playlist_items, pos, play_now, enqueue); + InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next); } void Playlist::UpdateItems(const SongList& songs) { diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index fefc970e2..ad42721a1 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -235,18 +235,18 @@ class Playlist : public QAbstractListModel { // Changing the playlist void InsertItems(const PlaylistItemList& items, int pos = -1, - bool play_now = false, bool enqueue = false); + bool play_now = false, bool enqueue = false, bool enqueue_next = false); void InsertLibraryItems(const SongList& items, int pos = -1, - bool play_now = false, bool enqueue = false); + bool play_now = false, bool enqueue = false, bool enqueue_next = false); void InsertSongs(const SongList& items, int pos = -1, bool play_now = false, - bool enqueue = false); + bool enqueue = false, bool enqueue_next = false); void InsertSongsOrLibraryItems(const SongList& items, int pos = -1, - bool play_now = false, bool enqueue = false); + bool play_now = false, bool enqueue = false, bool enqueue_next = false); void InsertSmartPlaylist(smart_playlists::GeneratorPtr gen, int pos = -1, - bool play_now = false, bool enqueue = false); + bool play_now = false, bool enqueue = false, bool enqueue_next = false); void InsertInternetItems(InternetService* service, const SongList& songs, int pos = -1, bool play_now = false, - bool enqueue = false); + bool enqueue = false, bool enqueue_next = false); void ReshuffleIndices(); // If this playlist contains the current item, this method will apply the @@ -335,7 +335,7 @@ class Playlist : public QAbstractListModel { void SetColumnAlignment(const ColumnAlignmentMap& alignment); void InsertUrls(const QList& urls, int pos = -1, bool play_now = false, - bool enqueue = false); + bool enqueue = false, bool enqueue_next = false); // Removes items with given indices from the playlist. This operation is not // undoable. void RemoveItemsWithoutUndo(const QList& indices); @@ -366,18 +366,18 @@ signals: void InsertInternetItems(const InternetModel* model, const QModelIndexList& items, int pos, bool play_now, - bool enqueue); + bool enqueue, bool enqueue_next = false); template void InsertSongItems(const SongList& songs, int pos, bool play_now, - bool enqueue); + bool enqueue, bool enqueue_next = false); void InsertDynamicItems(int count); // Modify the playlist without changing the undo stack. These are used by // our friends in PlaylistUndoCommands void InsertItemsWithoutUndo(const PlaylistItemList& items, int pos, - bool enqueue = false); + bool enqueue = false, bool enqueue_next = false); PlaylistItemList RemoveItemsWithoutUndo(int pos, int count); void MoveItemsWithoutUndo(const QList& source_rows, int pos); void MoveItemWithoutUndo(int source, int dest); diff --git a/src/playlist/playlistundocommands.cpp b/src/playlist/playlistundocommands.cpp index 8e70ef4c3..f2c467773 100644 --- a/src/playlist/playlistundocommands.cpp +++ b/src/playlist/playlistundocommands.cpp @@ -23,13 +23,13 @@ namespace PlaylistUndoCommands { Base::Base(Playlist* playlist) : QUndoCommand(0), playlist_(playlist) {} InsertItems::InsertItems(Playlist* playlist, const PlaylistItemList& items, - int pos, bool enqueue) - : Base(playlist), items_(items), pos_(pos), enqueue_(enqueue) { + int pos, bool enqueue, bool enqueue_next) + : Base(playlist), items_(items), pos_(pos), enqueue_(enqueue), enqueue_next_(enqueue_next) { setText(tr("add %n songs", "", items_.count())); } void InsertItems::redo() { - playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_); + playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_, enqueue_next_); } void InsertItems::undo() { diff --git a/src/playlist/playlistundocommands.h b/src/playlist/playlistundocommands.h index f2860e9a6..09b379ec0 100644 --- a/src/playlist/playlistundocommands.h +++ b/src/playlist/playlistundocommands.h @@ -40,8 +40,7 @@ class Base : public QUndoCommand { class InsertItems : public Base { public: - InsertItems(Playlist* playlist, const PlaylistItemList& items, int pos, - bool enqueue = false); + InsertItems(Playlist* playlist, const PlaylistItemList& items, int pos, bool enqueue = false, bool enqueue_next = false); void undo(); void redo(); @@ -56,6 +55,7 @@ class InsertItems : public Base { PlaylistItemList items_; int pos_; bool enqueue_; + bool enqueue_next_; }; class RemoveItems : public Base { diff --git a/src/playlist/songloaderinserter.cpp b/src/playlist/songloaderinserter.cpp index 031c64518..04e87d18e 100644 --- a/src/playlist/songloaderinserter.cpp +++ b/src/playlist/songloaderinserter.cpp @@ -37,11 +37,12 @@ SongLoaderInserter::SongLoaderInserter(TaskManager* task_manager, SongLoaderInserter::~SongLoaderInserter() { qDeleteAll(pending_); } void SongLoaderInserter::Load(Playlist* destination, int row, bool play_now, - bool enqueue, const QList& urls) { + bool enqueue, bool enqueue_next, const QList& urls) { destination_ = destination; row_ = row; play_now_ = play_now; enqueue_ = enqueue; + enqueue_next_ = enqueue_next; connect(destination, SIGNAL(destroyed()), SLOT(DestinationDestroyed())); connect(this, SIGNAL(PreloadFinished()), SLOT(InsertSongs())); @@ -78,11 +79,12 @@ void SongLoaderInserter::Load(Playlist* destination, int row, bool play_now, // In the meantime, MusicBrainz will be queried to get songs' metadata. // AudioCDTagsLoaded will be called next, and playlist's items will be updated. void SongLoaderInserter::LoadAudioCD(Playlist* destination, int row, - bool play_now, bool enqueue) { + bool play_now, bool enqueue, bool enqueue_next) { destination_ = destination; row_ = row; play_now_ = play_now; enqueue_ = enqueue; + enqueue_next_ = enqueue_next; SongLoader* loader = new SongLoader(library_, player_, this); NewClosure(loader, SIGNAL(AudioCDTracksLoaded()), diff --git a/src/playlist/songloaderinserter.h b/src/playlist/songloaderinserter.h index 14b3783a6..ea264198c 100644 --- a/src/playlist/songloaderinserter.h +++ b/src/playlist/songloaderinserter.h @@ -39,9 +39,9 @@ class SongLoaderInserter : public QObject { LibraryBackendInterface* library, const Player* player); ~SongLoaderInserter(); - void Load(Playlist* destination, int row, bool play_now, bool enqueue, + void Load(Playlist* destination, int row, bool play_now, bool enqueue, bool enqueue_next, const QList& urls); - void LoadAudioCD(Playlist* destination, int row, bool play_now, bool enqueue); + void LoadAudioCD(Playlist* destination, int row, bool play_now, bool enqueue, bool enqueue_now); signals: void Error(const QString& message); @@ -64,6 +64,7 @@ signals: int row_; bool play_now_; bool enqueue_; + bool enqueue_next_; SongList songs_; diff --git a/src/smartplaylists/generatorinserter.cpp b/src/smartplaylists/generatorinserter.cpp index d57ce0bfd..f732ec4f2 100644 --- a/src/smartplaylists/generatorinserter.cpp +++ b/src/smartplaylists/generatorinserter.cpp @@ -43,7 +43,7 @@ static PlaylistItemList Generate(GeneratorPtr generator, int dynamic_count) { } void GeneratorInserter::Load(Playlist* destination, int row, bool play_now, - bool enqueue, GeneratorPtr generator, + bool enqueue, bool enqueue_next, GeneratorPtr generator, int dynamic_count) { task_id_ = task_manager_->StartTask(tr("Loading smart playlist")); @@ -51,6 +51,7 @@ void GeneratorInserter::Load(Playlist* destination, int row, bool play_now, row_ = row; play_now_ = play_now; enqueue_ = enqueue; + enqueue_next_ = enqueue_next; is_dynamic_ = generator->is_dynamic(); connect(generator.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString))); diff --git a/src/smartplaylists/generatorinserter.h b/src/smartplaylists/generatorinserter.h index 2ededc94e..1d3df5fd0 100644 --- a/src/smartplaylists/generatorinserter.h +++ b/src/smartplaylists/generatorinserter.h @@ -40,7 +40,7 @@ class GeneratorInserter : public QObject { GeneratorInserter(TaskManager* task_manager, LibraryBackend* library, QObject* parent); - void Load(Playlist* destination, int row, bool play_now, bool enqueue, + void Load(Playlist* destination, int row, bool play_now, bool enqueue, bool enqueue_next, GeneratorPtr generator, int dynamic_count = 0); signals: @@ -59,6 +59,7 @@ signals: int row_; bool play_now_; bool enqueue_; + bool enqueue_next_; bool is_dynamic_; }; diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index fffab3a16..7f83ec39d 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -1782,6 +1782,8 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos, else playlist_queue_->setIcon(IconLoader::Load("go-next", IconLoader::Base)); + playlist_queue_play_next_->setIcon(IconLoader::Load("go-next", IconLoader::Base)); + if (!index.isValid()) { ui_->action_selection_set_value->setVisible(false); ui_->action_edit_value->setVisible(false);