Merge remote-tracking branch 'upstream/master' into qt5

This commit is contained in:
Chocobozzz 2018-05-14 08:25:27 +02:00
commit 16b4f9a991
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
28 changed files with 194 additions and 71 deletions

View File

@ -104,6 +104,7 @@ message SongMetadata {
TRUEAUDIO = 11; TRUEAUDIO = 11;
CDDA = 12; CDDA = 12;
OGGOPUS = 13; OGGOPUS = 13;
WAVPACK = 14;
STREAM = 99; STREAM = 99;
} }

View File

@ -52,6 +52,7 @@
#include <unsynchronizedlyricsframe.h> #include <unsynchronizedlyricsframe.h>
#include <vorbisfile.h> #include <vorbisfile.h>
#include <wavfile.h> #include <wavfile.h>
#include <wavpackfile.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -659,6 +660,8 @@ pb::tagreader::SongMetadata_Type TagReader::GuessFileType(
return pb::tagreader::SongMetadata_Type_WAV; return pb::tagreader::SongMetadata_Type_WAV;
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file()))
return pb::tagreader::SongMetadata_Type_TRUEAUDIO; return pb::tagreader::SongMetadata_Type_TRUEAUDIO;
if (dynamic_cast<TagLib::WavPack::File*>(fileref->file()))
return pb::tagreader::SongMetadata_Type_WAVPACK;
return pb::tagreader::SongMetadata_Type_UNKNOWN; return pb::tagreader::SongMetadata_Type_UNKNOWN;
} }

View File

@ -16,6 +16,7 @@ message SongMetadata {
TRUEAUDIO = 11; TRUEAUDIO = 11;
CDDA = 12; CDDA = 12;
OGGOPUS = 13; OGGOPUS = 13;
WAVPACK = 14;
STREAM = 99; STREAM = 99;
} }

View File

@ -28,11 +28,12 @@ class MimeData : public QMimeData {
public: public:
MimeData(bool clear = false, bool play_now = false, bool enqueue = false, MimeData(bool clear = false, bool play_now = false, bool enqueue = false,
bool open_in_new_playlist = false) bool enqueue_next_now = false, bool open_in_new_playlist = false)
: override_user_settings_(false), : override_user_settings_(false),
clear_first_(clear), clear_first_(clear),
play_now_(play_now), play_now_(play_now),
enqueue_now_(enqueue), enqueue_now_(enqueue),
enqueue_next_now_(enqueue_next_now),
open_in_new_playlist_(open_in_new_playlist), open_in_new_playlist_(open_in_new_playlist),
name_for_new_playlist_(QString()), name_for_new_playlist_(QString()),
from_doubleclick_(false) {} from_doubleclick_(false) {}
@ -53,6 +54,10 @@ class MimeData : public QMimeData {
// If this is set then the items are added to the queue after being inserted. // If this is set then the items are added to the queue after being inserted.
bool enqueue_now_; 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. // If this is set then the items are inserted into a newly created playlist.
bool open_in_new_playlist_; bool open_in_new_playlist_;

View File

@ -427,6 +427,8 @@ QString Song::TextForFiletype(FileType type) {
return QObject::tr("AIFF"); return QObject::tr("AIFF");
case Song::Type_Wav: case Song::Type_Wav:
return QObject::tr("Wav"); return QObject::tr("Wav");
case Song::Type_WavPack:
return QObject::tr("WavPack");
case Song::Type_TrueAudio: case Song::Type_TrueAudio:
return QObject::tr("TrueAudio"); return QObject::tr("TrueAudio");
case Song::Type_Cdda: case Song::Type_Cdda:
@ -447,6 +449,7 @@ bool Song::IsFileLossless() const {
case Song::Type_Flac: case Song::Type_Flac:
case Song::Type_OggFlac: case Song::Type_OggFlac:
case Song::Type_Wav: case Song::Type_Wav:
case Song::Type_WavPack:
return true; return true;
default: default:
return false; return false;
@ -673,7 +676,8 @@ void Song::InitFromFilePartial(const QString& filename) {
if (suffix == "mp3" || suffix == "ogg" || suffix == "flac" || if (suffix == "mp3" || suffix == "ogg" || suffix == "flac" ||
suffix == "mpc" || suffix == "m4a" || suffix == "aac" || suffix == "mpc" || suffix == "m4a" || suffix == "aac" ||
suffix == "wma" || suffix == "mp4" || suffix == "spx" || suffix == "wma" || suffix == "mp4" || suffix == "spx" ||
suffix == "wav" || suffix == "opus" || suffix == "m4b") { suffix == "wav" || suffix == "opus" || suffix == "m4b" ||
suffix == "wv") {
d->valid_ = true; d->valid_ = true;
} else { } else {
d->valid_ = false; d->valid_ = false;

View File

@ -101,6 +101,7 @@ class Song {
Type_TrueAudio = 11, Type_TrueAudio = 11,
Type_Cdda = 12, Type_Cdda = 12,
Type_OggOpus = 13, Type_OggOpus = 13,
Type_WavPack = 14,
Type_Stream = 99, Type_Stream = 99,
}; };
static QString TextForFiletype(FileType type); static QString TextForFiletype(FileType type);

View File

@ -60,6 +60,7 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply* reply, int id) {
result.description = result.description =
element["artist"].text() + " - " + element["name"].text(); element["artist"].text() + " - " + element["name"].text();
result.image_url = QUrl(element["image size=extralarge"].text()); result.image_url = QUrl(element["image size=extralarge"].text());
if (result.image_url.isEmpty()) continue;
results << result; results << result;
} }
} else { } else {

View File

@ -39,7 +39,7 @@ const char* DigitallyImportedClient::kAuthUrl =
"http://api.audioaddict.com/v1/%1/members/authenticate"; "http://api.audioaddict.com/v1/%1/members/authenticate";
const char* DigitallyImportedClient::kChannelListUrl = const char* DigitallyImportedClient::kChannelListUrl =
"http://api.v2.audioaddict.com/v1/%1/mobile/" "http://api.audioaddict.com/v1/%1/mobile/"
"batch_update?asset_group_key=mobile_icons&stream_set_key="; "batch_update?asset_group_key=mobile_icons&stream_set_key=";
DigitallyImportedClient::DigitallyImportedClient(const QString& service_name, DigitallyImportedClient::DigitallyImportedClient(const QString& service_name,

View File

@ -221,7 +221,7 @@ bool SpotifyBlobDownloader::CheckSignature(
return false; return false;
} }
} }
} catch (std::exception e) { } catch (std::exception& e) {
// This should only happen if we fail to parse our own key. // This should only happen if we fail to parse our own key.
qLog(Debug) << "Verifying spotify blob signature failed:" << e.what(); qLog(Debug) << "Verifying spotify blob signature failed:" << e.what();
return false; return false;

View File

@ -964,7 +964,7 @@ void LibraryBackend::UpdateManualAlbumArt(const QString& artist,
query.SetColumnSpec("ROWID, " + Song::kColumnSpec); query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
query.AddWhere("album", album); query.AddWhere("album", album);
if (!albumartist.isNull()) { if (!albumartist.isNull() && !albumartist.isEmpty()) {
query.AddWhere("albumartist", albumartist); query.AddWhere("albumartist", albumartist);
} else if (!artist.isNull()) { } else if (!artist.isNull()) {
query.AddWhere("artist", artist); query.AddWhere("artist", artist);
@ -984,7 +984,7 @@ void LibraryBackend::UpdateManualAlbumArt(const QString& artist,
QString( QString(
"UPDATE %1 SET art_manual = :art" "UPDATE %1 SET art_manual = :art"
" WHERE album = :album AND unavailable = 0").arg(songs_table_)); " WHERE album = :album AND unavailable = 0").arg(songs_table_));
if (!albumartist.isNull()) { if (!albumartist.isNull() && !albumartist.isEmpty()) {
sql += " AND albumartist = :albumartist"; sql += " AND albumartist = :albumartist";
} else if (!artist.isNull()) { } else if (!artist.isNull()) {
sql += " AND artist = :artist"; sql += " AND artist = :artist";
@ -994,7 +994,7 @@ void LibraryBackend::UpdateManualAlbumArt(const QString& artist,
q.prepare(sql); q.prepare(sql);
q.bindValue(":art", art); q.bindValue(":art", art);
q.bindValue(":album", album); q.bindValue(":album", album);
if (!albumartist.isNull()) { if (!albumartist.isNull() && !albumartist.isEmpty()) {
q.bindValue(":albumartist", albumartist); q.bindValue(":albumartist", albumartist);
} else if (!artist.isNull()) { } else if (!artist.isNull()) {
q.bindValue(":artist", artist); q.bindValue(":artist", artist);

View File

@ -392,6 +392,9 @@ void LibraryView::contextMenuEvent(QContextMenuEvent* e) {
add_to_playlist_enqueue_ = context_menu_->addAction( add_to_playlist_enqueue_ = context_menu_->addAction(
IconLoader::Load("go-next", IconLoader::Base), tr("Queue track"), this, IconLoader::Load("go-next", IconLoader::Base), tr("Queue track"), this,
SLOT(AddToPlaylistEnqueue())); 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(); context_menu_->addSeparator();
search_for_this_ = context_menu_->addAction( search_for_this_ = context_menu_->addAction(
IconLoader::Load("system-search", IconLoader::Base), IconLoader::Load("system-search", IconLoader::Base),
@ -616,6 +619,14 @@ void LibraryView::AddToPlaylistEnqueue() {
emit AddToPlaylistSignal(data); emit AddToPlaylistSignal(data);
} }
void LibraryView::AddToPlaylistEnqueueNext() {
QMimeData* data = model()->mimeData(selectedIndexes());
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
mime_data->enqueue_next_now_ = true;
}
emit AddToPlaylistSignal(data);
}
void LibraryView::OpenInNewPlaylist() { void LibraryView::OpenInNewPlaylist() {
QMimeData* data = model()->mimeData(selectedIndexes()); QMimeData* data = model()->mimeData(selectedIndexes());
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) { if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {

View File

@ -92,6 +92,7 @@ signals:
void Load(); void Load();
void AddToPlaylist(); void AddToPlaylist();
void AddToPlaylistEnqueue(); void AddToPlaylistEnqueue();
void AddToPlaylistEnqueueNext();
void OpenInNewPlaylist(); void OpenInNewPlaylist();
void Organise(); void Organise();
void CopyToDevice(); void CopyToDevice();
@ -131,6 +132,7 @@ signals:
QAction* load_; QAction* load_;
QAction* add_to_playlist_; QAction* add_to_playlist_;
QAction* add_to_playlist_enqueue_; QAction* add_to_playlist_enqueue_;
QAction* add_to_playlist_enqueue_next_;
QAction* open_in_new_playlist_; QAction* open_in_new_playlist_;
QAction* organise_; QAction* organise_;
QAction* copy_to_device_; QAction* copy_to_device_;

View File

@ -151,14 +151,14 @@ Playlist::~Playlist() {
template <typename T> template <typename T>
void Playlist::InsertSongItems(const SongList& songs, int pos, bool play_now, void Playlist::InsertSongItems(const SongList& songs, int pos, bool play_now,
bool enqueue) { bool enqueue, bool enqueue_next) {
PlaylistItemList items; PlaylistItemList items;
for (const Song& song : songs) { for (const Song& song : songs) {
items << PlaylistItemPtr(new T(song)); 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 { QVariant Playlist::headerData(int section, Qt::Orientation, int role) const {
@ -702,7 +702,7 @@ void Playlist::InsertDynamicItems(int count) {
connect(inserter, SIGNAL(PlayRequested(QModelIndex)), connect(inserter, SIGNAL(PlayRequested(QModelIndex)),
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 { Qt::ItemFlags Playlist::flags(const QModelIndex& index) const {
@ -732,12 +732,14 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action,
bool play_now = false; bool play_now = false;
bool enqueue_now = false; bool enqueue_now = false;
bool enqueue_next_now = false;
if (const MimeData* mime_data = qobject_cast<const MimeData*>(data)) { if (const MimeData* mime_data = qobject_cast<const MimeData*>(data)) {
if (mime_data->clear_first_) { if (mime_data->clear_first_) {
Clear(); Clear();
} }
play_now = mime_data->play_now_; play_now = mime_data->play_now_;
enqueue_now = mime_data->enqueue_now_; enqueue_now = mime_data->enqueue_now_;
enqueue_next_now = mime_data->enqueue_next_now_;
} }
if (const SongMimeData* song_data = qobject_cast<const SongMimeData*>(data)) { if (const SongMimeData* song_data = qobject_cast<const SongMimeData*>(data)) {
@ -747,33 +749,35 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action,
if (song_data->backend && if (song_data->backend &&
song_data->backend->songs_table() == Library::kSongsTable) song_data->backend->songs_table() == Library::kSongsTable)
InsertSongItems<LibraryPlaylistItem>(song_data->songs, row, play_now, InsertSongItems<LibraryPlaylistItem>(song_data->songs, row, play_now,
enqueue_now); enqueue_now, enqueue_next_now);
else if (song_data->backend && else if (song_data->backend &&
song_data->backend->songs_table() == MagnatuneService::kSongsTable) song_data->backend->songs_table() == MagnatuneService::kSongsTable)
InsertSongItems<MagnatunePlaylistItem>(song_data->songs, row, play_now, InsertSongItems<MagnatunePlaylistItem>(song_data->songs, row, play_now,
enqueue_now); enqueue_now, enqueue_next_now);
else if (song_data->backend && else if (song_data->backend &&
song_data->backend->songs_table() == JamendoService::kSongsTable) song_data->backend->songs_table() == JamendoService::kSongsTable)
InsertSongItems<JamendoPlaylistItem>(song_data->songs, row, play_now, InsertSongItems<JamendoPlaylistItem>(song_data->songs, row, play_now,
enqueue_now); enqueue_now, enqueue_next_now);
else else
InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now, InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now,
enqueue_now); enqueue_now, enqueue_next_now);
} else if (const InternetMimeData* internet_data = } else if (const InternetMimeData* internet_data =
qobject_cast<const InternetMimeData*>(data)) { qobject_cast<const InternetMimeData*>(data)) {
// Dragged from the Internet pane // Dragged from the Internet pane
InsertInternetItems(internet_data->model, internet_data->indexes, row, 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 = } else if (const InternetSongMimeData* internet_song_data =
qobject_cast<const InternetSongMimeData*>(data)) { qobject_cast<const InternetSongMimeData*>(data)) {
InsertInternetItems(internet_song_data->service, internet_song_data->songs, 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 = } else if (const GeneratorMimeData* generator_data =
qobject_cast<const GeneratorMimeData*>(data)) { qobject_cast<const GeneratorMimeData*>(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 = } else if (const PlaylistItemMimeData* item_data =
qobject_cast<const PlaylistItemMimeData*>(data)) { qobject_cast<const PlaylistItemMimeData*>(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)) { } else if (data->hasFormat(kRowsMimetype)) {
// Dragged from the playlist // Dragged from the playlist
// Rearranging it is tricky... // Rearranging it is tricky...
@ -808,7 +812,7 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action,
if (items.count() > kUndoItemLimit) { if (items.count() > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it // Too big to keep in the undo stack. Also clear the stack because it
// might have been invalidated. // might have been invalidated.
InsertItemsWithoutUndo(items, row, false); InsertItemsWithoutUndo(items, row, false, false);
undo_stack_->clear(); undo_stack_->clear();
} else { } else {
undo_stack_->push( undo_stack_->push(
@ -827,26 +831,27 @@ bool Playlist::dropMimeData(const QMimeData* data, Qt::DropAction action,
SongLoaderInserter* inserter = new SongLoaderInserter( SongLoaderInserter* inserter = new SongLoaderInserter(
task_manager_, library_, backend_->app()->player()); task_manager_, library_, backend_->app()->player());
connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString))); 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()) { } else if (data->hasUrls()) {
// URL list dragged from the file list or some other app // 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; return true;
} }
void Playlist::InsertUrls(const QList<QUrl>& urls, int pos, bool play_now, void Playlist::InsertUrls(const QList<QUrl>& urls, int pos, bool play_now,
bool enqueue) { bool enqueue, bool enqueue_next) {
SongLoaderInserter* inserter = new SongLoaderInserter( SongLoaderInserter* inserter = new SongLoaderInserter(
task_manager_, library_, backend_->app()->player()); task_manager_, library_, backend_->app()->player());
connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString))); 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, 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 // Hack: If the generator hasn't got a library set then use the main one
if (!generator->library()) { if (!generator->library()) {
generator->set_library(library_); generator->set_library(library_);
@ -856,7 +861,7 @@ void Playlist::InsertSmartPlaylist(GeneratorPtr generator, int pos,
new GeneratorInserter(task_manager_, library_, this); new GeneratorInserter(task_manager_, library_, this);
connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString))); 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()) { if (generator->is_dynamic()) {
TurnOnDynamicPlaylist(generator); TurnOnDynamicPlaylist(generator);
@ -975,7 +980,7 @@ void Playlist::MoveItemsWithoutUndo(int start, const QList<int>& dest_rows) {
} }
void Playlist::InsertItems(const PlaylistItemList& itemsIn, int pos, 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; if (itemsIn.isEmpty()) return;
PlaylistItemList items = itemsIn; PlaylistItemList items = itemsIn;
@ -1025,18 +1030,18 @@ void Playlist::InsertItems(const PlaylistItemList& itemsIn, int pos,
if (items.count() > kUndoItemLimit) { if (items.count() > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it // Too big to keep in the undo stack. Also clear the stack because it
// might have been invalidated. // might have been invalidated.
InsertItemsWithoutUndo(items, pos, enqueue); InsertItemsWithoutUndo(items, pos, enqueue, enqueue_next);
undo_stack_->clear(); undo_stack_->clear();
} else { } else {
undo_stack_->push( undo_stack_->push(new PlaylistUndoCommands::InsertItems(
new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue)); this, items, pos, enqueue, enqueue_next));
} }
if (play_now) emit PlayRequested(index(start, 0)); if (play_now) emit PlayRequested(index(start, 0));
} }
void Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items, int pos, void Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items, int pos,
bool enqueue) { bool enqueue, bool enqueue_next) {
if (items.isEmpty()) return; if (items.isEmpty()) return;
const int start = pos == -1 ? items_.count() : pos; const int start = pos == -1 ? items_.count() : pos;
@ -1071,22 +1076,33 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemList& items, int pos,
queue_->ToggleTracks(indexes); queue_->ToggleTracks(indexes);
} }
if (enqueue_next) {
QModelIndexList indexes;
for (int i = start; i <= end; ++i) {
indexes << index(i, 0);
}
queue_->InsertFirst(indexes);
}
Save(); Save();
ReshuffleIndices(); ReshuffleIndices();
} }
void Playlist::InsertLibraryItems(const SongList& songs, int pos, bool play_now, void Playlist::InsertLibraryItems(const SongList& songs, int pos, bool play_now,
bool enqueue) { bool enqueue, bool enqueue_next) {
InsertSongItems<LibraryPlaylistItem>(songs, pos, play_now, enqueue); InsertSongItems<LibraryPlaylistItem>(songs, pos, play_now, enqueue,
enqueue_next);
} }
void Playlist::InsertSongs(const SongList& songs, int pos, bool play_now, void Playlist::InsertSongs(const SongList& songs, int pos, bool play_now,
bool enqueue) { bool enqueue, bool enqueue_next) {
InsertSongItems<SongPlaylistItem>(songs, pos, play_now, enqueue); InsertSongItems<SongPlaylistItem>(songs, pos, play_now, enqueue,
enqueue_next);
} }
void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos, void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos,
bool play_now, bool enqueue) { bool play_now, bool enqueue,
bool enqueue_next) {
PlaylistItemList items; PlaylistItemList items;
for (const Song& song : songs) { for (const Song& song : songs) {
if (song.is_library_song()) { if (song.is_library_song()) {
@ -1095,12 +1111,13 @@ void Playlist::InsertSongsOrLibraryItems(const SongList& songs, int pos,
items << PlaylistItemPtr(new SongPlaylistItem(song)); 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, void Playlist::InsertInternetItems(const InternetModel* model,
const QModelIndexList& items, int pos, const QModelIndexList& items, int pos,
bool play_now, bool enqueue) { bool play_now, bool enqueue,
bool enqueue_next) {
PlaylistItemList playlist_items; PlaylistItemList playlist_items;
QList<QUrl> song_urls; QList<QUrl> song_urls;
@ -1119,23 +1136,24 @@ void Playlist::InsertInternetItems(const InternetModel* model,
} }
if (!song_urls.isEmpty()) { if (!song_urls.isEmpty()) {
InsertUrls(song_urls, pos, play_now, enqueue); InsertUrls(song_urls, pos, play_now, enqueue, enqueue_next);
play_now = false; play_now = false;
} }
InsertItems(playlist_items, pos, play_now, enqueue); InsertItems(playlist_items, pos, play_now, enqueue, enqueue_next);
} }
void Playlist::InsertInternetItems(InternetService* service, void Playlist::InsertInternetItems(InternetService* service,
const SongList& songs, int pos, const SongList& songs, int pos,
bool play_now, bool enqueue) { bool play_now, bool enqueue,
bool enqueue_next) {
PlaylistItemList playlist_items; PlaylistItemList playlist_items;
for (const Song& song : songs) { for (const Song& song : songs) {
playlist_items << shared_ptr<PlaylistItem>( playlist_items << shared_ptr<PlaylistItem>(
new InternetPlaylistItem(service, song)); 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) { void Playlist::UpdateItems(const SongList& songs) {

View File

@ -235,18 +235,22 @@ class Playlist : public QAbstractListModel {
// Changing the playlist // Changing the playlist
void InsertItems(const PlaylistItemList& items, int pos = -1, 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, 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, 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, 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, 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, void InsertInternetItems(InternetService* service, const SongList& songs,
int pos = -1, bool play_now = false, int pos = -1, bool play_now = false,
bool enqueue = false); bool enqueue = false, bool enqueue_next = false);
void ReshuffleIndices(); void ReshuffleIndices();
// If this playlist contains the current item, this method will apply the // If this playlist contains the current item, this method will apply the
@ -335,7 +339,7 @@ class Playlist : public QAbstractListModel {
void SetColumnAlignment(const ColumnAlignmentMap& alignment); void SetColumnAlignment(const ColumnAlignmentMap& alignment);
void InsertUrls(const QList<QUrl>& urls, int pos = -1, bool play_now = false, void InsertUrls(const QList<QUrl>& 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 // Removes items with given indices from the playlist. This operation is not
// undoable. // undoable.
void RemoveItemsWithoutUndo(const QList<int>& indices); void RemoveItemsWithoutUndo(const QList<int>& indices);
@ -366,18 +370,18 @@ signals:
void InsertInternetItems(const InternetModel* model, void InsertInternetItems(const InternetModel* model,
const QModelIndexList& items, int pos, bool play_now, const QModelIndexList& items, int pos, bool play_now,
bool enqueue); bool enqueue, bool enqueue_next = false);
template <typename T> template <typename T>
void InsertSongItems(const SongList& songs, int pos, bool play_now, void InsertSongItems(const SongList& songs, int pos, bool play_now,
bool enqueue); bool enqueue, bool enqueue_next = false);
void InsertDynamicItems(int count); void InsertDynamicItems(int count);
// Modify the playlist without changing the undo stack. These are used by // Modify the playlist without changing the undo stack. These are used by
// our friends in PlaylistUndoCommands // our friends in PlaylistUndoCommands
void InsertItemsWithoutUndo(const PlaylistItemList& items, int pos, void InsertItemsWithoutUndo(const PlaylistItemList& items, int pos,
bool enqueue = false); bool enqueue = false, bool enqueue_next = false);
PlaylistItemList RemoveItemsWithoutUndo(int pos, int count); PlaylistItemList RemoveItemsWithoutUndo(int pos, int count);
void MoveItemsWithoutUndo(const QList<int>& source_rows, int pos); void MoveItemsWithoutUndo(const QList<int>& source_rows, int pos);
void MoveItemWithoutUndo(int source, int dest); void MoveItemWithoutUndo(int source, int dest);

View File

@ -23,13 +23,17 @@ namespace PlaylistUndoCommands {
Base::Base(Playlist* playlist) : QUndoCommand(0), playlist_(playlist) {} Base::Base(Playlist* playlist) : QUndoCommand(0), playlist_(playlist) {}
InsertItems::InsertItems(Playlist* playlist, const PlaylistItemList& items, InsertItems::InsertItems(Playlist* playlist, const PlaylistItemList& items,
int pos, bool enqueue) int pos, bool enqueue, bool enqueue_next)
: Base(playlist), items_(items), pos_(pos), enqueue_(enqueue) { : Base(playlist),
items_(items),
pos_(pos),
enqueue_(enqueue),
enqueue_next_(enqueue_next) {
setText(tr("add %n songs", "", items_.count())); setText(tr("add %n songs", "", items_.count()));
} }
void InsertItems::redo() { void InsertItems::redo() {
playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_); playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_, enqueue_next_);
} }
void InsertItems::undo() { void InsertItems::undo() {

View File

@ -41,7 +41,7 @@ class Base : public QUndoCommand {
class InsertItems : public Base { class InsertItems : public Base {
public: public:
InsertItems(Playlist* playlist, const PlaylistItemList& items, int pos, InsertItems(Playlist* playlist, const PlaylistItemList& items, int pos,
bool enqueue = false); bool enqueue = false, bool enqueue_next = false);
void undo(); void undo();
void redo(); void redo();
@ -56,6 +56,7 @@ class InsertItems : public Base {
PlaylistItemList items_; PlaylistItemList items_;
int pos_; int pos_;
bool enqueue_; bool enqueue_;
bool enqueue_next_;
}; };
class RemoveItems : public Base { class RemoveItems : public Base {

View File

@ -113,6 +113,7 @@ PlaylistView::PlaylistView(QWidget* parent)
upgrading_from_qheaderview_(false), upgrading_from_qheaderview_(false),
read_only_settings_(true), read_only_settings_(true),
upgrading_from_version_(-1), upgrading_from_version_(-1),
background_initialized_(false),
background_image_type_(Default), background_image_type_(Default),
blur_radius_(kDefaultBlurRadius), blur_radius_(kDefaultBlurRadius),
opacity_level_(kDefaultOpacityLevel), opacity_level_(kDefaultOpacityLevel),
@ -1128,10 +1129,12 @@ void PlaylistView::ReloadSettings() {
// set_background_image when it is not needed, as this will cause the fading // set_background_image when it is not needed, as this will cause the fading
// animation to start again. This also avoid to do useless // animation to start again. This also avoid to do useless
// "force_background_redraw". // "force_background_redraw".
if (background_image_filename != background_image_filename_ || if (background_initialized_ == false ||
background_image_filename != background_image_filename_ ||
background_type != background_image_type_ || background_type != background_image_type_ ||
blur_radius_ != blur_radius || opacity_level_ != opacity_level) { blur_radius_ != blur_radius || opacity_level_ != opacity_level) {
// Store background properties // Store background properties
background_initialized_ = true;
background_image_type_ = background_type; background_image_type_ = background_type;
background_image_filename_ = background_image_filename; background_image_filename_ = background_image_filename;
blur_radius_ = blur_radius; blur_radius_ = blur_radius;

View File

@ -195,15 +195,17 @@ signals:
bool read_only_settings_; bool read_only_settings_;
int upgrading_from_version_; int upgrading_from_version_;
bool background_initialized_;
BackgroundImageType background_image_type_; BackgroundImageType background_image_type_;
// Used if background image is a filemane
QString background_image_filename_;
// Stores the background image to be displayed. As we want this image to be // Stores the background image to be displayed. As we want this image to be
// particular (in terms of format, opacity), you should probably use // particular (in terms of format, opacity), you should probably use
// set_background_image_type instead of modifying background_image_ directly // set_background_image_type instead of modifying background_image_ directly
QImage background_image_; QImage background_image_;
int blur_radius_; int blur_radius_;
int opacity_level_; int opacity_level_;
// Used if background image is a filemane
QString background_image_filename_;
QImage current_song_cover_art_; QImage current_song_cover_art_;
QPixmap cached_scaled_background_image_; QPixmap cached_scaled_background_image_;

View File

@ -151,6 +151,29 @@ void Queue::ToggleTracks(const QModelIndexList& source_indexes) {
} }
} }
void Queue::InsertFirst(const QModelIndexList& source_indexes) {
for (const QModelIndex& source_index : source_indexes) {
QModelIndex proxy_index = mapFromSource(source_index);
if (proxy_index.isValid()) {
// Already in the queue, so remove it to be reinserted later
const int row = proxy_index.row();
beginRemoveRows(QModelIndex(), row, row);
source_indexes_.removeAt(row);
endRemoveRows();
}
}
const int rows = source_indexes.count();
// Enqueue the tracks at the beginning
beginInsertRows(QModelIndex(), 0, rows - 1);
int offset = 0;
for (const QModelIndex& source_index : source_indexes) {
source_indexes_.insert(offset, QPersistentModelIndex(source_index));
offset++;
}
endInsertRows();
}
int Queue::PositionOf(const QModelIndex& source_index) const { int Queue::PositionOf(const QModelIndex& source_index) const {
return mapFromSource(source_index).row(); return mapFromSource(source_index).row();
} }

View File

@ -39,6 +39,7 @@ class Queue : public QAbstractProxyModel {
// Modify the queue // Modify the queue
int TakeNext(); int TakeNext();
void ToggleTracks(const QModelIndexList& source_indexes); void ToggleTracks(const QModelIndexList& source_indexes);
void InsertFirst(const QModelIndexList& source_indexes);
void Clear(); void Clear();
void Move(const QList<int>& proxy_rows, int pos); void Move(const QList<int>& proxy_rows, int pos);
void MoveUp(int row); void MoveUp(int row);

View File

@ -37,11 +37,13 @@ SongLoaderInserter::SongLoaderInserter(TaskManager* task_manager,
SongLoaderInserter::~SongLoaderInserter() { qDeleteAll(pending_); } SongLoaderInserter::~SongLoaderInserter() { qDeleteAll(pending_); }
void SongLoaderInserter::Load(Playlist* destination, int row, bool play_now, void SongLoaderInserter::Load(Playlist* destination, int row, bool play_now,
bool enqueue, const QList<QUrl>& urls) { bool enqueue, bool enqueue_next,
const QList<QUrl>& urls) {
destination_ = destination; destination_ = destination;
row_ = row; row_ = row;
play_now_ = play_now; play_now_ = play_now;
enqueue_ = enqueue; enqueue_ = enqueue;
enqueue_next_ = enqueue_next;
connect(destination, SIGNAL(destroyed()), SLOT(DestinationDestroyed())); connect(destination, SIGNAL(destroyed()), SLOT(DestinationDestroyed()));
connect(this, SIGNAL(PreloadFinished()), SLOT(InsertSongs())); connect(this, SIGNAL(PreloadFinished()), SLOT(InsertSongs()));
@ -78,11 +80,13 @@ void SongLoaderInserter::Load(Playlist* destination, int row, bool play_now,
// In the meantime, MusicBrainz will be queried to get songs' metadata. // In the meantime, MusicBrainz will be queried to get songs' metadata.
// AudioCDTagsLoaded will be called next, and playlist's items will be updated. // AudioCDTagsLoaded will be called next, and playlist's items will be updated.
void SongLoaderInserter::LoadAudioCD(Playlist* destination, int row, void SongLoaderInserter::LoadAudioCD(Playlist* destination, int row,
bool play_now, bool enqueue) { bool play_now, bool enqueue,
bool enqueue_next) {
destination_ = destination; destination_ = destination;
row_ = row; row_ = row;
play_now_ = play_now; play_now_ = play_now;
enqueue_ = enqueue; enqueue_ = enqueue;
enqueue_next_ = enqueue_next;
SongLoader* loader = new SongLoader(library_, player_, this); SongLoader* loader = new SongLoader(library_, player_, this);
NewClosure(loader, SIGNAL(AudioCDTracksLoaded()), NewClosure(loader, SIGNAL(AudioCDTracksLoaded()),
@ -119,7 +123,8 @@ void SongLoaderInserter::InsertSongs() {
// Insert songs (that haven't been completely loaded) to allow user to see // Insert songs (that haven't been completely loaded) to allow user to see
// and play them while not loaded completely // and play them while not loaded completely
if (destination_) { if (destination_) {
destination_->InsertSongsOrLibraryItems(songs_, row_, play_now_, enqueue_); destination_->InsertSongsOrLibraryItems(songs_, row_, play_now_, enqueue_,
enqueue_next_);
} }
} }

View File

@ -40,10 +40,11 @@ class SongLoaderInserter : public QObject {
~SongLoaderInserter(); ~SongLoaderInserter();
void Load(Playlist* destination, int row, bool play_now, bool enqueue, void Load(Playlist* destination, int row, bool play_now, bool enqueue,
const QList<QUrl>& urls); bool enqueue_next, const QList<QUrl>& 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: signals:
void Error(const QString& message); void Error(const QString& message);
void PreloadFinished(); void PreloadFinished();
void EffectiveLoadFinished(const SongList& songs); void EffectiveLoadFinished(const SongList& songs);
@ -64,6 +65,7 @@ signals:
int row_; int row_;
bool play_now_; bool play_now_;
bool enqueue_; bool enqueue_;
bool enqueue_next_;
SongList songs_; SongList songs_;

View File

@ -43,14 +43,15 @@ static PlaylistItemList Generate(GeneratorPtr generator, int dynamic_count) {
} }
void GeneratorInserter::Load(Playlist* destination, int row, bool play_now, void GeneratorInserter::Load(Playlist* destination, int row, bool play_now,
bool enqueue, GeneratorPtr generator, bool enqueue, bool enqueue_next,
int dynamic_count) { GeneratorPtr generator, int dynamic_count) {
task_id_ = task_manager_->StartTask(tr("Loading smart playlist")); task_id_ = task_manager_->StartTask(tr("Loading smart playlist"));
destination_ = destination; destination_ = destination;
row_ = row; row_ = row;
play_now_ = play_now; play_now_ = play_now;
enqueue_ = enqueue; enqueue_ = enqueue;
enqueue_next_ = enqueue_next;
is_dynamic_ = generator->is_dynamic(); is_dynamic_ = generator->is_dynamic();
connect(generator.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString))); connect(generator.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));

View File

@ -41,9 +41,9 @@ class GeneratorInserter : public QObject {
QObject* parent); QObject* parent);
void Load(Playlist* destination, int row, bool play_now, bool enqueue, void Load(Playlist* destination, int row, bool play_now, bool enqueue,
GeneratorPtr generator, int dynamic_count = 0); bool enqueue_next, GeneratorPtr generator, int dynamic_count = 0);
signals: signals:
void Error(const QString& message); void Error(const QString& message);
void PlayRequested(const QModelIndex& index); void PlayRequested(const QModelIndex& index);
@ -59,6 +59,7 @@ signals:
int row_; int row_;
bool play_now_; bool play_now_;
bool enqueue_; bool enqueue_;
bool enqueue_next_;
bool is_dynamic_; bool is_dynamic_;
}; };

View File

@ -752,7 +752,7 @@ void AlbumCoverManager::LoadSelectedToPlaylist() {
void AlbumCoverManager::SaveAndSetCover(QListWidgetItem* item, void AlbumCoverManager::SaveAndSetCover(QListWidgetItem* item,
const QImage& image) { const QImage& image) {
const QString artist = item->data(Role_ArtistName).toString(); const QString artist = item->data(Role_ArtistName).toString();
const QString albumartist = item->data(Role_ArtistName).toString(); const QString albumartist = item->data(Role_AlbumArtistName).toString();
const QString album = item->data(Role_AlbumName).toString(); const QString album = item->data(Role_AlbumName).toString();
QString path = QString path =

View File

@ -677,6 +677,10 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
playlist_queue_ = playlist_menu_->addAction("", this, SLOT(PlaylistQueue())); playlist_queue_ = playlist_menu_->addAction("", this, SLOT(PlaylistQueue()));
playlist_queue_->setShortcut(QKeySequence("Ctrl+D")); playlist_queue_->setShortcut(QKeySequence("Ctrl+D"));
ui_->playlist->addAction(playlist_queue_); ui_->playlist->addAction(playlist_queue_);
playlist_queue_play_next_ =
playlist_menu_->addAction("", this, SLOT(PlaylistQueuePlayNext()));
playlist_queue_play_next_->setShortcut(QKeySequence("Ctrl+Shift+D"));
ui_->playlist->addAction(playlist_queue_play_next_);
playlist_skip_ = playlist_menu_->addAction("", this, SLOT(PlaylistSkip())); playlist_skip_ = playlist_menu_->addAction("", this, SLOT(PlaylistSkip()));
ui_->playlist->addAction(playlist_skip_); ui_->playlist->addAction(playlist_skip_);
playlist_menu_->addSeparator(); playlist_menu_->addSeparator();
@ -1411,7 +1415,7 @@ void MainWindow::closeEvent(QCloseEvent* event) {
event->accept(); event->accept();
SetHiddenInTray(true); SetHiddenInTray(true);
} else { } else {
QApplication::quit(); Exit();
} }
} }
@ -1759,6 +1763,11 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos,
else else
playlist_queue_->setText(tr("Toggle queue status")); playlist_queue_->setText(tr("Toggle queue status"));
if (all > 1)
playlist_queue_play_next_->setText(tr("Play selected tracks next"));
else
playlist_queue_play_next_->setText(tr("Play next"));
if (in_skipped == 1 && not_in_skipped == 0) if (in_skipped == 1 && not_in_skipped == 0)
playlist_skip_->setText(tr("Unskip track")); playlist_skip_->setText(tr("Unskip track"));
else if (in_skipped > 1 && not_in_skipped == 0) else if (in_skipped > 1 && not_in_skipped == 0)
@ -1773,6 +1782,9 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos,
else else
playlist_queue_->setIcon(IconLoader::Load("go-next", IconLoader::Base)); playlist_queue_->setIcon(IconLoader::Load("go-next", IconLoader::Base));
playlist_queue_play_next_->setIcon(
IconLoader::Load("go-next", IconLoader::Base));
if (!index.isValid()) { if (!index.isValid()) {
ui_->action_selection_set_value->setVisible(false); ui_->action_selection_set_value->setVisible(false);
ui_->action_edit_value->setVisible(false); ui_->action_edit_value->setVisible(false);
@ -2493,6 +2505,17 @@ void MainWindow::PlaylistQueue() {
app_->playlist_manager()->current()->queue()->ToggleTracks(indexes); app_->playlist_manager()->current()->queue()->ToggleTracks(indexes);
} }
void MainWindow::PlaylistQueuePlayNext() {
QModelIndexList indexes;
for (const QModelIndex& proxy_index :
ui_->playlist->view()->selectionModel()->selectedRows()) {
indexes << app_->playlist_manager()->current()->proxy()->mapToSource(
proxy_index);
}
app_->playlist_manager()->current()->queue()->InsertFirst(indexes);
}
void MainWindow::PlaylistSkip() { void MainWindow::PlaylistSkip() {
QModelIndexList indexes; QModelIndexList indexes;
for (const QModelIndex& proxy_index : for (const QModelIndex& proxy_index :

View File

@ -162,6 +162,7 @@ signals:
void PlaylistPlay(); void PlaylistPlay();
void PlaylistStopAfter(); void PlaylistStopAfter();
void PlaylistQueue(); void PlaylistQueue();
void PlaylistQueuePlayNext();
void PlaylistSkip(); void PlaylistSkip();
void PlaylistRemoveCurrent(); void PlaylistRemoveCurrent();
void PlaylistEditFinished(const QModelIndex& index); void PlaylistEditFinished(const QModelIndex& index);
@ -361,6 +362,7 @@ signals:
QAction* playlist_delete_; QAction* playlist_delete_;
QAction* playlist_open_in_browser_; QAction* playlist_open_in_browser_;
QAction* playlist_queue_; QAction* playlist_queue_;
QAction* playlist_queue_play_next_;
QAction* playlist_skip_; QAction* playlist_skip_;
QAction* playlist_add_to_another_; QAction* playlist_add_to_another_;
QList<QAction*> playlistitem_actions_; QList<QAction*> playlistitem_actions_;

View File

@ -105,7 +105,11 @@ void TrackSliderSlider::enterEvent(QEvent* e) {
void TrackSliderSlider::leaveEvent(QEvent* e) { void TrackSliderSlider::leaveEvent(QEvent* e) {
QSlider::leaveEvent(e); QSlider::leaveEvent(e);
popup_->hide(); // On some (but not all) systems, displaying the TrackSliderPopup
// generates a leaveEvent. Ensure that this leaveEvent is genuine.
if (!geometry().contains(mapFromGlobal(QCursor::pos()))) {
popup_->hide();
}
} }
void TrackSliderSlider::keyPressEvent(QKeyEvent* event) { void TrackSliderSlider::keyPressEvent(QKeyEvent* event) {