diff --git a/Changelog b/Changelog
index 8b4456523..c3909f475 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,17 @@ Strawberry Music Player
=======================
ChangeLog
+Unreleased:
+
+ * Added new lyrics provider with lyrics from AudD and API Seeds
+ * New improved context widget with albums and lyrics
+ * Fixed playing and context widget getting stuck in play mode when there was an error
+ * Changed icons for artists in collection, tidal and cover manager
+ * Removed "search" icon from "Search automatically" checkbox (right click) that looked ugly
+ * Removed some unused widgets from the src/widgets directory
+ * Fixed initial size of window and side panel
+ * Fixed saving window size correctly
+
Version 0.2.1:
* Fixed crash with newer Qt
diff --git a/data/data.qrc b/data/data.qrc
index e5780faa4..2a80823ad 100644
--- a/data/data.qrc
+++ b/data/data.qrc
@@ -3,8 +3,7 @@
schema/schema.sql
schema/schema-1.sql
schema/device-schema.sql
- style/mainwindow.css
- style/statusview.css
+ style/strawberry.css
misc/playing_tooltip.txt
pictures/strawberry.png
pictures/strawbs-transparent.png
@@ -98,8 +97,6 @@
icons/128x128/speaker.png
icons/128x128/star-grey.png
icons/128x128/star.png
- icons/128x128/strawberry-panel-grey.png
- icons/128x128/strawberry-panel.png
icons/128x128/strawberry.png
icons/128x128/strawberry.svg
icons/128x128/tools-wizard.png
@@ -188,8 +185,6 @@
icons/64x64/speaker.png
icons/64x64/star-grey.png
icons/64x64/star.png
- icons/64x64/strawberry-panel-grey.png
- icons/64x64/strawberry-panel.png
icons/64x64/strawberry.png
icons/64x64/tools-wizard.png
icons/64x64/view-choose.png
@@ -280,8 +275,6 @@
icons/48x48/speaker.png
icons/48x48/star-grey.png
icons/48x48/star.png
- icons/48x48/strawberry-panel-grey.png
- icons/48x48/strawberry-panel.png
icons/48x48/strawberry.png
icons/48x48/tools-wizard.png
icons/48x48/view-choose.png
@@ -372,8 +365,6 @@
icons/32x32/speaker.png
icons/32x32/star-grey.png
icons/32x32/star.png
- icons/32x32/strawberry-panel-grey.png
- icons/32x32/strawberry-panel.png
icons/32x32/strawberry.png
icons/32x32/strawberry.svg
icons/32x32/tools-wizard.png
@@ -465,8 +456,6 @@
icons/22x22/speaker.png
icons/22x22/star-grey.png
icons/22x22/star.png
- icons/22x22/strawberry-panel-grey.png
- icons/22x22/strawberry-panel.png
icons/22x22/strawberry.png
icons/22x22/strawberry.svg
icons/22x22/tools-wizard.png
@@ -481,6 +470,7 @@
icons/22x22/xine.png
icons/22x22/zoom-in.png
icons/22x22/zoom-out.png
- icons/22x22/tidal.png
+ icons/22x22/tidal.png
+ fonts/HumongousofEternitySt.ttf
diff --git a/data/fonts/HumongousofEternitySt.ttf b/data/fonts/HumongousofEternitySt.ttf
new file mode 100644
index 000000000..73e818b52
Binary files /dev/null and b/data/fonts/HumongousofEternitySt.ttf differ
diff --git a/data/icons/128x128/strawberry-panel-grey.png b/data/icons/128x128/strawberry-panel-grey.png
deleted file mode 100644
index cac3b2622..000000000
Binary files a/data/icons/128x128/strawberry-panel-grey.png and /dev/null differ
diff --git a/data/icons/128x128/strawberry-panel.png b/data/icons/128x128/strawberry-panel.png
deleted file mode 100644
index 4d575b1ad..000000000
Binary files a/data/icons/128x128/strawberry-panel.png and /dev/null differ
diff --git a/data/icons/22x22/strawberry-panel-grey.png b/data/icons/22x22/strawberry-panel-grey.png
deleted file mode 100644
index 8ad482b4f..000000000
Binary files a/data/icons/22x22/strawberry-panel-grey.png and /dev/null differ
diff --git a/data/icons/22x22/strawberry-panel.png b/data/icons/22x22/strawberry-panel.png
deleted file mode 100644
index 004df73e4..000000000
Binary files a/data/icons/22x22/strawberry-panel.png and /dev/null differ
diff --git a/data/icons/32x32/strawberry-panel-grey.png b/data/icons/32x32/strawberry-panel-grey.png
deleted file mode 100644
index 5787faf97..000000000
Binary files a/data/icons/32x32/strawberry-panel-grey.png and /dev/null differ
diff --git a/data/icons/32x32/strawberry-panel.png b/data/icons/32x32/strawberry-panel.png
deleted file mode 100644
index 961d0cd69..000000000
Binary files a/data/icons/32x32/strawberry-panel.png and /dev/null differ
diff --git a/data/icons/48x48/strawberry-panel-grey.png b/data/icons/48x48/strawberry-panel-grey.png
deleted file mode 100644
index 8d11609a0..000000000
Binary files a/data/icons/48x48/strawberry-panel-grey.png and /dev/null differ
diff --git a/data/icons/48x48/strawberry-panel.png b/data/icons/48x48/strawberry-panel.png
deleted file mode 100644
index fb87d0a1b..000000000
Binary files a/data/icons/48x48/strawberry-panel.png and /dev/null differ
diff --git a/data/icons/64x64/strawberry-panel-grey.png b/data/icons/64x64/strawberry-panel-grey.png
deleted file mode 100644
index e14770991..000000000
Binary files a/data/icons/64x64/strawberry-panel-grey.png and /dev/null differ
diff --git a/data/icons/64x64/strawberry-panel.png b/data/icons/64x64/strawberry-panel.png
deleted file mode 100644
index cabce0397..000000000
Binary files a/data/icons/64x64/strawberry-panel.png and /dev/null differ
diff --git a/data/icons/full/strawberry-panel-grey.png b/data/icons/full/strawberry-panel-grey.png
deleted file mode 100644
index 75d8a1e5c..000000000
Binary files a/data/icons/full/strawberry-panel-grey.png and /dev/null differ
diff --git a/data/icons/full/strawberry-panel.png b/data/icons/full/strawberry-panel.png
deleted file mode 100644
index e651e1b33..000000000
Binary files a/data/icons/full/strawberry-panel.png and /dev/null differ
diff --git a/data/style/statusview.css b/data/style/statusview.css
deleted file mode 100644
index 6203a1005..000000000
--- a/data/style/statusview.css
+++ /dev/null
@@ -1,11 +0,0 @@
-StatusView {
- background: white;
- background-color: white;
-}
-QVBoxLayout {
- background: white;
- background-color: white;
-}
-QScrollArea {
- background: qpalette(base);
-}
diff --git a/data/style/mainwindow.css b/data/style/strawberry.css
similarity index 79%
rename from data/style/mainwindow.css
rename to data/style/strawberry.css
index cbc6f45f0..619710fbe 100644
--- a/data/style/mainwindow.css
+++ b/data/style/strawberry.css
@@ -52,3 +52,20 @@ darwin {
darwin QMenu {
font-size: 13pt;
}
+
+#scrollarea_play {
+ background-color: white;
+ font: 11pt;
+}
+
+#scrollarea_stop {
+ background-color: white;
+ font: 11pt;
+}
+
+#scrollAreaWidgetContents_stop {
+ background-color: white;
+}
+#scrollAreaWidgetContents_play {
+ background-color: white;
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d1cd748d3..e5c9cce5e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -107,7 +107,6 @@ set(SOURCES
core/urlhandler.cpp
core/utilities.cpp
core/scangiomodulepath.cpp
- core/flowlayout.cpp
core/iconloader.cpp
core/qtsystemtrayicon.cpp
core/standarditemiconloader.cpp
@@ -129,6 +128,10 @@ set(SOURCES
equalizer/equalizer.cpp
equalizer/equalizerslider.cpp
+ context/contextview.cpp
+ context/contextalbumsmodel.cpp
+ context/contextalbumsview.cpp
+
collection/collection.cpp
collection/collectionmodel.cpp
collection/collectionbackend.cpp
@@ -218,8 +221,6 @@ set(SOURCES
widgets/autoexpandingtreeview.cpp
widgets/busyindicator.cpp
widgets/clickablelabel.cpp
- widgets/didyoumean.cpp
- widgets/elidedlabel.cpp
widgets/fancytabwidget.cpp
widgets/favoritewidget.cpp
widgets/fileview.cpp
@@ -230,14 +231,9 @@ set(SOURCES
widgets/lineedit.cpp
widgets/linetextedit.cpp
widgets/multiloadingindicator.cpp
- widgets/statusview.cpp
widgets/playingwidget.cpp
widgets/osd.cpp
widgets/osdpretty.cpp
- widgets/prettyimage.cpp
- widgets/prettyimageview.cpp
- widgets/progressitemdelegate.cpp
- widgets/ratingwidget.cpp
widgets/renametablineedit.cpp
widgets/sliderwidget.cpp
widgets/stickyslider.cpp
@@ -246,7 +242,6 @@ set(SOURCES
widgets/trackslider.cpp
widgets/tracksliderpopup.cpp
widgets/tracksliderslider.cpp
- widgets/widgetfadehelper.cpp
widgets/loginstatewidget.cpp
musicbrainz/acoustidclient.cpp
@@ -271,6 +266,7 @@ set(SOURCES
internet/internetmodel.cpp
internet/internetservice.cpp
internet/internetplaylistitem.cpp
+
tidal/tidalservice.cpp
tidal/tidalsearch.cpp
tidal/tidalsearchview.cpp
@@ -278,6 +274,13 @@ set(SOURCES
tidal/tidalsearchsortmodel.cpp
tidal/tidalsearchitemdelegate.cpp
+ lyrics/lyricsproviders.cpp
+ lyrics/lyricsprovider.cpp
+ lyrics/lyricsfetcher.cpp
+ lyrics/lyricsfetchersearch.cpp
+ lyrics/auddlyricsprovider.cpp
+ lyrics/apiseedslyricsprovider.cpp
+
)
set(HEADERS
@@ -309,6 +312,10 @@ set(HEADERS
equalizer/equalizer.h
equalizer/equalizerslider.h
+
+ context/contextview.h
+ context/contextalbumsmodel.h
+ context/contextalbumsview.h
collection/collection.h
collection/collectionmodel.h
@@ -391,8 +398,6 @@ set(HEADERS
widgets/autoexpandingtreeview.h
widgets/busyindicator.h
widgets/clickablelabel.h
- widgets/didyoumean.h
- widgets/elidedlabel.h
widgets/fancytabwidget.h
widgets/favoritewidget.h
widgets/fileview.h
@@ -402,14 +407,9 @@ set(HEADERS
widgets/lineedit.h
widgets/linetextedit.h
widgets/multiloadingindicator.h
- widgets/statusview.h
widgets/playingwidget.h
widgets/osd.h
widgets/osdpretty.h
- widgets/prettyimage.h
- widgets/prettyimageview.h
- widgets/progressitemdelegate.h
- widgets/ratingwidget.h
widgets/renametablineedit.h
widgets/sliderwidget.h
widgets/stickyslider.h
@@ -417,7 +417,6 @@ set(HEADERS
widgets/trackslider.h
widgets/tracksliderpopup.h
widgets/tracksliderslider.h
- widgets/widgetfadehelper.h
widgets/loginstatewidget.h
musicbrainz/acoustidclient.h
@@ -447,6 +446,13 @@ set(HEADERS
tidal/tidalsearch.h
tidal/tidalsearchview.h
tidal/tidalsearchmodel.h
+
+ lyrics/lyricsproviders.h
+ lyrics/lyricsprovider.h
+ lyrics/lyricsfetcher.h
+ lyrics/lyricsfetchersearch.h
+ lyrics/auddlyricsprovider.h
+ lyrics/apiseedslyricsprovider.h
)
@@ -454,6 +460,8 @@ set(UI
core/mainwindow.ui
+ context/contextviewcontainer.ui
+
collection/groupbydialog.ui
collection/collectionfilterwidget.ui
collection/collectionviewcontainer.ui
diff --git a/src/collection/collection.cpp b/src/collection/collection.cpp
index 8ecbd28a0..94eba5889 100644
--- a/src/collection/collection.cpp
+++ b/src/collection/collection.cpp
@@ -57,7 +57,6 @@ SCollection::SCollection(Application *app, QObject *parent)
backend_->Init(app->database(), kSongsTable, kDirsTable, kSubdirsTable, kFtsTable);
model_ = new CollectionModel(backend_, app_, this);
-
ReloadSettings();
diff --git a/src/collection/collectionbackend.cpp b/src/collection/collectionbackend.cpp
index f0db67aaa..19ebbcad1 100644
--- a/src/collection/collectionbackend.cpp
+++ b/src/collection/collectionbackend.cpp
@@ -54,7 +54,6 @@ CollectionBackend::CollectionBackend(QObject *parent)
{}
void CollectionBackend::Init(Database *db, const QString &songs_table, const QString &dirs_table, const QString &subdirs_table, const QString &fts_table) {
-
db_ = db;
songs_table_ = songs_table;
dirs_table_ = dirs_table;
@@ -219,8 +218,6 @@ void CollectionBackend::UpdateTotalArtistCount() {
q.exec();
if (db_->CheckErrors(q)) return;
if (!q.next()) return;
-
- //qLog(Debug) << "TotalArtist: " << q.value(0).toInt();
emit TotalArtistCountUpdated(q.value(0).toInt());
@@ -236,8 +233,6 @@ void CollectionBackend::UpdateTotalAlbumCount() {
q.exec();
if (db_->CheckErrors(q)) return;
if (!q.next()) return;
-
- //qLog(Debug) << "TotalAlbum: " << q.value(0).toInt();
emit TotalAlbumCountUpdated(q.value(0).toInt());
@@ -530,7 +525,7 @@ void CollectionBackend::MarkSongsUnavailable(const SongList &songs, bool unavail
}
QStringList CollectionBackend::GetAll(const QString &column, const QueryOptions &opt) {
-
+
CollectionQuery query(opt);
query.SetColumnSpec("DISTINCT " + column);
query.AddCompilationRequirement(false);
@@ -547,6 +542,7 @@ QStringList CollectionBackend::GetAll(const QString &column, const QueryOptions
}
QStringList CollectionBackend::GetAllArtists(const QueryOptions &opt) {
+
return GetAll("artist", opt);
}
@@ -596,8 +592,7 @@ CollectionBackend::AlbumList CollectionBackend::GetAlbumsByArtist(const QString
return GetAlbums(artist, QString(), false, opt);
}
-CollectionBackend::AlbumList CollectionBackend::GetAlbumsByAlbumArtist(
- const QString &album_artist, const QueryOptions &opt) {
+CollectionBackend::AlbumList CollectionBackend::GetAlbumsByAlbumArtist(const QString &album_artist, const QueryOptions &opt) {
return GetAlbums(QString(), album_artist, false, opt);
}
@@ -629,6 +624,7 @@ SongList CollectionBackend::ExecCollectionQuery(CollectionQuery *query) {
ret << song;
}
return ret;
+
}
Song CollectionBackend::GetSongById(int id) {
@@ -638,7 +634,6 @@ Song CollectionBackend::GetSongById(int id) {
}
SongList CollectionBackend::GetSongsById(const QList &ids) {
-
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@@ -658,7 +653,6 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids) {
}
SongList CollectionBackend::GetSongsByForeignId(const QStringList &ids, const QString &table, const QString &column) {
-
QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect());
@@ -687,7 +681,6 @@ Song CollectionBackend::GetSongById(int id, QSqlDatabase &db) {
}
SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &db) {
-
QString in = ids.join(",");
QSqlQuery q(db);
@@ -705,7 +698,6 @@ SongList CollectionBackend::GetSongsById(const QStringList &ids, QSqlDatabase &d
}
Song CollectionBackend::GetSongByUrl(const QUrl &url, qint64 beginning) {
-
CollectionQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("filename", url.toEncoded());
@@ -719,7 +711,6 @@ Song CollectionBackend::GetSongByUrl(const QUrl &url, qint64 beginning) {
}
SongList CollectionBackend::GetSongsByUrl(const QUrl &url) {
-
CollectionQuery query;
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
query.AddWhere("filename", url.toEncoded());
@@ -757,6 +748,7 @@ SongList CollectionBackend::GetCompilationSongs(const QString &album, const Quer
ret << song;
}
return ret;
+
}
void CollectionBackend::UpdateCompilations() {
@@ -933,7 +925,7 @@ CollectionBackend::Album CollectionBackend::GetAlbumArt(const QString &artist, c
}
void CollectionBackend::UpdateManualAlbumArtAsync(const QString &artist, const QString &albumartist, const QString &album, const QString &art) {
-
+
metaObject()->invokeMethod(this, "UpdateManualAlbumArt", Qt::QueuedConnection, Q_ARG(QString, artist), Q_ARG(QString, albumartist), Q_ARG(QString, album), Q_ARG(QString, art));
}
diff --git a/src/collection/collectiondirectorymodel.cpp b/src/collection/collectiondirectorymodel.cpp
index 09b6418a2..4510233db 100644
--- a/src/collection/collectiondirectorymodel.cpp
+++ b/src/collection/collectiondirectorymodel.cpp
@@ -36,6 +36,8 @@
#include "collectionbackend.h"
#include "collectiondirectorymodel.h"
+using std::shared_ptr;
+
CollectionDirectoryModel::CollectionDirectoryModel(CollectionBackend *backend, QObject *parent)
: QStandardItemModel(parent),
dir_icon_(IconLoader::Load("document-open-folder")),
diff --git a/src/collection/collectionfilterwidget.cpp b/src/collection/collectionfilterwidget.cpp
index 6b93246aa..dd5a96927 100644
--- a/src/collection/collectionfilterwidget.cpp
+++ b/src/collection/collectionfilterwidget.cpp
@@ -43,6 +43,7 @@
#include "core/iconloader.h"
#include "core/song.h"
+#include "core/logging.h"
#include "collectionmodel.h"
#include "collectionquery.h"
#include "savedgroupingmanager.h"
@@ -58,6 +59,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
filter_delay_(new QTimer(this)),
filter_applies_to_model_(true),
delay_behaviour_(DelayedOnLargeLibraries) {
+
ui_->setupUi(this);
// Add the available fields to the tooltip here instead of the ui file to prevent that they get translated by mistake.
diff --git a/src/collection/collectionmodel.cpp b/src/collection/collectionmodel.cpp
index d23a26822..1cc6bca13 100644
--- a/src/collection/collectionmodel.cpp
+++ b/src/collection/collectionmodel.cpp
@@ -85,8 +85,8 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
total_song_count_(0),
total_artist_count_(0),
total_album_count_(0),
- artist_icon_(IconLoader::Load("guitar")),
- album_icon_(IconLoader::Load("cd")),
+ artist_icon_(IconLoader::Load("folder-sound")),
+ album_icon_(IconLoader::Load("cdcase")),
playlists_dir_icon_(IconLoader::Load("folder-sound")),
playlist_icon_(IconLoader::Load("albums")),
init_task_id_(-1),
@@ -109,10 +109,9 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
//icon_cache_->setCacheDirectory(Utilities::GetConfigPath(Utilities::Path_CacheRoot) + "/pixmapcache");
//icon_cache_->setMaximumCacheSize(CollectionModel::kIconCacheSize);
- //QIcon nocover = IconLoader::Load("nocover");
- //QIcon nocover(":/pictures/noalbumart.png");
- //no_cover_icon_ = nocover.pixmap(nocover.availableSizes().last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
- no_cover_icon_ = QPixmap(":/pictures/noalbumart.png").scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ QIcon nocover = IconLoader::Load("cdcase");
+ no_cover_icon_ = nocover.pixmap(nocover.availableSizes().last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ //no_cover_icon_ = QPixmap(":/pictures/noalbumart.png").scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
connect(backend_, SIGNAL(SongsDiscovered(SongList)), SLOT(SongsDiscovered(SongList)));
connect(backend_, SIGNAL(SongsDeleted(SongList)), SLOT(SongsDeleted(SongList)));
@@ -159,7 +158,6 @@ void CollectionModel::SaveGrouping(QString name) {
}
-
void CollectionModel::Init(bool async) {
if (async) {
@@ -717,6 +715,7 @@ CollectionModel::QueryResult CollectionModel::RunQuery(CollectionItem *parent) {
// Execute the query
QMutexLocker l(backend_->db()->Mutex());
+
if (!backend_->ExecQuery(&q)) return result;
while (q.Next()) {
@@ -751,7 +750,6 @@ void CollectionModel::PostQuery(CollectionItem *parent, const CollectionModel::Q
}
void CollectionModel::LazyPopulate(CollectionItem *parent, bool signal) {
-
if (parent->lazy_loaded) return;
parent->lazy_loaded = true;
@@ -761,7 +759,6 @@ void CollectionModel::LazyPopulate(CollectionItem *parent, bool signal) {
}
void CollectionModel::ResetAsync() {
-
QFuture future = QtConcurrent::run(this, &CollectionModel::RunQuery, root_);
NewClosure(future, this, SLOT(ResetAsyncQueryFinished(QFuture)), future);
@@ -1033,13 +1030,13 @@ CollectionItem *CollectionModel::ItemFromQuery(GroupBy type, bool signal, bool c
item->key = QString::number(bitrate);
item->sort_text = SortTextForNumber(bitrate) + " ";
break;
-
+
case GroupBy_Samplerate:
samplerate = qMax(0, row.value(0).toInt());
item->key = QString::number(samplerate);
item->sort_text = SortTextForNumber(samplerate) + " ";
break;
-
+
case GroupBy_Bitdepth:
bitdepth = qMax(0, row.value(0).toInt());
item->key = QString::number(bitdepth);
diff --git a/src/collection/collectionquery.cpp b/src/collection/collectionquery.cpp
index a9470c5b5..28e75664e 100644
--- a/src/collection/collectionquery.cpp
+++ b/src/collection/collectionquery.cpp
@@ -31,11 +31,12 @@
#include
#include "collectionquery.h"
+#include "core/logging.h"
#include "core/song.h"
QueryOptions::QueryOptions() : max_age_(-1), query_mode_(QueryMode_All) {}
-CollectionQuery::CollectionQuery(const QueryOptions& options)
+CollectionQuery::CollectionQuery(const QueryOptions &options)
: include_unavailable_(false), join_with_fts_(false), limit_(-1) {
if (!options.filter().isEmpty()) {
diff --git a/src/collection/collectionview.cpp b/src/collection/collectionview.cpp
index 56e1421ff..bf8ead77d 100644
--- a/src/collection/collectionview.cpp
+++ b/src/collection/collectionview.cpp
@@ -298,7 +298,7 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
if (!last_selected_song_.url().isEmpty()) {
QModelIndex index = qobject_cast(model())->mapToSource(current);
SongList songs = app_->collection_model()->GetChildSongs(index);
- for (const Song& song : songs) {
+ for (const Song &song : songs) {
if (song == last_selected_song_) {
setCurrentIndex(current);
return true;
@@ -338,9 +338,9 @@ void CollectionView::ReloadSettings() {
QSettings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
- SetAutoOpen(settings.value("auto_open", true).toBool());
+ SetAutoOpen(settings.value("auto_open", false).toBool());
- if (app_ != nullptr) {
+ if (app_) {
app_->collection_model()->set_pretty_covers(settings.value("pretty_covers", true).toBool());
app_->collection_model()->set_show_dividers(settings.value("show_dividers", true).toBool());
}
@@ -437,7 +437,7 @@ void CollectionView::paintEvent(QPaintEvent *event) {
}
void CollectionView::mouseReleaseEvent(QMouseEvent *e) {
-
+
QTreeView::mouseReleaseEvent(e);
if (total_song_count_ == 0) {
@@ -494,7 +494,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
int regular_elements = 0;
int regular_editable = 0;
- for (const QModelIndex& index : selected_indexes) {
+ for (const QModelIndex &index : selected_indexes) {
regular_elements++;
if(app_->collection_model()->data(index, CollectionModel::Role_Editable).toBool()) {
regular_editable++;
@@ -559,8 +559,7 @@ void CollectionView::ShowInVarious(bool on) {
QList all_of_album = app_->collection_backend()->GetSongsByAlbum(album);
QSet other_artists;
for (const Song &s : all_of_album) {
- if (!albums.contains(album, s.artist()) &&
- !other_artists.contains(s.artist())) {
+ if (!albums.contains(album, s.artist()) && !other_artists.contains(s.artist())) {
other_artists.insert(s.artist());
}
}
@@ -586,7 +585,7 @@ void CollectionView::ShowInVarious(bool on) {
void CollectionView::Load() {
QMimeData *data = model()->mimeData(selectedIndexes());
- if (MimeData* mime_data = qobject_cast(data)) {
+ if (MimeData *mime_data = qobject_cast(data)) {
mime_data->clear_first_ = true;
}
emit AddToPlaylistSignal(data);
diff --git a/src/collection/collectionviewcontainer.cpp b/src/collection/collectionviewcontainer.cpp
index 6bcc3cc87..6cf8f7580 100644
--- a/src/collection/collectionviewcontainer.cpp
+++ b/src/collection/collectionviewcontainer.cpp
@@ -43,11 +43,6 @@ CollectionViewContainer::CollectionViewContainer(QWidget *parent) : QWidget(pare
}
CollectionViewContainer::~CollectionViewContainer() { delete ui_; }
-
CollectionView *CollectionViewContainer::view() const { return ui_->view; }
-
-CollectionFilterWidget *CollectionViewContainer::filter() const {
- return ui_->filter;
-}
-
+CollectionFilterWidget *CollectionViewContainer::filter() const { return ui_->filter; }
void CollectionViewContainer::ReloadSettings() { view()->ReloadSettings(); }
diff --git a/src/collection/collectionviewcontainer.ui b/src/collection/collectionviewcontainer.ui
index 824917c64..08b458b5c 100644
--- a/src/collection/collectionviewcontainer.ui
+++ b/src/collection/collectionviewcontainer.ui
@@ -6,7 +6,7 @@
0
0
- 400
+ 300
300
diff --git a/src/context/contextalbumsmodel.cpp b/src/context/contextalbumsmodel.cpp
new file mode 100644
index 000000000..3ebd7f930
--- /dev/null
+++ b/src/context/contextalbumsmodel.cpp
@@ -0,0 +1,527 @@
+/*
+ * Strawberry Music Player
+ * This code was part of Clementine.
+ * Copyright 2010, David Sansome
+ * Copyright 2013, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#include "config.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/application.h"
+#include "core/closure.h"
+#include "core/database.h"
+#include "core/iconloader.h"
+#include "core/logging.h"
+#include "collection/collectionquery.h"
+#include "collection/collectionbackend.h"
+#include "collection/collectionitem.h"
+#include "collection/sqlrow.h"
+#include "playlist/playlistmanager.h"
+#include "playlist/songmimedata.h"
+#include "covermanager/albumcoverloader.h"
+
+#include "contextalbumsmodel.h"
+
+using std::placeholders::_1;
+using std::placeholders::_2;
+
+const int ContextAlbumsModel::kPrettyCoverSize = 32;
+const qint64 ContextAlbumsModel::kIconCacheSize = 100000000; //~100MB
+
+ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent) :
+ SimpleTreeModel(new CollectionItem(this), parent),
+ backend_(backend),
+ app_(app),
+ artist_icon_(IconLoader::Load("folder-sound")),
+ album_icon_(IconLoader::Load("cdcase")),
+ playlists_dir_icon_(IconLoader::Load("folder-sound")),
+ playlist_icon_(IconLoader::Load("albums")),
+ use_pretty_covers_(true)
+ {
+
+ root_->lazy_loaded = true;
+
+ cover_loader_options_.desired_height_ = kPrettyCoverSize;
+ cover_loader_options_.pad_output_image_ = true;
+ cover_loader_options_.scale_output_image_ = true;
+
+ connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)), SLOT(AlbumArtLoaded(quint64, QImage)));
+
+ QIcon nocover = IconLoader::Load("cdcase");
+ no_cover_icon_ = nocover.pixmap(nocover.availableSizes().last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+
+}
+
+ContextAlbumsModel::~ContextAlbumsModel() { delete root_; }
+
+void ContextAlbumsModel::set_pretty_covers(bool use_pretty_covers) {
+
+ if (use_pretty_covers != use_pretty_covers_) {
+ use_pretty_covers_ = use_pretty_covers;
+ Reset();
+ }
+
+}
+
+void ContextAlbumsModel::AddSongs(const SongList &songs) {
+
+ for (const Song &song : songs) {
+ if (song_nodes_.contains(song.id())) continue;
+
+ // Before we can add each song we need to make sure the required container items already exist in the tree.
+
+ // Find parent containers in the tree
+ CollectionItem *container = root_;
+
+ // Does it exist already?
+ if (!container_nodes_.contains(song.album())) {
+ // Create the container
+ container_nodes_[song.album()] = ItemFromSong(CollectionItem::Type_Container, true, container, song, 0);
+ }
+ container = container_nodes_[song.album()];
+ if (!container->lazy_loaded) continue;
+
+ // We've gone all the way down to the deepest level and everything was already lazy loaded, so now we have to create the song in the container.
+ song_nodes_[song.id()] = ItemFromSong(CollectionItem::Type_Song, true, container, song, -1);
+ }
+
+}
+
+QString ContextAlbumsModel::AlbumIconPixmapCacheKey(const QModelIndex &index) const {
+
+ QStringList path;
+ QModelIndex index_copy(index);
+ while (index_copy.isValid()) {
+ path.prepend(index_copy.data().toString());
+ index_copy = index_copy.parent();
+ }
+ return "contextalbumsart:" + path.join("/");
+
+}
+
+QVariant ContextAlbumsModel::AlbumIcon(const QModelIndex &index) {
+
+ CollectionItem *item = IndexToItem(index);
+ if (!item) return no_cover_icon_;
+
+ // Check the cache for a pixmap we already loaded.
+ const QString cache_key = AlbumIconPixmapCacheKey(index);
+
+ QPixmap cached_pixmap;
+ if (QPixmapCache::find(cache_key, &cached_pixmap)) {
+ return cached_pixmap;
+ }
+
+ // Maybe we're loading a pixmap already?
+ if (pending_cache_keys_.contains(cache_key)) {
+ return no_cover_icon_;
+ }
+
+ // No art is cached and we're not loading it already. Load art for the first song in the album.
+ SongList songs = GetChildSongs(index);
+ if (!songs.isEmpty()) {
+ const quint64 id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, songs.first());
+ pending_art_[id] = ItemAndCacheKey(item, cache_key);
+ pending_cache_keys_.insert(cache_key);
+ }
+
+ return no_cover_icon_;
+
+}
+
+void ContextAlbumsModel::AlbumArtLoaded(quint64 id, const QImage &image) {
+
+ ItemAndCacheKey item_and_cache_key = pending_art_.take(id);
+ CollectionItem *item = item_and_cache_key.first;
+ const QString &cache_key = item_and_cache_key.second;
+
+ if (!item) return;
+
+ pending_cache_keys_.remove(cache_key);
+
+ // Insert this image in the cache.
+ if (image.isNull()) {
+ // Set the no_cover image so we don't continually try to load art.
+ QPixmapCache::insert(cache_key, no_cover_icon_);
+ }
+ else {
+ //qLog(Debug) << cache_key;
+ QPixmap image_pixmap;
+ image_pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(cache_key, image_pixmap);
+ }
+
+ const QModelIndex index = ItemToIndex(item);
+ emit dataChanged(index, index);
+
+}
+
+QVariant ContextAlbumsModel::data(const QModelIndex &index, int role) const {
+
+ const CollectionItem *item = IndexToItem(index);
+
+ // Handle a special case for returning album artwork instead of a generic CD icon.
+ // This is here instead of in the other data() function to let us use the QModelIndex& version of GetChildSongs,
+ // which satisfies const-ness, instead of the CollectionItem *version, which doesn't.
+ if (use_pretty_covers_) {
+ bool is_album_node = false;
+ if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container) {
+ is_album_node = (item->container_level == 0);
+ }
+ if (is_album_node) {
+ // It has const behaviour some of the time - that's ok right?
+ return const_cast(this)->AlbumIcon(index);
+ }
+ }
+
+ return data(item, role);
+
+}
+
+QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
+
+ switch (role) {
+ case Qt::DisplayRole:
+ case Qt::ToolTipRole:
+ return item->DisplayText();
+
+ case Qt::DecorationRole:
+ switch (item->type) {
+ case CollectionItem::Type_PlaylistContainer:
+ return playlists_dir_icon_;
+ case CollectionItem::Type_Container:
+ if (item->type == CollectionItem::Type_Container && item->container_level == 0) { return album_icon_; }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case Role_Type:
+ return item->type;
+
+ case Role_IsDivider:
+ return item->type == CollectionItem::Type_Divider;
+
+ case Role_ContainerType:
+ return item->type;
+
+ case Role_Key:
+ return item->key;
+
+ case Role_Artist:
+ return item->metadata.artist();
+
+ case Role_Editable:
+ if (!item->lazy_loaded) {
+ const_cast(this)->LazyPopulate(const_cast(item), true);
+ }
+
+ if (item->type == CollectionItem::Type_Container) {
+ // if we have even one non editable item as a child, we ourselves are not available for edit
+ if (!item->children.isEmpty()) {
+ for (CollectionItem *child : item->children) {
+ if (!data(child, role).toBool()) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ else if (item->type == CollectionItem::Type_Song) {
+ return item->metadata.IsEditable();
+ }
+ else {
+ return false;
+ }
+
+ case Role_SortText:
+ return item->SortText();
+ }
+ return QVariant();
+
+}
+
+ContextAlbumsModel::QueryResult ContextAlbumsModel::RunQuery(CollectionItem *parent) {
+
+ QueryResult result;
+ CollectionQuery q(query_options_);
+ q.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
+
+ // Walk up through the item's parents adding filters as necessary
+ CollectionItem *p = parent;
+ while (p && p->type == CollectionItem::Type_Container) {
+ if (p->container_level == 0) {
+ q.AddWhere("album", p->key);
+ }
+ p = p->parent;
+ }
+
+ // Execute the query
+ QMutexLocker l(backend_->db()->Mutex());
+
+ if (!backend_->ExecQuery(&q)) return result;
+
+ while (q.Next()) {
+ result.rows << SqlRow(q);
+ }
+ return result;
+
+}
+
+void ContextAlbumsModel::PostQuery(CollectionItem *parent, const ContextAlbumsModel::QueryResult &result, bool signal) {
+
+ int child_level = (parent == root_ ? 0 : parent->container_level + 1);
+
+ for (const SqlRow &row : result.rows) {
+
+ CollectionItem::Type item_type = (parent == root_ ? CollectionItem::Type_Container : CollectionItem::Type_Song);
+
+ if (signal) beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
+
+ CollectionItem *item = new CollectionItem(item_type, parent);
+ item->container_level = child_level;
+ item->metadata.InitFromQuery(row, true);
+ item->key = item->metadata.title();
+ item->display_text = item->metadata.TitleWithCompilationArtist();
+ item->sort_text = SortTextForSong(item->metadata);
+ if (parent != root_) item->lazy_loaded = true;
+
+ if (signal) endInsertRows();
+
+ if (parent == root_) container_nodes_[item->key] = item;
+ else song_nodes_[item->metadata.id()] = item;
+
+ }
+
+}
+
+void ContextAlbumsModel::LazyPopulate(CollectionItem *parent, bool signal) {
+
+ if (parent->lazy_loaded) return;
+ parent->lazy_loaded = true;
+
+ QueryResult result = RunQuery(parent);
+ PostQuery(parent, result, signal);
+
+}
+
+void ContextAlbumsModel::Reset() {
+
+ beginResetModel();
+ delete root_;
+ song_nodes_.clear();
+ container_nodes_.clear();
+ pending_art_.clear();
+
+ root_ = new CollectionItem(this);
+ root_->lazy_loaded = false;
+ endResetModel();
+
+}
+
+CollectionItem *ContextAlbumsModel::ItemFromSong(CollectionItem::Type item_type, bool signal, CollectionItem *parent, const Song &s, int container_level) {
+
+ if (signal) beginInsertRows(ItemToIndex(parent), parent->children.count(), parent->children.count());
+
+ CollectionItem *item = new CollectionItem(item_type, parent);
+ item->container_level = container_level;
+
+ if (item->key.isNull()) item->key = s.album();
+ //if (item->key.isNull()) item->key = s.effective_albumartist();
+ item->display_text = TextOrUnknown(item->key);
+ item->sort_text = SortTextForArtist(item->key);
+
+ if (item_type == CollectionItem::Type_Song) item->lazy_loaded = true;
+ if (signal) endInsertRows();
+
+ return item;
+
+}
+
+QString ContextAlbumsModel::TextOrUnknown(const QString &text) {
+
+ if (text.isEmpty()) return tr("Unknown");
+ return text;
+
+}
+
+QString ContextAlbumsModel::SortText(QString text) {
+
+ if (text.isEmpty()) {
+ text = " unknown";
+ }
+ else {
+ text = text.toLower();
+ }
+ text = text.remove(QRegExp("[^\\w ]"));
+
+ return text;
+
+}
+
+QString ContextAlbumsModel::SortTextForArtist(QString artist) {
+
+ artist = SortText(artist);
+
+ if (artist.startsWith("the ")) {
+ artist = artist.right(artist.length() - 4) + ", the";
+ }
+ else if (artist.startsWith("a ")) {
+ artist = artist.right(artist.length() - 2) + ", a";
+ }
+ else if (artist.startsWith("an ")) {
+ artist = artist.right(artist.length() - 3) + ", an";
+ }
+
+ return artist;
+
+}
+
+QString ContextAlbumsModel::SortTextForSong(const Song &song) {
+
+ QString ret = QString::number(qMax(0, song.disc()) * 1000 + qMax(0, song.track()));
+ ret.prepend(QString("0").repeated(6 - ret.length()));
+ ret.append(song.url().toString());
+ return ret;
+
+}
+
+Qt::ItemFlags ContextAlbumsModel::flags(const QModelIndex &index) const {
+
+ switch (IndexToItem(index)->type) {
+ case CollectionItem::Type_Song:
+ case CollectionItem::Type_Container:
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
+ case CollectionItem::Type_Divider:
+ case CollectionItem::Type_Root:
+ case CollectionItem::Type_LoadingIndicator:
+ default:
+ return Qt::ItemIsEnabled;
+ }
+
+}
+
+QStringList ContextAlbumsModel::mimeTypes() const {
+ return QStringList() << "text/uri-list";
+}
+
+QMimeData *ContextAlbumsModel::mimeData(const QModelIndexList &indexes) const {
+
+ if (indexes.isEmpty()) return nullptr;
+
+ SongMimeData *data = new SongMimeData;
+ QList urls;
+ QSet song_ids;
+
+ data->backend = backend_;
+
+ for (const QModelIndex &index : indexes) {
+ GetChildSongs(IndexToItem(index), &urls, &data->songs, &song_ids);
+ }
+
+ data->setUrls(urls);
+ data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs);
+
+ return data;
+
+}
+
+bool ContextAlbumsModel::CompareItems(const CollectionItem *a, const CollectionItem *b) const {
+
+ QVariant left(data(a, ContextAlbumsModel::Role_SortText));
+ QVariant right(data(b, ContextAlbumsModel::Role_SortText));
+
+ if (left.type() == QVariant::Int) return left.toInt() < right.toInt();
+ return left.toString() < right.toString();
+
+}
+
+void ContextAlbumsModel::GetChildSongs(CollectionItem *item, QList *urls, SongList *songs, QSet *song_ids) const {
+
+ switch (item->type) {
+ case CollectionItem::Type_Container: {
+ const_cast(this)->LazyPopulate(item);
+
+ QList children = item->children;
+ qSort(children.begin(), children.end(), std::bind(&ContextAlbumsModel::CompareItems, this, _1, _2));
+
+ for (CollectionItem *child : children)
+ GetChildSongs(child, urls, songs, song_ids);
+ break;
+ }
+
+ case CollectionItem::Type_Song:
+ urls->append(item->metadata.url());
+ if (!song_ids->contains(item->metadata.id())) {
+ songs->append(item->metadata);
+ song_ids->insert(item->metadata.id());
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+SongList ContextAlbumsModel::GetChildSongs(const QModelIndexList &indexes) const {
+
+ QList dontcare;
+ SongList ret;
+ QSet song_ids;
+
+ for (const QModelIndex &index : indexes) {
+ GetChildSongs(IndexToItem(index), &dontcare, &ret, &song_ids);
+ }
+ return ret;
+
+}
+
+SongList ContextAlbumsModel::GetChildSongs(const QModelIndex &index) const {
+ return GetChildSongs(QModelIndexList() << index);
+}
+
+bool ContextAlbumsModel::canFetchMore(const QModelIndex &parent) const {
+
+ if (!parent.isValid()) return false;
+
+ CollectionItem *item = IndexToItem(parent);
+ return !item->lazy_loaded;
+
+}
diff --git a/src/context/contextalbumsmodel.h b/src/context/contextalbumsmodel.h
new file mode 100644
index 000000000..4faece1f7
--- /dev/null
+++ b/src/context/contextalbumsmodel.h
@@ -0,0 +1,143 @@
+/*
+ * Strawberry Music Player
+ * This code was part of Clementine.
+ * Copyright 2010, David Sansome
+ * Copyright 2013, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#ifndef CONTEXTALBUMSMODEL_H
+#define CONTEXTALBUMSMODEL_H
+
+#include "config.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/simpletreemodel.h"
+#include "core/song.h"
+#include "collection/collectionquery.h"
+#include "collection/collectionitem.h"
+#include "collection/sqlrow.h"
+#include "covermanager/albumcoverloaderoptions.h"
+
+class Application;
+class CollectionBackend;
+class CollectionItem;
+
+class ContextAlbumsModel : public SimpleTreeModel {
+ Q_OBJECT
+
+ public:
+ ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent = nullptr);
+ ~ContextAlbumsModel();
+
+ static const int kPrettyCoverSize;
+ static const qint64 kIconCacheSize;
+
+ enum Role {
+ Role_Type = Qt::UserRole + 1,
+ Role_ContainerType,
+ Role_SortText,
+ Role_Key,
+ Role_Artist,
+ Role_IsDivider,
+ Role_Editable,
+ LastRole
+ };
+
+ struct QueryResult {
+ QueryResult() {}
+ SqlRowList rows;
+ };
+
+ CollectionBackend *backend() const { return backend_; }
+
+ void GetChildSongs(CollectionItem *item, QList *urls, SongList *songs, QSet *song_ids) const;
+ SongList GetChildSongs(const QModelIndex &index) const;
+ SongList GetChildSongs(const QModelIndexList &indexes) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ QStringList mimeTypes() const;
+ QMimeData *mimeData(const QModelIndexList &indexes) const;
+ bool canFetchMore(const QModelIndex &parent) const;
+
+ void set_pretty_covers(bool use_pretty_covers);
+ bool use_pretty_covers() const { return use_pretty_covers_; }
+
+ static QString TextOrUnknown(const QString &text);
+ static QString SortText(QString text);
+ static QString SortTextForArtist(QString artist);
+ static QString SortTextForSong(const Song &song);
+
+ void Reset();
+ void AddSongs(const SongList &songs);
+
+ protected:
+ void LazyPopulate(CollectionItem *item) { LazyPopulate(item, true); }
+ void LazyPopulate(CollectionItem *item, bool signal);
+
+ private slots:
+ void AlbumArtLoaded(quint64 id, const QImage &image);
+
+ private:
+ QueryResult RunQuery(CollectionItem *parent);
+ void PostQuery(CollectionItem *parent, const QueryResult &result, bool signal);
+ CollectionItem *ItemFromSong(CollectionItem::Type item_type, bool signal, CollectionItem *parent, const Song &s, int container_level);
+
+ QString AlbumIconPixmapCacheKey(const QModelIndex &index) const;
+ QVariant AlbumIcon(const QModelIndex &index);
+ QVariant data(const CollectionItem *item, int role) const;
+ bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
+
+ private:
+ CollectionBackend *backend_;
+ Application *app_;
+ QueryOptions query_options_;
+ QMap song_nodes_;
+ QMap container_nodes_;
+ QIcon artist_icon_;
+ QIcon album_icon_;
+ QPixmap no_cover_icon_;
+ QIcon playlists_dir_icon_;
+ QIcon playlist_icon_;
+ QNetworkDiskCache *icon_cache_;
+ bool use_pretty_covers_;
+ AlbumCoverLoaderOptions cover_loader_options_;
+ typedef QPair ItemAndCacheKey;
+ QMap pending_art_;
+ QSet pending_cache_keys_;
+};
+
+#endif // CONTEXTALBUMSMODEL_H
diff --git a/src/context/contextalbumsview.cpp b/src/context/contextalbumsview.cpp
new file mode 100644
index 000000000..fad2349e7
--- /dev/null
+++ b/src/context/contextalbumsview.cpp
@@ -0,0 +1,532 @@
+/*
+ * Strawberry Music Player
+ * This code was part of Clementine.
+ * Copyright 2010, David Sansome
+ * Copyright 2013, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#include "config.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/application.h"
+#include "core/iconloader.h"
+#include "core/mimedata.h"
+#include "core/utilities.h"
+#include "collection/collectionbackend.h"
+#include "collection/collectiondirectorymodel.h"
+#include "collection/collectionitem.h"
+#include "device/devicemanager.h"
+#include "device/devicestatefiltermodel.h"
+#include "dialogs/edittagdialog.h"
+#ifdef HAVE_GSTREAMER
+#include "dialogs/organisedialog.h"
+#endif
+#include "settings/collectionsettingspage.h"
+
+#include "contextview.h"
+#include "contextalbumsmodel.h"
+#include "contextalbumsview.h"
+
+ContextItemDelegate::ContextItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
+
+void ContextItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const {
+
+ const bool is_divider = index.data(ContextAlbumsModel::Role_IsDivider).toBool();
+
+ if (is_divider) {
+ QString text(index.data().toString());
+
+ painter->save();
+
+ QRect text_rect(opt.rect);
+
+ // Does this item have an icon?
+ QPixmap pixmap;
+ QVariant decoration = index.data(Qt::DecorationRole);
+ if (!decoration.isNull()) {
+ if (decoration.canConvert()) {
+ pixmap = decoration.value();
+ }
+ else if (decoration.canConvert()) {
+ pixmap = decoration.value().pixmap(opt.decorationSize);
+ }
+ }
+
+ // Draw the icon at the left of the text rectangle
+ if (!pixmap.isNull()) {
+ QRect icon_rect(text_rect.topLeft(), opt.decorationSize);
+ const int padding = (text_rect.height() - icon_rect.height()) / 2;
+ icon_rect.adjust(padding, padding, padding, padding);
+ text_rect.moveLeft(icon_rect.right() + padding + 6);
+
+ if (pixmap.size() != opt.decorationSize) {
+ pixmap = pixmap.scaled(opt.decorationSize, Qt::KeepAspectRatio);
+ }
+
+ painter->drawPixmap(icon_rect, pixmap);
+ }
+ else {
+ text_rect.setLeft(text_rect.left() + 30);
+ }
+
+ // Draw the text
+ QFont bold_font(opt.font);
+ bold_font.setBold(true);
+
+ painter->setPen(opt.palette.color(QPalette::Text));
+ painter->setFont(bold_font);
+ painter->drawText(text_rect, text);
+
+ // Draw the line under the item
+ QColor line_color = opt.palette.color(QPalette::Text);
+ QLinearGradient grad_color(opt.rect.bottomLeft(), opt.rect.bottomRight());
+ const double fade_start_end = (opt.rect.width()/3.0)/opt.rect.width();
+ line_color.setAlphaF(0.0);
+ grad_color.setColorAt(0, line_color);
+ line_color.setAlphaF(0.5);
+ grad_color.setColorAt(fade_start_end, line_color);
+ grad_color.setColorAt(1.0 - fade_start_end, line_color);
+ line_color.setAlphaF(0.0);
+ grad_color.setColorAt(1, line_color);
+ painter->setPen(QPen(grad_color, 1));
+ painter->drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight());
+
+ painter->restore();
+ }
+ else {
+ if (!is_divider) QStyledItemDelegate::paint(painter, opt, index);
+ }
+
+}
+
+bool ContextItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) {
+
+ return true;
+
+ Q_UNUSED(option);
+
+ if (!event || !view) return false;
+
+ QHelpEvent *he = static_cast(event);
+ QString text = displayText(index.data(), QLocale::system());
+
+ if (text.isEmpty() || !he) return false;
+
+ switch (event->type()) {
+ case QEvent::ToolTip: {
+ QRect displayed_text;
+ QSize real_text;
+ bool is_elided = false;
+
+ real_text = sizeHint(option, index);
+ displayed_text = view->visualRect(index);
+ is_elided = displayed_text.width() < real_text.width();
+
+ if (is_elided) {
+ QToolTip::showText(he->globalPos(), text, view);
+ }
+ else if (index.data(Qt::ToolTipRole).isValid()) {
+ // If the item has a tooltip text, display it
+ QString tooltip_text = index.data(Qt::ToolTipRole).toString();
+ QToolTip::showText(he->globalPos(), tooltip_text, view);
+ }
+ else {
+ // in case that another text was previously displayed
+ QToolTip::hideText();
+ }
+ return true;
+ }
+
+ case QEvent::QueryWhatsThis:
+ return true;
+
+ case QEvent::WhatsThis:
+ QWhatsThis::showText(he->globalPos(), text, view);
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+
+}
+
+ContextAlbumsView::ContextAlbumsView(QWidget *parent)
+ : AutoExpandingTreeView(parent),
+ app_(nullptr),
+ context_menu_(nullptr),
+ is_in_keyboard_search_(false),
+ model_(nullptr)
+ {
+
+ setStyleSheet("border: none;");
+
+ setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
+ setItemDelegate(new ContextItemDelegate(this));
+ setAttribute(Qt::WA_MacShowFocusRect, false);
+ setHeaderHidden(true);
+ setAllColumnsShowFocus(true);
+ setDragEnabled(true);
+ setDragDropMode(QAbstractItemView::DragOnly);
+ setSelectionMode(QAbstractItemView::ExtendedSelection);
+ SetAddOnDoubleClick(false);
+
+}
+
+ContextAlbumsView::~ContextAlbumsView() {}
+
+void ContextAlbumsView::SaveFocus() {
+
+ QModelIndex current = currentIndex();
+ QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
+ if (!type.isValid() || !(type.toInt() == CollectionItem::Type_Song || type.toInt() == CollectionItem::Type_Container || type.toInt() == CollectionItem::Type_Divider)) {
+ return;
+ }
+
+ last_selected_path_.clear();
+ last_selected_song_ = Song();
+ last_selected_container_ = QString();
+
+ switch (type.toInt()) {
+ case CollectionItem::Type_Song: {
+ QModelIndex index = current;
+ SongList songs = model_->GetChildSongs(index);
+ if (!songs.isEmpty()) {
+ last_selected_song_ = songs.last();
+ }
+ break;
+ }
+
+ case CollectionItem::Type_Container:
+ case CollectionItem::Type_Divider: {
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ SaveContainerPath(current);
+
+}
+
+void ContextAlbumsView::SaveContainerPath(const QModelIndex &child) {
+
+
+// return;
+
+ QModelIndex current = model()->parent(child);
+ QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
+ if (!type.isValid() || !(type.toInt() == CollectionItem::Type_Container || type.toInt() == CollectionItem::Type_Divider)) {
+ return;
+ }
+
+ QString text = model()->data(current, ContextAlbumsModel::Role_SortText).toString();
+ last_selected_path_ << text;
+ SaveContainerPath(current);
+
+}
+
+void ContextAlbumsView::RestoreFocus() {
+
+ if (last_selected_container_.isEmpty() && last_selected_song_.url().isEmpty()) {
+ return;
+ }
+ RestoreLevelFocus();
+
+}
+
+bool ContextAlbumsView::RestoreLevelFocus(const QModelIndex &parent) {
+
+ if (model()->canFetchMore(parent)) {
+ model()->fetchMore(parent);
+ }
+ int rows = model()->rowCount(parent);
+ for (int i = 0; i < rows; i++) {
+ QModelIndex current = model()->index(i, 0, parent);
+ QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
+ switch (type.toInt()) {
+ case CollectionItem::Type_Song:
+ if (!last_selected_song_.url().isEmpty()) {
+ QModelIndex index = qobject_cast(model())->mapToSource(current);
+ SongList songs = model_->GetChildSongs(index);
+ for (const Song &song : songs) {
+ if (song == last_selected_song_) {
+ setCurrentIndex(current);
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return false;
+
+}
+
+void ContextAlbumsView::ReloadSettings() {
+
+ QSettings settings;
+
+ settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
+ SetAutoOpen(settings.value("auto_open", true).toBool());
+
+ if (app_ && model_) {
+ model_->set_pretty_covers(settings.value("pretty_covers", true).toBool());
+ }
+
+ settings.endGroup();
+
+}
+
+void ContextAlbumsView::SetApplication(Application *app) {
+
+ app_ = app;
+
+ model_ = new ContextAlbumsModel(app_->collection_backend(), app_, this);
+ model_->Reset();
+
+ setModel(model_);
+
+ connect(model_, SIGNAL(modelAboutToBeReset()), this, SLOT(SaveFocus()));
+ connect(model_, SIGNAL(modelReset()), this, SLOT(RestoreFocus()));
+
+ ReloadSettings();
+
+}
+
+void ContextAlbumsView::paintEvent(QPaintEvent *event) {
+ QTreeView::paintEvent(event);
+}
+
+void ContextAlbumsView::mouseReleaseEvent(QMouseEvent *e) {
+ QTreeView::mouseReleaseEvent(e);
+}
+
+void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
+
+ if (!context_menu_) {
+ context_menu_ = new QMenu(this);
+ //context_menu_->setStyleSheet("background-color: #3DADE8;");
+
+ add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
+ load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
+ open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenInNewPlaylist()));
+
+ context_menu_->addSeparator();
+ add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue track"), this, SLOT(AddToPlaylistEnqueue()));
+
+#ifdef HAVE_GSTREAMER
+ context_menu_->addSeparator();
+ organise_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organise files..."), this, SLOT(Organise()));
+ copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, SLOT(CopyToDevice()));
+#endif
+
+ context_menu_->addSeparator();
+ edit_track_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit track information..."), this, SLOT(EditTracks()));
+ edit_tracks_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit tracks information..."), this, SLOT(EditTracks()));
+ show_in_browser_ = context_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, SLOT(ShowInBrowser()));
+
+ context_menu_->addSeparator();
+
+#ifdef HAVE_GSTREAMER
+ copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
+ connect(app_->device_manager()->connected_devices_model(), SIGNAL(IsEmptyChanged(bool)), copy_to_device_, SLOT(setDisabled(bool)));
+#endif
+
+ }
+
+ context_menu_index_ = indexAt(e->pos());
+ if (!context_menu_index_.isValid()) return;
+ QModelIndexList selected_indexes = selectionModel()->selectedRows();
+
+ int regular_elements = 0;
+ int regular_editable = 0;
+
+ for (const QModelIndex &index : selected_indexes) {
+ regular_elements++;
+ if(model_->data(index, ContextAlbumsModel::Role_Editable).toBool()) {
+ regular_editable++;
+ }
+ }
+
+ // TODO: check if custom plugin actions should be enabled / visible
+ const int songs_selected = regular_elements;
+ const bool regular_elements_only = songs_selected == regular_elements && regular_elements > 0;
+
+ // in all modes
+ load_->setEnabled(songs_selected);
+ add_to_playlist_->setEnabled(songs_selected);
+ open_in_new_playlist_->setEnabled(songs_selected);
+ add_to_playlist_enqueue_->setEnabled(songs_selected);
+
+ // if neither edit_track not edit_tracks are available, we show disabled edit_track element
+ edit_track_->setVisible(regular_editable <= 1);
+ edit_track_->setEnabled(regular_editable == 1);
+
+#ifdef HAVE_GSTREAMER
+ organise_->setVisible(regular_elements_only);
+ copy_to_device_->setVisible(regular_elements_only);
+#endif
+
+ // only when all selected items are editable
+#ifdef HAVE_GSTREAMER
+ organise_->setEnabled(regular_elements == regular_editable);
+ copy_to_device_->setEnabled(regular_elements == regular_editable);
+#endif
+
+ context_menu_->popup(e->globalPos());
+
+}
+
+void ContextAlbumsView::Load() {
+
+ QMimeData *data = model()->mimeData(selectedIndexes());
+ if (MimeData *mime_data = qobject_cast(data)) {
+ mime_data->clear_first_ = true;
+ }
+ emit AddToPlaylistSignal(data);
+
+}
+
+void ContextAlbumsView::AddToPlaylist() {
+
+ emit AddToPlaylistSignal(model()->mimeData(selectedIndexes()));
+
+}
+
+void ContextAlbumsView::AddToPlaylistEnqueue() {
+
+ QMimeData *data = model()->mimeData(selectedIndexes());
+ if (MimeData* mime_data = qobject_cast(data)) {
+ mime_data->enqueue_now_ = true;
+ }
+ emit AddToPlaylistSignal(data);
+
+}
+
+void ContextAlbumsView::OpenInNewPlaylist() {
+
+ QMimeData *data = model()->mimeData(selectedIndexes());
+ if (MimeData* mime_data = qobject_cast(data)) {
+ mime_data->open_in_new_playlist_ = true;
+ }
+ emit AddToPlaylistSignal(data);
+
+}
+
+void ContextAlbumsView::scrollTo(const QModelIndex &index, ScrollHint hint) {
+
+ if (is_in_keyboard_search_)
+ QTreeView::scrollTo(index, QAbstractItemView::PositionAtTop);
+ else
+ QTreeView::scrollTo(index, hint);
+
+}
+
+SongList ContextAlbumsView::GetSelectedSongs() const {
+ QModelIndexList selected_indexes = selectionModel()->selectedRows();
+ return model_->GetChildSongs(selected_indexes);
+}
+
+#ifdef HAVE_GSTREAMER
+void ContextAlbumsView::Organise() {
+
+ if (!organise_dialog_)
+ organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
+
+ organise_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
+ organise_dialog_->SetCopy(false);
+ if (organise_dialog_->SetSongs(GetSelectedSongs()))
+ organise_dialog_->show();
+ else {
+ QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
+ }
+}
+#endif
+
+void ContextAlbumsView::EditTracks() {
+
+ if (!edit_tag_dialog_) {
+ edit_tag_dialog_.reset(new EditTagDialog(app_, this));
+ }
+ edit_tag_dialog_->SetSongs(GetSelectedSongs());
+ edit_tag_dialog_->show();
+
+}
+
+#ifdef HAVE_GSTREAMER
+void ContextAlbumsView::CopyToDevice() {
+
+ if (!organise_dialog_)
+ organise_dialog_.reset(new OrganiseDialog(app_->task_manager()));
+
+ organise_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
+ organise_dialog_->SetCopy(true);
+ organise_dialog_->SetSongs(GetSelectedSongs());
+ organise_dialog_->show();
+
+}
+#endif
+
+void ContextAlbumsView::ShowInBrowser() {
+
+ QList urls;
+ for (const Song &song : GetSelectedSongs()) {
+ urls << song.url();
+ }
+
+ Utilities::OpenInFileBrowser(urls);
+}
diff --git a/src/context/contextalbumsview.h b/src/context/contextalbumsview.h
new file mode 100644
index 000000000..af25671c4
--- /dev/null
+++ b/src/context/contextalbumsview.h
@@ -0,0 +1,151 @@
+/*
+ * Strawberry Music Player
+ * This code was part of Clementine.
+ * Copyright 2010, David Sansome
+ * Copyright 2013, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#ifndef CONTEXTALBUMSVIEW_H
+#define CONTEXTALBUMSVIEW_H
+
+#include "config.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/song.h"
+#include "widgets/autoexpandingtreeview.h"
+
+class Application;
+class EditTagDialog;
+#ifdef HAVE_GSTREAMER
+class OrganiseDialog;
+#endif
+class ContextAlbumsModel;
+
+class ContextItemDelegate : public QStyledItemDelegate {
+ Q_OBJECT
+
+ public:
+ ContextItemDelegate(QObject *parent);
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+ public slots:
+ bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index);
+};
+
+class ContextAlbumsView : public AutoExpandingTreeView {
+ Q_OBJECT
+
+ public:
+ ContextAlbumsView(QWidget *parent = nullptr);
+ ~ContextAlbumsView();
+
+ // Returns Songs currently selected in the collection view.
+ // Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs.
+ SongList GetSelectedSongs() const;
+
+ void SetApplication(Application *app);
+
+ // QTreeView
+ void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
+
+ ContextAlbumsModel *albums_model() { return model_; }
+
+ public slots:
+ void ReloadSettings();
+
+ void SaveFocus();
+ void RestoreFocus();
+
+signals:
+ void ShowConfigDialog();
+
+ protected:
+ // QWidget
+ void paintEvent(QPaintEvent *event);
+ void mouseReleaseEvent(QMouseEvent *e);
+ void contextMenuEvent(QContextMenuEvent *e);
+
+ private slots:
+ void Load();
+ void AddToPlaylist();
+ void AddToPlaylistEnqueue();
+ void OpenInNewPlaylist();
+#ifdef HAVE_GSTREAMER
+ void Organise();
+ void CopyToDevice();
+#endif
+ void EditTracks();
+ void ShowInBrowser();
+
+ private:
+ void RecheckIsEmpty();
+ bool RestoreLevelFocus(const QModelIndex &parent = QModelIndex());
+ void SaveContainerPath(const QModelIndex &child);
+
+ private:
+ Application *app_;
+
+ QMenu *context_menu_;
+ QModelIndex context_menu_index_;
+ QAction *load_;
+ QAction *add_to_playlist_;
+ QAction *add_to_playlist_enqueue_;
+ QAction *open_in_new_playlist_;
+#ifdef HAVE_GSTREAMER
+ QAction *organise_;
+ QAction *copy_to_device_;
+#endif
+ QAction *delete_;
+ QAction *edit_track_;
+ QAction *edit_tracks_;
+ QAction *show_in_browser_;
+
+#ifdef HAVE_GSTREAMER
+ std::unique_ptr organise_dialog_;
+#endif
+ std::unique_ptr edit_tag_dialog_;
+
+ bool is_in_keyboard_search_;
+
+ // Save focus
+ Song last_selected_song_;
+ QString last_selected_container_;
+ QSet last_selected_path_;
+
+ ContextAlbumsModel *model_;
+
+};
+
+#endif // CONTEXTALBUMSVIEW_H
+
diff --git a/src/context/contextview.cpp b/src/context/contextview.cpp
new file mode 100644
index 000000000..f3850d42e
--- /dev/null
+++ b/src/context/contextview.cpp
@@ -0,0 +1,654 @@
+/*
+ * Strawberry Music Player
+ * Copyright 2013, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/application.h"
+#include "core/logging.h"
+#include "core/player.h"
+#include "core/song.h"
+#include "core/utilities.h"
+#include "core/iconloader.h"
+#include "engine/engine_fwd.h"
+#include "engine/enginebase.h"
+#include "engine/enginetype.h"
+#include "engine/enginedevice.h"
+#include "engine/devicefinder.h"
+#include "collection/collection.h"
+#include "collection/collectionbackend.h"
+#include "collection/collectionquery.h"
+#include "collection/collectionmodel.h"
+#include "collection/collectionview.h"
+#include "covermanager/albumcoverchoicecontroller.h"
+#include "covermanager/albumcoverloader.h"
+#include "covermanager/currentartloader.h"
+#include "lyrics/lyricsfetcher.h"
+
+#include "contextview.h"
+#include "contextalbumsmodel.h"
+#include "ui_contextviewcontainer.h"
+
+using std::unique_ptr;
+
+const char *ContextView::kSettingsGroup = "ContextView";
+
+ContextView::ContextView(QWidget *parent) :
+ QWidget(parent),
+ app_(nullptr),
+ ui_(new Ui_ContextViewContainer),
+ collectionview_(nullptr),
+ menu_(new QMenu(this)),
+ timeline_fade_(new QTimeLine(1000, this)),
+ image_strawberry_(":/pictures/strawberry.png"),
+ album_cover_choice_controller_(new AlbumCoverChoiceController(this)),
+ lyrics_fetcher_(nullptr),
+ active_(false),
+ downloading_covers_(false)
+ {
+
+ ui_->setupUi(this);
+ ui_->widget_stacked->setCurrentWidget(ui_->widget_stop);
+ ui_->label_play_album->installEventFilter(this);
+
+ QFontDatabase::addApplicationFont(":/fonts/HumongousofEternitySt.ttf");
+
+ connect(timeline_fade_, SIGNAL(valueChanged(qreal)), SLOT(FadePreviousTrack(qreal)));
+ timeline_fade_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0
+
+ cover_loader_options_.desired_height_ = 300;
+ cover_loader_options_.pad_output_image_ = true;
+ cover_loader_options_.scale_output_image_ = true;
+ pixmap_current_ = QPixmap::fromImage(AlbumCoverLoader::ScaleAndPad(cover_loader_options_, image_strawberry_));
+
+ AddActions();
+ LoadSettings();
+
+}
+
+ContextView::~ContextView() { delete ui_; }
+
+void ContextView::LoadSettings() {
+
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+
+ action_show_data_->setChecked(s.value("show_data", true).toBool());
+ action_show_output_->setChecked(s.value("show_output", true).toBool());
+ action_show_albums_->setChecked(s.value("show_albums", true).toBool());
+ action_show_lyrics_->setChecked(s.value("show_lyrics", false).toBool());
+ album_cover_choice_controller_->search_cover_auto_action()->setChecked(s.value("search_for_cover_auto", true).toBool());
+
+ s.endGroup();
+
+}
+
+void ContextView::SetApplication(Application *app) {
+
+ app_ = app;
+
+ connect(app_->current_art_loader(), SIGNAL(ArtLoaded(Song, QString, QImage)), SLOT(AlbumArtLoaded(Song, QString, QImage)));
+
+ album_cover_choice_controller_->SetApplication(app_);
+ connect(album_cover_choice_controller_, SIGNAL(AutomaticCoverSearchDone()), this, SLOT(AutomaticCoverSearchDone()));
+ connect(album_cover_choice_controller_->cover_from_file_action(), SIGNAL(triggered()), this, SLOT(LoadCoverFromFile()));
+ connect(album_cover_choice_controller_->cover_to_file_action(), SIGNAL(triggered()), this, SLOT(SaveCoverToFile()));
+ connect(album_cover_choice_controller_->cover_from_url_action(), SIGNAL(triggered()), this, SLOT(LoadCoverFromURL()));
+ connect(album_cover_choice_controller_->search_for_cover_action(), SIGNAL(triggered()), this, SLOT(SearchForCover()));
+ connect(album_cover_choice_controller_->unset_cover_action(), SIGNAL(triggered()), this, SLOT(UnsetCover()));
+ connect(album_cover_choice_controller_->show_cover_action(), SIGNAL(triggered()), this, SLOT(ShowCover()));
+ connect(album_cover_choice_controller_->search_cover_auto_action(), SIGNAL(triggered()), this, SLOT(SearchCoverAutomatically()));
+
+ ui_->widget_play_albums->SetApplication(app_);
+
+ lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this);
+ connect(lyrics_fetcher_, SIGNAL(LyricsFetched(quint64, const QString)), this, SLOT(UpdateLyrics(quint64, const QString)));
+
+}
+
+void ContextView::SetCollectionView(CollectionView *collectionview) {
+ collectionview_ = collectionview;
+ connect(collectionview_, SIGNAL(TotalSongCountUpdated_()), this, SLOT(UpdateNoSong()));
+ connect(collectionview_, SIGNAL(TotalArtistCountUpdated_()), this, SLOT(UpdateNoSong()));
+ connect(collectionview_, SIGNAL(TotalAlbumCountUpdated_()), this, SLOT(UpdateNoSong()));
+}
+
+void ContextView::AddActions() {
+
+ action_show_data_ = new QAction(tr("Show song technical data"), this);
+ action_show_data_->setCheckable(true);
+ action_show_data_->setChecked(true);
+
+ action_show_output_ = new QAction(tr("Show engine and device"), this);
+ action_show_output_->setCheckable(true);
+ action_show_output_->setChecked(true);
+
+ action_show_albums_ = new QAction(tr("Show albums by artist"), this);
+ action_show_albums_->setCheckable(true);
+ action_show_albums_->setChecked(true);
+
+ action_show_lyrics_ = new QAction(tr("Show song lyrics"), this);
+ action_show_lyrics_->setCheckable(true);
+ action_show_lyrics_->setChecked(false);
+
+ menu_->addAction(action_show_data_);
+ menu_->addAction(action_show_output_);
+ menu_->addAction(action_show_albums_);
+ menu_->addAction(action_show_lyrics_);
+ menu_->addSeparator();
+
+ connect(action_show_data_, SIGNAL(triggered()), this, SLOT(ActionShowData()));
+ connect(action_show_output_, SIGNAL(triggered()), this, SLOT(ActionShowOutput()));
+ connect(action_show_albums_, SIGNAL(triggered()), this, SLOT(ActionShowAlbums()));
+ connect(action_show_lyrics_, SIGNAL(triggered()), this, SLOT(ActionShowLyrics()));
+
+ QList cover_actions = album_cover_choice_controller_->GetAllActions();
+ cover_actions.append(album_cover_choice_controller_->search_cover_auto_action());
+ menu_->addActions(cover_actions);
+ menu_->addSeparator();
+
+}
+
+void ContextView::Playing() {
+}
+
+void ContextView::Stopped() {
+
+ active_ = false;
+ song_ = song_empty_;
+ downloading_covers_ = false;
+ prev_artist_ = QString();
+ lyrics_ = QString();
+ SetImage(image_strawberry_);
+
+}
+
+void ContextView::Error() {
+}
+
+void ContextView::UpdateNoSong() {
+ NoSong();
+}
+
+void ContextView::SongChanged(const Song &song) {
+
+ image_previous_ = image_original_;
+ prev_artist_ = song_.artist();
+ lyrics_ = QString();
+ song_ = song;
+ UpdateSong();
+ update();
+ if (action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
+
+}
+
+void ContextView::SetText(QLabel *label, int value, const QString &suffix, const QString &def) {
+ label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
+}
+
+void ContextView::NoSong() {
+
+ ui_->label_stop_top->setStyleSheet(
+ "font: 20pt \"Humongous of Eternity St\";"
+ "font-weight: Regular;"
+ );
+
+ ui_->label_stop_top->setText("No song playing");
+
+ QString html = QString(
+ "%1 songs
\n"
+ "%2 artists
\n"
+ "%3 albums
\n"
+ )
+ .arg(collectionview_->TotalSongs())
+ .arg(collectionview_->TotalArtists())
+ .arg(collectionview_->TotalAlbums())
+ ;
+
+ ui_->label_stop_summary->setStyleSheet(
+ "font: 12pt;"
+ "font-weight: regular;"
+ );
+ ui_->label_stop_summary->setText(html);
+
+}
+
+void ContextView::UpdateSong() {
+
+ QList labels_play_data;
+
+ labels_play_data << ui_->label_filetype
+ << ui_->filetype
+ << ui_->label_length
+ << ui_->length
+ << ui_->label_samplerate
+ << ui_->samplerate
+ << ui_->label_bitdepth
+ << ui_->bitdepth
+ << ui_->label_bitrate
+ << ui_->bitrate;
+
+ ui_->label_play_top->setStyleSheet(
+ "font: 11pt;"
+ "font-weight: regular;"
+ );
+ ui_->label_play_top->setText( QString("%1 - %2
%3").arg(song_.PrettyTitle().toHtmlEscaped(), song_.artist().toHtmlEscaped(), song_.album().toHtmlEscaped()));
+
+ if (action_show_data_->isChecked()) {
+ for (QLabel *l : labels_play_data) {
+ l->setEnabled(true);
+ l->setVisible(true);
+ l->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ }
+ ui_->layout_play_data->setEnabled(true);
+ ui_->filetype->setText(song_.TextForFiletype());
+ ui_->length->setText(Utilities::PrettyTimeNanosec(song_.length_nanosec()));
+ SetText(ui_->samplerate, song_.samplerate(), "Hz");
+ SetText(ui_->bitdepth, song_.bitdepth(), "Bit");
+ SetText(ui_->bitrate, song_.bitrate(), tr("kbps"));
+ ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed);
+ }
+ else {
+ for (QLabel *l : labels_play_data) {
+ l->setEnabled(false);
+ l->setVisible(false);
+ l->setMaximumSize(0, 0);
+ }
+ ui_->layout_play_data->setEnabled(false);
+ ui_->filetype->clear();
+ ui_->length->clear();
+ ui_->samplerate->clear();
+ ui_->bitdepth->clear();
+ ui_->bitrate->clear();
+ ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed);
+ }
+
+ if (action_show_output_->isChecked()) {
+ Engine::EngineType enginetype(Engine::None);
+ if (app_->player()->engine()) enginetype = app_->player()->engine()->type();
+ QIcon icon_engine = IconLoader::Load(EngineName(enginetype), 32);
+
+ ui_->label_engine->setVisible(true);
+ ui_->label_engine->setMaximumSize(60, QWIDGETSIZE_MAX);
+ ui_->label_engine_icon->setVisible(true);
+ ui_->label_engine_icon->setPixmap(icon_engine.pixmap(icon_engine.availableSizes().last()));
+ ui_->label_engine_icon->setMinimumSize(32, 32);
+ ui_->label_engine_icon->setMaximumSize(32, 32);
+ ui_->engine->setVisible(true);
+ ui_->engine->setText(EngineDescription(enginetype));
+ ui_->engine->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ ui_->spacer_play_output->changeSize(20, 20, QSizePolicy::Fixed);
+
+ DeviceFinder::Device device;
+ for (DeviceFinder *f : app_->enginedevice()->device_finders_) {
+ for (const DeviceFinder::Device &d : f->ListDevices()) {
+ if (d.value != app_->player()->engine()->device()) continue;
+ device = d;
+ break;
+ }
+ }
+ if (device.value.isValid()) {
+ ui_->label_device->setVisible(true);
+ ui_->label_device->setMaximumSize(60, QWIDGETSIZE_MAX);
+ ui_->label_device_icon->setVisible(true);
+ ui_->label_device_icon->setMinimumSize(32, 32);
+ ui_->label_device_icon->setMaximumSize(32, 32);
+ ui_->device->setVisible(true);
+ ui_->device->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+ QIcon icon_device = IconLoader::Load(device.iconname, 32);
+ ui_->label_device_icon->setPixmap(icon_device.pixmap(icon_device.availableSizes().last()));
+ ui_->device->setText(device.description);
+ }
+ else {
+ ui_->label_device->setVisible(false);
+ ui_->label_device->setMaximumSize(0, 0);
+ ui_->label_device_icon->setVisible(false);
+ ui_->label_device_icon->setMinimumSize(0, 0);
+ ui_->label_device_icon->setMaximumSize(0, 0);
+ ui_->label_device_icon->clear();
+ ui_->device->clear();
+ ui_->device->setVisible(false);
+ ui_->device->setMaximumSize(0, 0);
+ }
+ }
+ else {
+ ui_->label_engine->setVisible(false);
+ ui_->label_engine->setMaximumSize(0, 0);
+ ui_->label_engine_icon->clear();
+ ui_->label_engine_icon->setVisible(false);
+ ui_->label_engine_icon->setMinimumSize(0, 0);
+ ui_->label_engine_icon->setMaximumSize(0, 0);
+ ui_->engine->clear();
+ ui_->engine->setVisible(false);
+ ui_->engine->setMaximumSize(0, 0);
+ ui_->spacer_play_output->changeSize(0, 0, QSizePolicy::Fixed);
+ ui_->label_device->setVisible(false);
+ ui_->label_device->setMaximumSize(0, 0);
+ ui_->label_device_icon->setVisible(false);
+ ui_->label_device_icon->setMinimumSize(0, 0);
+ ui_->label_device_icon->setMaximumSize(0, 0);
+ ui_->label_device_icon->clear();
+ ui_->device->clear();
+ ui_->device->setVisible(false);
+ ui_->device->setMaximumSize(0, 0);
+ }
+
+ if (action_show_albums_->isChecked() && prev_artist_ != song_.artist()) {
+ const QueryOptions opt;
+ CollectionBackend::AlbumList albumlist;
+ ui_->widget_play_albums->albums_model()->Reset();
+ albumlist = app_->collection_backend()->GetAlbumsByArtist(song_.artist(), opt);
+ if (albumlist.count() > 1) {
+ ui_->label_play_albums->setVisible(true);
+ ui_->label_play_albums->setMinimumSize(0, 20);
+ ui_->label_play_albums->setText(QString("Albums by %1").arg( song_.artist().toHtmlEscaped()));
+ ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
+ for (CollectionBackend::Album album : albumlist) {
+ SongList songs = app_->collection_backend()->GetSongs(song_.artist(), album.album_name, opt);
+ ui_->widget_play_albums->albums_model()->AddSongs(songs);
+ }
+ ui_->widget_play_albums->setEnabled(true);
+ ui_->widget_play_albums->setVisible(true);
+ ui_->spacer_play_albums1->changeSize(20, 10, QSizePolicy::Fixed);
+ ui_->spacer_play_albums2->changeSize(20, 20, QSizePolicy::Fixed);
+ }
+ else {
+ ui_->label_play_albums->clear();
+ ui_->label_play_albums->setVisible(false);
+ ui_->label_play_albums->setMinimumSize(0, 0);
+ ui_->widget_play_albums->setEnabled(false);
+ ui_->widget_play_albums->setVisible(false);
+ ui_->spacer_play_albums1->changeSize(0, 0, QSizePolicy::Fixed);
+ ui_->spacer_play_albums2->changeSize(0, 0, QSizePolicy::Fixed);
+ }
+ }
+ else if (!action_show_albums_->isChecked()) {
+ ui_->label_play_albums->clear();
+ ui_->label_play_albums->setVisible(false);
+ ui_->label_play_albums->setMinimumSize(0, 0);
+ ui_->widget_play_albums->albums_model()->Reset();
+ ui_->widget_play_albums->setEnabled(false);
+ ui_->widget_play_albums->setVisible(false);
+ ui_->spacer_play_albums1->changeSize(0, 0, QSizePolicy::Fixed);
+ ui_->spacer_play_albums2->changeSize(0, 0, QSizePolicy::Fixed);
+ }
+
+ if (action_show_lyrics_->isChecked()) {
+ ui_->label_play_lyrics->setText(lyrics_);
+ }
+ else {
+ ui_->label_play_lyrics->clear();
+ }
+
+ ui_->widget_stacked->setCurrentWidget(ui_->widget_play);
+
+}
+
+void ContextView::UpdateLyrics(quint64 id, const QString lyrics) {
+
+ lyrics_ = lyrics;
+ if (action_show_lyrics_->isChecked()) {
+ ui_->label_play_lyrics->setText(lyrics);
+ }
+ else ui_->label_play_lyrics->clear();
+
+}
+
+bool ContextView::eventFilter(QObject *object, QEvent *event) {
+
+ switch(event->type()) {
+ case QEvent::Paint:{
+ handlePaintEvent(object, event);
+ }
+ default:{
+ return QObject::eventFilter(object, event);
+ }
+ }
+
+ return(true);
+
+}
+
+void ContextView::handlePaintEvent(QObject *object, QEvent *event) {
+
+ if (object == ui_->label_play_album) {
+ PaintEventAlbum(event);
+ }
+
+ return;
+
+}
+
+void ContextView::PaintEventAlbum(QEvent *event) {
+
+ QPainter p(ui_->label_play_album);
+
+ DrawImage(&p);
+
+ // Draw the previous track's image if we're fading
+ if (!pixmap_previous_.isNull()) {
+ p.setOpacity(pixmap_previous_opacity_);
+ p.drawPixmap(0, 0, pixmap_previous_);
+ }
+}
+
+void ContextView::DrawImage(QPainter *p) {
+
+ p->drawPixmap(0, 0, 300, 300, pixmap_current_);
+ if ((downloading_covers_) && (spinner_animation_)) {
+ p->drawPixmap(50, 50, 16, 16, spinner_animation_->currentPixmap());
+ }
+
+}
+
+void ContextView::FadePreviousTrack(qreal value) {
+
+ pixmap_previous_opacity_ = value;
+ if (qFuzzyCompare(pixmap_previous_opacity_, qreal(0.0))) {
+ image_previous_ = QImage();
+ pixmap_previous_ = QPixmap();
+ }
+ update();
+
+ if (value == 0 && !active_) {
+ ui_->widget_stacked->setCurrentWidget(ui_->widget_stop);
+ NoSong();
+ }
+
+}
+
+void ContextView::contextMenuEvent(QContextMenuEvent *e) {
+ if (menu_ && ui_->widget_stacked->currentWidget() == ui_->widget_play) menu_->popup(mapToGlobal(e->pos()));
+}
+
+void ContextView::mouseReleaseEvent(QMouseEvent *) {
+}
+
+void ContextView::dragEnterEvent(QDragEnterEvent *e) {
+ QWidget::dragEnterEvent(e);
+}
+
+void ContextView::dropEvent(QDropEvent *e) {
+ QWidget::dropEvent(e);
+}
+
+void ContextView::ScaleCover() {
+
+ pixmap_current_ = QPixmap::fromImage(AlbumCoverLoader::ScaleAndPad(cover_loader_options_, image_original_));
+ update();
+
+}
+
+void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) {
+
+ if (song == song_) {}
+ else {
+ qLog(Error) << __PRETTY_FUNCTION__ << "Ignoring" << song.title() << "because current song is" << song_.title();
+ return;
+ }
+
+ active_ = true;
+ downloading_covers_ = false;
+ SetImage(image);
+ GetCoverAutomatically();
+
+}
+
+void ContextView::SetImage(const QImage &image) {
+
+ // Cache the current pixmap so we can fade between them
+ pixmap_previous_ = QPixmap(size());
+ pixmap_previous_.fill(palette().background().color());
+ pixmap_previous_opacity_ = 1.0;
+
+ QPainter p(&pixmap_previous_);
+ DrawImage(&p);
+ p.end();
+
+ image_original_ = image;
+
+ ScaleCover();
+
+ // Were we waiting for this cover to load before we started fading?
+ if (!pixmap_previous_.isNull() && timeline_fade_) {
+ timeline_fade_->stop();
+ timeline_fade_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0
+ timeline_fade_->start();
+ }
+
+}
+
+bool ContextView::GetCoverAutomatically() {
+
+ // Search for cover automatically?
+ bool search = !song_.has_manually_unset_cover() && song_.art_automatic().isEmpty() && song_.art_manual().isEmpty() && !song_.artist().isEmpty() && !song_.album().isEmpty();
+
+ if (search) {
+ downloading_covers_ = true;
+ album_cover_choice_controller_->SearchCoverAutomatically(song_);
+
+ // Show a spinner animation
+ spinner_animation_.reset(new QMovie(":/pictures/spinner.gif", QByteArray(), this));
+ connect(spinner_animation_.get(), SIGNAL(updated(const QRect&)), SLOT(update()));
+ spinner_animation_->start();
+ update();
+ }
+
+ return search;
+
+}
+
+void ContextView::AutomaticCoverSearchDone() {
+
+ downloading_covers_ = false;
+ spinner_animation_.reset();
+ update();
+
+}
+
+void ContextView::ActionShowData() {
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+ s.setValue("show_data", action_show_data_->isChecked());
+ s.endGroup();
+ UpdateSong();
+}
+
+void ContextView::ActionShowOutput() {
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+ s.setValue("show_output", action_show_output_->isChecked());
+ s.endGroup();
+ UpdateSong();
+}
+
+void ContextView::ActionShowAlbums() {
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+ s.setValue("show_albums", action_show_albums_->isChecked());
+ s.endGroup();
+ prev_artist_ = QString();
+ UpdateSong();
+}
+
+void ContextView::ActionShowLyrics() {
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+ s.setValue("show_lyrics", action_show_lyrics_->isChecked());
+ s.endGroup();
+ UpdateSong();
+ if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
+}
+
+void ContextView::LoadCoverFromFile() {
+ album_cover_choice_controller_->LoadCoverFromFile(&song_);
+}
+
+void ContextView::LoadCoverFromURL() {
+ album_cover_choice_controller_->LoadCoverFromURL(&song_);
+}
+
+void ContextView::SearchForCover() {
+ album_cover_choice_controller_->SearchForCover(&song_);
+}
+
+void ContextView::SaveCoverToFile() {
+ album_cover_choice_controller_->SaveCoverToFile(song_, image_original_);
+}
+
+void ContextView::UnsetCover() {
+ album_cover_choice_controller_->UnsetCover(&song_);
+}
+
+void ContextView::ShowCover() {
+ album_cover_choice_controller_->ShowCover(song_);
+}
+
+void ContextView::SearchCoverAutomatically() {
+
+ QSettings s;
+ s.beginGroup(kSettingsGroup);
+ s.setValue("search_for_cover_auto", album_cover_choice_controller_->search_cover_auto_action()->isChecked());
+ s.endGroup();
+
+ GetCoverAutomatically();
+
+}
diff --git a/src/widgets/statusview.h b/src/context/contextview.h
similarity index 63%
rename from src/widgets/statusview.h
rename to src/context/contextview.h
index e999e6703..9b2eb7243 100644
--- a/src/widgets/statusview.h
+++ b/src/context/contextview.h
@@ -17,8 +17,8 @@
*
*/
-#ifndef STATUSVIEW_H
-#define STATUSVIEW_H
+#ifndef CONTEXTVIEW_H
+#define CONTEXTVIEW_H
#include "config.h"
@@ -31,52 +31,48 @@
#include
#include
#include
-#include
#include
+#include
#include
#include
-#include
#include
-#include
-#include
+#include
#include
#include "core/song.h"
#include "covermanager/albumcoverloaderoptions.h"
-class QEvent;
-class QContextMenuEvent;
-class QDragEnterEvent;
-class QDropEvent;
-class QMouseEvent;
+#include "ui_contextviewcontainer.h"
+
+using std::unique_ptr;
class Application;
class CollectionView;
-class CollectionViewContainer;
+class CollectionModel;
class AlbumCoverChoiceController;
+class Ui_ContextViewContainer;
+class ContextAlbumsView;
+class LyricsFetcher;
-class StatusView : public QWidget {
+class ContextView : public QWidget {
Q_OBJECT
-public:
- StatusView(CollectionViewContainer *collectionviewcontainer, QWidget *parent = nullptr);
- ~StatusView();
-
- static const char* kSettingsGroup;
- static const int kPadding;
- static const int kGradientHead;
- static const int kGradientTail;
- static const int kMaxCoverSize;
- static const int kBottomOffset;
- static const int kTopBorder;
+ public:
+ ContextView(QWidget *parent = nullptr);
+ ~ContextView();
void SetApplication(Application *app);
+ void SetCollectionView(CollectionView *collectionview);
-public slots:
+ ContextAlbumsView *albums() { return ui_->widget_play_albums; }
+
+ public slots:
+ void UpdateNoSong();
+ void Playing();
+ void Stopped();
+ void Error();
void SongChanged(const Song &song);
- void SongFinished();
- void AlbumArtLoaded(const Song& metadata, const QString &uri, const QImage &image);
- void FadePreviousTrack(qreal value);
+ void AlbumArtLoaded(const Song &song, const QString &uri, const QImage &image);
void LoadCoverFromFile();
void SaveCoverToFile();
@@ -86,92 +82,83 @@ public slots:
void ShowCover();
void SearchCoverAutomatically();
void AutomaticCoverSearchDone();
-
-private:
- QVBoxLayout *layout_;
- QScrollArea *scrollarea_;
- QVBoxLayout *container_layout_;
- QWidget *container_widget_;
+ void UpdateLyrics(quint64 id, const QString lyrics);
- QWidget *widget_stopped_;
- QWidget *widget_playing_;
- QVBoxLayout *layout_playing_;
- QVBoxLayout *layout_stopped_;
- QLabel *label_stopped_top_;
- QLabel *label_stopped_logo_;
- QLabel *label_stopped_text_;
- QLabel *label_playing_top_;
- QLabel *label_playing_album_;
- QLabel *label_playing_text_;
+ private:
- QPixmap *pixmap_album_;
- QPainter *painter_album_;
-
+ enum WidgetState {
+ //State_None = 0,
+ State_Playing,
+ State_Stopped
+ };
+
+ static const char *kSettingsGroup;
+ static const int kPadding;
+ static const int kGradientHead;
+ static const int kGradientTail;
+ static const int kMaxCoverSize;
+ static const int kBottomOffset;
+ static const int kTopBorder;
+
+ Application *app_;
+ Ui_ContextViewContainer *ui_;
CollectionView *collectionview_;
-
+ WidgetState widgetstate_;
+ QMenu *menu_;
+ QTimeLine *timeline_fade_;
+ QImage image_strawberry_;
+ AlbumCoverChoiceController *album_cover_choice_controller_;
+ LyricsFetcher *lyrics_fetcher_;
+ bool active_;
+ bool downloading_covers_;
+
+ QAction *action_show_data_;
+ QAction *action_show_output_;
+ QAction *action_show_albums_;
+ QAction *action_show_lyrics_;
AlbumCoverLoaderOptions cover_loader_options_;
-
- QImage original_;
-
- void CreateWidget();
- void NoSongWidget();
- void SongWidget();
+ Song song_;
+ Song song_empty_;
+ Song song_prev_;
+ QImage image_original_;
+ QImage image_previous_;
+ QPixmap *pixmap_album_;
+ QPixmap pixmap_current_;
+ QPixmap pixmap_previous_;
+ QPainter *painter_album_;
+ qreal pixmap_previous_opacity_;
+ std::unique_ptr spinner_animation_;
+
+ QString prev_artist_;
+ QString lyrics_;
+
+ void LoadSettings();
void AddActions();
+ void SetText(QLabel *label, int value, const QString &suffix, const QString &def = QString());
+ void NoSong();
+ void UpdateSong();
void SetImage(const QImage &image);
void DrawImage(QPainter *p);
void ScaleCover();
bool GetCoverAutomatically();
-
- Application *app_;
- AlbumCoverChoiceController *album_cover_choice_controller_;
- QAction *fit_cover_width_action_;
-
- bool visible_;
- int small_ideal_height_;
- int total_height_;
- bool fit_width_;
- QTimeLine *fade_animation_;
- QImage image_blank_;
- QImage image_nosong_;
-
- // Information about the current track
- Song metadata_;
- QPixmap pixmap_current_;
-
- // Holds the last track while we're fading to the new track
- QPixmap pixmap_previous_;
- qreal pixmap_previous_opacity_;
-
- std::unique_ptr spinner_animation_;
- bool downloading_covers_;
- bool stopped_;
- bool playing_;
-
- enum WidgetState {
- None = 0,
- Playing,
- Stopped
- };
- WidgetState widgetstate_;
- QMenu *menu_;
-
-protected:
+ protected:
bool eventFilter(QObject *, QEvent *);
void handlePaintEvent(QObject *object, QEvent *event);
- void paintEvent_album(QEvent *event);
+ void PaintEventAlbum(QEvent *event);
void contextMenuEvent(QContextMenuEvent *e);
void mouseReleaseEvent(QMouseEvent *);
void dragEnterEvent(QDragEnterEvent *e);
void dropEvent(QDropEvent *e);
- void UpdateSong();
- void NoSong();
- void SwitchWidgets(WidgetState state);
private slots:
- void UpdateNoSong();
+ void ActionShowData();
+ void ActionShowOutput();
+ void ActionShowAlbums();
+ void ActionShowLyrics();
+ void FadePreviousTrack(qreal value);
};
-#endif // STATUSVIEW_H
+#endif // CONTEXTVIEW_H
diff --git a/src/context/contextviewcontainer.ui b/src/context/contextviewcontainer.ui
new file mode 100644
index 000000000..e56398882
--- /dev/null
+++ b/src/context/contextviewcontainer.ui
@@ -0,0 +1,567 @@
+
+
+ ContextViewContainer
+
+
+
+ 0
+ 0
+ 400
+ 927
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
-
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 396
+ 923
+
+
+
+
-
+
+
+
+ 0
+ 70
+
+
+
+
+ 16777215
+ 70
+
+
+
+ No song playing
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+ -
+
+
+
+ 300
+ 300
+
+
+
+
+ 300
+ 300
+
+
+
+
+
+
+ :/pictures/strawberry.png
+
+
+ true
+
+
+
+ -
+
+
+
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 0
+ 53
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
-
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 396
+ 923
+
+
+
+
-
+
+
+
+ 0
+ 70
+
+
+
+
+ 16777215
+ 70
+
+
+
+
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+ true
+
+
+
+ -
+
+
+
+ 300
+ 300
+
+
+
+
+ 300
+ 300
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Bit depth
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Length
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Samplerate
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Filetype
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Bitrate
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
-
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+
+
+
+
+ -
+
+
+
+ 32
+ 32
+
+
+
+
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+ Engine
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+ Device
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 20
+
+
+
+
+ 300
+ 16777215
+
+
+
+
+
+
+ true
+
+
+ 6
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 10
+
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 0
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 0
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ContextAlbumsView
+ QWidget
+ context/contextalbumsview.h
+
+
+
+
+
+
+
diff --git a/src/core/application.cpp b/src/core/application.cpp
index 801bf8774..2c03ab6d2 100644
--- a/src/core/application.cpp
+++ b/src/core/application.cpp
@@ -52,6 +52,11 @@
#include "covermanager/discogscoverprovider.h"
#include "covermanager/musicbrainzcoverprovider.h"
+#include "lyrics/lyricsproviders.h"
+#include "lyrics/lyricsprovider.h"
+#include "lyrics/auddlyricsprovider.h"
+#include "lyrics/apiseedslyricsprovider.h"
+
#include "internet/internetmodel.h"
#include "tidal/tidalsearch.h"
@@ -60,18 +65,18 @@ bool Application::kIsPortable = false;
class ApplicationImpl {
public:
ApplicationImpl(Application *app) :
- tag_reader_client_([=]() {
+ tag_reader_client_([=]() {
TagReaderClient *client = new TagReaderClient(app);
app->MoveToNewThread(client);
client->Start();
return client;
}),
- database_([=]() {
+ database_([=]() {
Database *db = new Database(app, app);
app->MoveToNewThread(db);
DoInAMinuteOrSo(db, SLOT(DoBackup()));
return db;
- }),
+ }),
appearance_([=]() { return new Appearance(app); }),
task_manager_([=]() { return new TaskManager(app); }),
player_([=]() { return new Player(app, app); }),
@@ -88,10 +93,10 @@ class ApplicationImpl {
CoverProviders *cover_providers = new CoverProviders(app);
// Initialize the repository of cover providers.
#ifdef HAVE_LIBLASTFM
- cover_providers->AddProvider(new LastFmCoverProvider(app));
+ cover_providers->AddProvider(new LastFmCoverProvider(app));
#endif
cover_providers->AddProvider(new AmazonCoverProvider(app));
- cover_providers->AddProvider(new DiscogsCoverProvider(app));
+ cover_providers->AddProvider(new DiscogsCoverProvider(app));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app));
return cover_providers;
}),
@@ -102,7 +107,13 @@ class ApplicationImpl {
}),
current_art_loader_([=]() { return new CurrentArtLoader(app, app); }),
internet_model_([=]() { return new InternetModel(app, app); }),
- tidal_search_([=]() { return new TidalSearch(app, app); })
+ tidal_search_([=]() { return new TidalSearch(app, app); }),
+ lyrics_providers_([=]() {
+ LyricsProviders *lyrics_providers = new LyricsProviders(app);
+ lyrics_providers->AddProvider(new AuddLyricsProvider(app));
+ lyrics_providers->AddProvider(new APISeedsLyricsProvider(app));
+ return lyrics_providers;
+ })
{ }
Lazy tag_reader_client_;
@@ -120,6 +131,7 @@ class ApplicationImpl {
Lazy current_art_loader_;
Lazy internet_model_;
Lazy tidal_search_;
+ Lazy lyrics_providers_;
};
@@ -227,3 +239,7 @@ InternetModel* Application::internet_model() const {
TidalSearch* Application::tidal_search() const {
return p_->tidal_search_.get();
}
+
+LyricsProviders *Application::lyrics_providers() const {
+ return p_->lyrics_providers_.get();
+}
diff --git a/src/core/application.h b/src/core/application.h
index 7ad8cc684..b81703035 100644
--- a/src/core/application.h
+++ b/src/core/application.h
@@ -51,6 +51,7 @@ class AlbumCoverLoader;
class CurrentArtLoader;
class InternetModel;
class TidalSearch;
+class LyricsProviders;
class Application : public QObject {
Q_OBJECT
@@ -84,6 +85,8 @@ class Application : public QObject {
InternetModel *internet_model() const;
TidalSearch *tidal_search() const;
+ LyricsProviders *lyrics_providers() const;
+
void MoveToNewThread(QObject *object);
void MoveToThread(QObject *object, QThread *thread);
diff --git a/src/core/database.cpp b/src/core/database.cpp
index 4c221886d..8b22e330c 100644
--- a/src/core/database.cpp
+++ b/src/core/database.cpp
@@ -507,15 +507,14 @@ void Database::ExecSchemaCommands(QSqlDatabase &db, const QString &schema, int s
}
void Database::ExecSongTablesCommands(QSqlDatabase &db, const QStringList &song_tables, const QStringList &commands) {
-
+
for (const QString &command : commands) {
// There are now lots of "songs" tables that need to have the same schema: songs and device_*_songs.
// We allow a magic value in the schema files to update all songs tables at once.
if (command.contains(kMagicAllSongsTables)) {
for (const QString &table : song_tables) {
// Another horrible hack: device songs tables don't have matching _fts tables, so if this command tries to touch one, ignore it.
- if (table.startsWith("device_") &&
- command.contains(QString(kMagicAllSongsTables) + "_fts")) {
+ if (table.startsWith("device_") && command.contains(QString(kMagicAllSongsTables) + "_fts")) {
continue;
}
@@ -526,7 +525,8 @@ void Database::ExecSongTablesCommands(QSqlDatabase &db, const QStringList &song_
if (CheckErrors(query))
qFatal("Unable to update music collection database");
}
- } else {
+ }
+ else {
QSqlQuery query(db.exec(command));
if (CheckErrors(query)) qFatal("Unable to update music collection database");
}
diff --git a/src/core/flowlayout.cpp b/src/core/flowlayout.cpp
deleted file mode 100644
index 9d3bd7361..000000000
--- a/src/core/flowlayout.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
-** the names of its contributors may be used to endorse or promote
-** products derived from this software without specific prior written
-** permission.
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "flowlayout.h"
-
-//! [1]
-FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
- : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
-{
- setContentsMargins(margin, margin, margin, margin);
-}
-
-FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
- : m_hSpace(hSpacing), m_vSpace(vSpacing) {
- setContentsMargins(margin, margin, margin, margin);
-}
-//! [1]
-
-//! [2]
-FlowLayout::~FlowLayout() {
- QLayoutItem* item;
- while ((item = takeAt(0))) delete item;
-}
-//! [2]
-
-//! [3]
-void FlowLayout::addItem(QLayoutItem* item) { itemList.append(item); }
-//! [3]
-
-//! [4]
-int FlowLayout::horizontalSpacing() const {
- if (m_hSpace >= 0) {
- return m_hSpace;
- } else {
- return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
- }
-}
-
-int FlowLayout::verticalSpacing() const {
- if (m_vSpace >= 0) {
- return m_vSpace;
- } else {
- return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
- }
-}
-//! [4]
-
-//! [5]
-int FlowLayout::count() const { return itemList.size(); }
-
-QLayoutItem* FlowLayout::itemAt(int index) const {
- return itemList.value(index);
-}
-
-QLayoutItem* FlowLayout::takeAt(int index) {
- if (index >= 0 && index < itemList.size())
- return itemList.takeAt(index);
- else
- return 0;
-}
-//! [5]
-
-//! [6]
-Qt::Orientations FlowLayout::expandingDirections() const { return 0; }
-//! [6]
-
-//! [7]
-bool FlowLayout::hasHeightForWidth() const { return true; }
-
-int FlowLayout::heightForWidth(int width) const {
- int height = doLayout(QRect(0, 0, width, 0), true);
- return height;
-}
-//! [7]
-
-//! [8]
-void FlowLayout::setGeometry(const QRect& rect) {
- QLayout::setGeometry(rect);
- doLayout(rect, false);
-}
-
-QSize FlowLayout::sizeHint() const { return minimumSize(); }
-
-QSize FlowLayout::minimumSize() const {
- QSize size;
- for (QLayoutItem* item : itemList)
- size = size.expandedTo(item->minimumSize());
-
- size += QSize(2 * margin(), 2 * margin());
- return size;
-}
-//! [8]
-
-//! [9]
-int FlowLayout::doLayout(const QRect& rect, bool testOnly) const {
- int left, top, right, bottom;
- getContentsMargins(&left, &top, &right, &bottom);
- QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
- int x = effectiveRect.x();
- int y = effectiveRect.y();
- int lineHeight = 0;
- //! [9]
-
- //! [10]
- for (QLayoutItem* item : itemList) {
- QWidget* wid = item->widget();
- int spaceX = horizontalSpacing();
- if (spaceX == -1)
- spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
- int spaceY = verticalSpacing();
- if (spaceY == -1)
- spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
- //! [10]
- //! [11]
- int nextX = x + item->sizeHint().width() + spaceX;
- if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
- x = effectiveRect.x();
- y = y + lineHeight + spaceY;
- nextX = x + item->sizeHint().width() + spaceX;
- lineHeight = 0;
- }
-
- if (!testOnly) item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
-
- x = nextX;
- lineHeight = qMax(lineHeight, item->sizeHint().height());
- }
- return y + lineHeight - rect.y() + bottom;
-}
-//! [11]
-//! [12]
-int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const {
- QObject* parent = this->parent();
- if (!parent) {
- return -1;
- }
- else if (parent->isWidgetType()) {
- QWidget *pw = static_cast(parent);
- return pw->style()->pixelMetric(pm, 0, pw);
- }
- else {
- return static_cast(parent)->spacing();
- }
-}
-//! [12]
diff --git a/src/core/flowlayout.h b/src/core/flowlayout.h
deleted file mode 100644
index 30069f219..000000000
--- a/src/core/flowlayout.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the examples of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** You may use this file under the terms of the BSD license as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
-** the names of its contributors may be used to endorse or promote
-** products derived from this software without specific prior written
-** permission.
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef FLOWLAYOUT_H
-#define FLOWLAYOUT_H
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-//! [0]
-class FlowLayout : public QLayout {
- public:
- FlowLayout(QWidget* parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
- FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
- ~FlowLayout();
-
- void addItem(QLayoutItem *item);
- int horizontalSpacing() const;
- int verticalSpacing() const;
- Qt::Orientations expandingDirections() const;
- bool hasHeightForWidth() const;
- int heightForWidth(int) const;
- int count() const;
- QLayoutItem *itemAt(int index) const;
- QSize minimumSize() const;
- void setGeometry(const QRect &rect);
- QSize sizeHint() const;
- QLayoutItem *takeAt(int index);
-
-private:
- int doLayout(const QRect &rect, bool testOnly) const;
- int smartSpacing(QStyle::PixelMetric pm) const;
-
- QList itemList;
- int m_hSpace;
- int m_vSpace;
-};
-//! [0]
-
-#endif
diff --git a/src/core/iconloader.cpp b/src/core/iconloader.cpp
index 4b14fc2ca..f6c326a1b 100644
--- a/src/core/iconloader.cpp
+++ b/src/core/iconloader.cpp
@@ -31,34 +31,24 @@
#include "core/logging.h"
#include "iconloader.h"
-QList IconLoader::sizes_;
-QString IconDefault(":/icons/64x64/strawberry.png");
-
-void IconLoader::Init() {
-
- sizes_.clear();
- sizes_ << 22 << 32 << 48 << 64;
-
- if (!QFile::exists(IconDefault)) {
- qLog(Error) << "Default icon does not exist" << IconDefault;
- }
-
-}
-
-QIcon IconLoader::Load(const QString &name) {
+QIcon IconLoader::Load(const QString &name, const int size) {
QIcon ret;
+ QList sizes;
+ sizes.clear();
+ if (size == 0) { sizes << 22 << 32 << 48 << 64; }
+ else sizes << size;
+
if (name.isEmpty()) {
- qLog(Warning) << "Icon name is empty!";
- ret.addFile(IconDefault, QSize(64, 64));
+ qLog(Error) << "Icon name is empty!";
return ret;
}
const QString path(":icons/%1x%2/%3.png");
- for (int size : sizes_) {
- QString filename(path.arg(size).arg(size).arg(name));
- if (QFile::exists(filename)) ret.addFile(filename, QSize(size, size));
+ for (int s : sizes) {
+ QString filename(path.arg(s).arg(s).arg(name));
+ if (QFile::exists(filename)) ret.addFile(filename, QSize(s, s));
}
// Load icon from system theme only if it hasn't been found
@@ -68,10 +58,6 @@ QIcon IconLoader::Load(const QString &name) {
qLog(Warning) << "Couldn't load icon" << name;
}
- if (ret.isNull()) {
- ret.addFile(IconDefault, QSize(64, 64));
- }
-
return ret;
}
diff --git a/src/core/iconloader.h b/src/core/iconloader.h
index e209cc76c..c1ad9f509 100644
--- a/src/core/iconloader.h
+++ b/src/core/iconloader.h
@@ -27,14 +27,10 @@
class IconLoader {
public:
-
- static void Init();
- static QIcon Load(const QString &name);
-
+ static QIcon Load(const QString &name, const int size = 0);
private:
IconLoader() {}
-
- static QList sizes_;
};
#endif // ICONLOADER_H
+
diff --git a/src/core/main.cpp b/src/core/main.cpp
index a37fc4ce0..17ccb6c6a 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -200,9 +200,6 @@ int main(int argc, char* argv[]) {
// Resources
Q_INIT_RESOURCE(data);
-
- // Icons
- IconLoader::Init();
Application app;
diff --git a/src/core/mainwindow.cpp b/src/core/mainwindow.cpp
index 37d58fd82..03a401cf9 100644
--- a/src/core/mainwindow.cpp
+++ b/src/core/mainwindow.cpp
@@ -89,12 +89,13 @@
#include "widgets/fancytabwidget.h"
#include "widgets/playingwidget.h"
#include "widgets/sliderwidget.h"
-#include "widgets/statusview.h"
#include "widgets/fileview.h"
#include "widgets/multiloadingindicator.h"
#include "widgets/osd.h"
#include "widgets/stylehelper.h"
#include "widgets/trackslider.h"
+#include "context/contextview.h"
+#include "collection/collectionview.h"
#include "collection/collection.h"
#include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h"
@@ -158,12 +159,12 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
osd_(osd),
edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)),
global_shortcuts_(new GlobalShortcuts(this)),
+ context_view_(new ContextView(this)),
collection_view_(new CollectionViewContainer(this)),
- status_view_(new StatusView(collection_view_, this)),
file_view_(new FileView(this)),
- playlist_list_(new PlaylistListContainer(this)),
device_view_container_(new DeviceViewContainer(this)),
device_view_(device_view_container_->view()),
+ playlist_list_(new PlaylistListContainer(this)),
settings_dialog_(std::bind(&MainWindow::CreateSettingsDialog, this)),
cover_manager_([=]() {
AlbumCoverManager *cover_manager = new AlbumCoverManager(app, app->collection_backend());
@@ -195,7 +196,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
collection_sort_model_(new QSortFilterProxyModel(this)),
track_position_timer_(new QTimer(this)),
track_slider_timer_(new QTimer(this)),
- was_maximized_(false),
+ initialised_(false),
+ was_maximized_(true),
saved_playback_position_(0),
saved_playback_state_(Engine::Empty),
doubleclick_addmode_(AddBehaviour_Append),
@@ -214,7 +216,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
#endif
ui_->multi_loading_indicator->SetTaskManager(app_->task_manager());
- status_view_->SetApplication(app_);
+ context_view_->SetApplication(app_);
+ context_view_->SetCollectionView(collection_view_->view());
ui_->widget_playing->SetApplication(app_);
int volume = app_->player()->GetVolume();
@@ -225,7 +228,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
StyleHelper::setBaseColor(palette().color(QPalette::Highlight).darker());
// Add tabs to the fancy tab widget
- ui_->tabs->addTab(status_view_, IconLoader::Load("strawberry"), tr("Status"));
+ ui_->tabs->addTab(context_view_, IconLoader::Load("strawberry"), tr("Context"));
ui_->tabs->addTab(collection_view_, IconLoader::Load("vinyl"), tr("Collection"));
ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), tr("Files"));
ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist"), tr("Playlists"));
@@ -435,6 +438,10 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->playlist->view(), SIGNAL(BackgroundPropertyChanged()), SLOT(RefreshStyleSheet()));
connect(ui_->track_slider, SIGNAL(ValueChangedSeconds(int)), app_->player(), SLOT(SeekTo(int)));
+
+ // Context connections
+
+ connect(context_view_->albums(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
// Collection connections
connect(collection_view_->view(), SIGNAL(AddToPlaylistSignal(QMimeData*)), SLOT(AddToPlaylist(QMimeData*)));
@@ -581,8 +588,12 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
connect(ui_->tabs, SIGNAL(CurrentChanged(int)), SLOT(TabSwitched()));
connect(ui_->tabs, SIGNAL(CurrentChanged(int)), SLOT(SaveGeometry()));
- // Status
- ConnectStatusView(status_view_);
+ // Context
+ connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), context_view_, SLOT(SongChanged(Song)));
+ connect(app_->player(), SIGNAL(PlaylistFinished()), context_view_, SLOT(Stopped()));
+ connect(app_->player(), SIGNAL(Playing()), context_view_, SLOT(Playing()));
+ connect(app_->player(), SIGNAL(Stopped()), context_view_, SLOT(Stopped()));
+ connect(app_->player(), SIGNAL(Error()), context_view_, SLOT(Error()));
// Analyzer
//ui_->analyzer->SetEngine(app_->player()->engine());
@@ -611,8 +622,12 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
// Playing widget
qLog(Debug) << "Creating playing widget";
ui_->widget_playing->set_ideal_height(ui_->status_bar->sizeHint().height() + ui_->player_controls->sizeHint().height());
+ connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), ui_->widget_playing, SLOT(SongChanged(Song)));
+ connect(app_->player(), SIGNAL(PlaylistFinished()), ui_->widget_playing, SLOT(Stopped()));
+ connect(app_->player(), SIGNAL(Playing()), ui_->widget_playing, SLOT(Playing()));
connect(app_->player(), SIGNAL(Stopped()), ui_->widget_playing, SLOT(Stopped()));
- //connect(ui_->widget_playing, SIGNAL(ShowAboveStatusBarChanged(bool)), SLOT(PlayingWidgetPositionChanged(bool)));
+ connect(app_->player(), SIGNAL(Error()), ui_->widget_playing, SLOT(Error()));
+
connect(ui_->action_console, SIGNAL(triggered()), SLOT(ShowConsole()));
PlayingWidgetPositionChanged();
@@ -622,7 +637,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
const_cast(Appearance::kDefaultPalette) = QApplication::palette();
app_->appearance()->LoadUserTheme();
StyleSheetLoader *css_loader = new StyleSheetLoader(this);
- css_loader->SetStyleSheet(this, ":style/mainwindow.css");
+ css_loader->SetStyleSheet(this, ":/style/strawberry.css");
RefreshStyleSheet();
// Load playlists
@@ -643,15 +658,15 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
// Set last used geometry to position window on the correct monitor
// Set window state only if the window was last maximized
- was_maximized_ = settings_.value("maximized", false).toBool();
- restoreGeometry(settings_.value("geometry").toByteArray());
- if (was_maximized_) {
- setWindowState(windowState() | Qt::WindowMaximized);
- }
+ was_maximized_ = settings_.value("maximized", true).toBool();
+
+ if (was_maximized_) setWindowState(windowState() | Qt::WindowMaximized);
+ else restoreGeometry(settings_.value("geometry").toByteArray());
if (!ui_->splitter->restoreState(settings_.value("splitter_state").toByteArray())) {
- ui_->splitter->setSizes(QList() << 300 << width() - 300);
+ ui_->splitter->setSizes(QList() << 250 << width() - 250);
}
+
ui_->tabs->setCurrentIndex(settings_.value("current_tab", 1 /* Collection tab */ ).toInt());
FancyTabWidget::Mode default_mode = FancyTabWidget::Mode_LargeSidebar;
ui_->tabs->SetMode(FancyTabWidget::Mode(settings_.value("tab_mode", default_mode).toInt()));
@@ -715,6 +730,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
qLog(Debug) << "Started";
RefreshStyleSheet();
+
+ initialised_ = true;
}
@@ -733,8 +750,6 @@ void MainWindow::ReloadSettings() {
bool showtrayicon = settings.value("showtrayicon", true).toBool();
settings.endGroup();
- //qLog(Debug) << "showtrayicon" << showtrayicon;
-
tray_icon_->SetVisible(showtrayicon);
if (!showtrayicon && !isVisible()) show();
#endif
@@ -766,7 +781,7 @@ void MainWindow::RefreshStyleSheet() {
setStyleSheet(styleSheet());
}
void MainWindow::MediaStopped() {
-
+
setWindowTitle("Strawberry Music Player");
ui_->action_stop->setEnabled(false);
@@ -828,7 +843,6 @@ void MainWindow::VolumeChanged(int volume) {
void MainWindow::SongChanged(const Song &song) {
- //setWindowTitle(song.PrettyTitleWithArtist() + " --- Strawberry Music Player");
setWindowTitle(song.PrettyTitleWithArtist());
tray_icon_->SetProgress(0);
@@ -853,7 +867,14 @@ void MainWindow::TrackSkipped(PlaylistItemPtr item) {
}
}
-void MainWindow::resizeEvent(QResizeEvent*) { SaveGeometry(); }
+void MainWindow::changeEvent(QEvent *event) {
+ if (!initialised_) return;
+ SaveGeometry();
+}
+
+void MainWindow::resizeEvent(QResizeEvent *event) {
+ SaveGeometry();
+}
void MainWindow::TabSwitched() {
@@ -870,10 +891,8 @@ void MainWindow::SaveGeometry() {
was_maximized_ = isMaximized();
settings_.setValue("maximized", was_maximized_);
- // Save the geometry only when mainwindow is not in maximized state
- if (!was_maximized_) {
- settings_.setValue("geometry", saveGeometry());
- }
+ if (was_maximized_) settings_.remove("geometry");
+ else settings_.setValue("geometry", saveGeometry());
settings_.setValue("splitter_state", ui_->splitter->saveState());
settings_.setValue("current_tab", ui_->tabs->currentIndex());
settings_.setValue("tab_mode", ui_->tabs->mode());
@@ -2104,34 +2123,6 @@ void MainWindow::ShowQueueManager() {
queue_manager_->show();
}
-#if 0
-void MainWindow::ConnectInfoView(SongInfoBase *view) {
-
- QObject::connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), view, SLOT(SongChanged(Song)));
- QObject::connect(app_->player(), SIGNAL(PlaylistFinished()), view, SLOT(SongFinished()));
- QObject::connect(app_->player(), SIGNAL(Stopped()), view, SLOT(SongFinished()));
-
- QObject::connect(view, SIGNAL(ShowSettingsDialog()), SLOT(ShowSongInfoConfig()));
-
-}
-#endif
-
-void MainWindow::ConnectStatusView(StatusView *statusview) {
-
- QObject::connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), statusview, SLOT(SongChanged(Song)));
- QObject::connect(app_->player(), SIGNAL(PlaylistFinished()), statusview, SLOT(SongFinished()));
- QObject::connect(app_->player(), SIGNAL(Stopped()), statusview, SLOT(SongFinished()));
-
- //QObject::connect(statusview, SIGNAL(ShowSettingsDialog()), SLOT(ShowSongInfoConfig()));
-
-}
-
-#if 0
-void MainWindow::ShowSongInfoConfig() {
- OpenSettingsDialogAtPage(SettingsDialog::Page_SongInformation);
-}
-#endif
-
void MainWindow::PlaylistViewSelectionModelChanged() {
connect(ui_->playlist->view()->selectionModel(),SIGNAL(currentChanged(QModelIndex, QModelIndex)), SLOT(PlaylistCurrentChanged(QModelIndex)));
diff --git a/src/core/mainwindow.h b/src/core/mainwindow.h
index 920a8c1ed..9df6f89bf 100644
--- a/src/core/mainwindow.h
+++ b/src/core/mainwindow.h
@@ -59,6 +59,7 @@
class About;
class AlbumCoverManager;;
class Application;
+class ContextView;
class CollectionViewContainer;
class CommandlineOptions;
class DeviceView;
@@ -73,7 +74,6 @@ class OrganiseDialog;
class PlaylistListContainer;
class QueueManager;
class Song;
-class StatusView;
class SystemTrayIcon;
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
class TagFetcher;
@@ -129,6 +129,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
protected:
void keyPressEvent(QKeyEvent *event);
+ void changeEvent(QEvent *event);
void resizeEvent(QResizeEvent *event);
void closeEvent(QCloseEvent *event);
@@ -270,7 +271,6 @@ signals:
void SearchForAlbum();
private:
- void ConnectStatusView(StatusView *statusview);
void ApplyAddBehaviour(AddBehaviour b, MimeData *data) const;
void ApplyPlayBehaviour(PlayBehaviour b, MimeData *data) const;
@@ -292,12 +292,12 @@ signals:
GlobalShortcuts *global_shortcuts_;
+ ContextView *context_view_;
CollectionViewContainer *collection_view_;
- StatusView *status_view_;
FileView *file_view_;
- PlaylistListContainer *playlist_list_;
DeviceViewContainer *device_view_container_;
DeviceView *device_view_;
+ PlaylistListContainer *playlist_list_;
Lazy settings_dialog_;
Lazy cover_manager_;
@@ -354,6 +354,7 @@ signals:
QTimer *track_slider_timer_;
QSettings settings_;
+ bool initialised_;
bool was_maximized_;
int saved_playback_position_;
Engine::State saved_playback_state_;
diff --git a/src/core/player.cpp b/src/core/player.cpp
index ce11ef579..7169fc1e9 100644
--- a/src/core/player.cpp
+++ b/src/core/player.cpp
@@ -431,6 +431,7 @@ void Player::EngineStateChanged(Engine::State state) {
emit Playing();
break;
case Engine::Error:
+ emit Error();
case Engine::Empty:
case Engine::Idle:
emit Stopped();
diff --git a/src/core/player.h b/src/core/player.h
index ee86f3a32..f38bc656b 100644
--- a/src/core/player.h
+++ b/src/core/player.h
@@ -95,10 +95,11 @@ class PlayerInterface : public QObject {
virtual void Play() = 0;
virtual void ShowOSD() = 0;
-signals:
+ signals:
void Playing();
void Paused();
void Stopped();
+ void Error();
void PlaylistFinished();
void VolumeChanged(int volume);
void Error(const QString &message);
diff --git a/src/core/qtsystemtrayicon.cpp b/src/core/qtsystemtrayicon.cpp
index 333681ca7..d571fd275 100644
--- a/src/core/qtsystemtrayicon.cpp
+++ b/src/core/qtsystemtrayicon.cpp
@@ -47,12 +47,12 @@ QtSystemTrayIcon::QtSystemTrayIcon(QObject *parent)
action_mute_(nullptr)
{
- QIcon theme_icon = IconLoader::Load("strawberry-panel");
- QIcon theme_icon_grey = IconLoader::Load("strawberry-panel-grey");
+ QIcon theme_icon = IconLoader::Load("strawberry", 48);
+ QIcon theme_icon_grey = IconLoader::Load("strawberry-grey", 48);
if (theme_icon.isNull() || theme_icon_grey.isNull()) {
// Load the default icon
- QIcon icon(":/icons/64x64/strawberry-panel.png");
+ QIcon icon(":/icons/48x48/strawberry.png");
normal_icon_ = icon.pixmap(48, QIcon::Normal);
grey_icon_ = icon.pixmap(48, QIcon::Disabled);
}
diff --git a/src/core/song.cpp b/src/core/song.cpp
index 456fa3cee..b4cd97b8a 100644
--- a/src/core/song.cpp
+++ b/src/core/song.cpp
@@ -693,7 +693,7 @@ void Song::InitFromFilePartial(const QString &filename) {
TagLib::FileRef fileref(filename.toUtf8().constData());
//if (TagLib::FileRef::defaultFileExtensions().contains(suffix.toUtf8().constData())) {
- if (fileref.file()) d->valid_ = true;
+ if (fileref.file() || (suffix == "dsf")) d->valid_ = true;
else {
d->valid_ = false;
qLog(Error) << "File" << filename << "is not recognized by TagLib as a valid audio file.";
diff --git a/src/core/songloader.cpp b/src/core/songloader.cpp
index ad4b03efb..6f9633765 100644
--- a/src/core/songloader.cpp
+++ b/src/core/songloader.cpp
@@ -118,9 +118,10 @@ SongLoader::Result SongLoader::Load(const QUrl &url) {
return LoadLocal(url_.toLocalFile());
}
- if (sRawUriSchemes.contains(url_.scheme()) || player_->HandlerForUrl(url) != nullptr) {
+ if (sRawUriSchemes.contains(url_.scheme()) || player_->HandlerForUrl(url)) {
// The URI scheme indicates that it can't possibly be a playlist,
- // or we have a custom handler for the URL, so add it as a raw stream. AddAsRawStream();
+ // or we have a custom handler for the URL, so add it as a raw stream.
+ AddAsRawStream();
return Success;
}
diff --git a/src/covermanager/albumcoverchoicecontroller.cpp b/src/covermanager/albumcoverchoicecontroller.cpp
index b3f48efa3..9dfd34a7a 100644
--- a/src/covermanager/albumcoverchoicecontroller.cpp
+++ b/src/covermanager/albumcoverchoicecontroller.cpp
@@ -79,7 +79,7 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget *parent) :
unset_cover_ = new QAction(IconLoader::Load("list-remove"), tr("Unset cover"), this);
show_cover_ = new QAction(IconLoader::Load("zoom-in"), tr("Show fullsize..."), this);
- search_cover_auto_ = new QAction(IconLoader::Load("search"), tr("Search automatically"), this);
+ search_cover_auto_ = new QAction(tr("Search automatically"), this);
search_cover_auto_->setCheckable(true);
search_cover_auto_->setChecked(false);
diff --git a/src/covermanager/albumcoverfetchersearch.h b/src/covermanager/albumcoverfetchersearch.h
index eaf457344..7d5e19eb3 100644
--- a/src/covermanager/albumcoverfetchersearch.h
+++ b/src/covermanager/albumcoverfetchersearch.h
@@ -58,26 +58,26 @@ class AlbumCoverFetcherSearch : public QObject {
CoverSearchStatistics statistics() const { return statistics_; }
-signals:
+ signals:
// It's the end of search (when there was no fetch-me-a-cover request).
- void SearchFinished(quint64, const CoverSearchResults& results);
+ void SearchFinished(quint64, const CoverSearchResults &results);
// It's the end of search and we've fetched a cover.
void AlbumCoverFetched(quint64, const QImage &cover);
-private slots:
+ private slots:
void ProviderSearchFinished(int id, const QList &results);
void ProviderCoverFetchFinished(RedirectFollower *reply);
void TerminateSearch();
-private:
+ private:
void AllProvidersFinished();
void FetchMoreImages();
float ScoreImage(const QImage &image) const;
void SendBestImage();
-private:
+ private:
static const int kSearchTimeoutMs;
static const int kImageLoadTimeoutMs;
static const int kTargetSize;
diff --git a/src/covermanager/albumcoverloader.cpp b/src/covermanager/albumcoverloader.cpp
index 37db4af3f..8c4cd9634 100644
--- a/src/covermanager/albumcoverloader.cpp
+++ b/src/covermanager/albumcoverloader.cpp
@@ -226,6 +226,7 @@ void AlbumCoverLoader::RemoteFetchFinished(QNetworkReply *reply) {
}
NextState(&task);
+
}
QImage AlbumCoverLoader::ScaleAndPad(const AlbumCoverLoaderOptions &options, const QImage &image) {
diff --git a/src/covermanager/albumcovermanager.cpp b/src/covermanager/albumcovermanager.cpp
index d99454201..f5090aee7 100644
--- a/src/covermanager/albumcovermanager.cpp
+++ b/src/covermanager/albumcovermanager.cpp
@@ -92,9 +92,8 @@ AlbumCoverManager::AlbumCoverManager(Application *app, CollectionBackend *collec
cover_searcher_(nullptr),
cover_export_(nullptr),
cover_exporter_(new AlbumCoverExporter(this)),
- artist_icon_(IconLoader::Load("guitar" )),
- all_artists_icon_(IconLoader::Load("cd" )),
- //no_cover_icon_(IconLoader::Load("nocover")),
+ artist_icon_(IconLoader::Load("folder-sound" )),
+ all_artists_icon_(IconLoader::Load("vinyl" )),
no_cover_icon_(":/pictures/noalbumart.png"),
no_cover_image_(GenerateNoCoverImage(no_cover_icon_)),
no_cover_item_icon_(QPixmap::fromImage(no_cover_image_)),
diff --git a/src/covermanager/amazoncoverprovider.cpp b/src/covermanager/amazoncoverprovider.cpp
index d3e69b922..2c5470a34 100644
--- a/src/covermanager/amazoncoverprovider.cpp
+++ b/src/covermanager/amazoncoverprovider.cpp
@@ -111,7 +111,7 @@ void AmazonCoverProvider::QueryFinished(QNetworkReply *reply, int id) {
reply->deleteLater();
- QString data=(QString)reply->readAll();
+ QString data(reply->readAll());
CoverSearchResults results;
diff --git a/src/covermanager/lastfmcoverprovider.h b/src/covermanager/lastfmcoverprovider.h
index 423fa9911..b195f2297 100644
--- a/src/covermanager/lastfmcoverprovider.h
+++ b/src/covermanager/lastfmcoverprovider.h
@@ -37,7 +37,7 @@
class LastFmCoverProvider : public CoverProvider {
Q_OBJECT
-public:
+ public:
explicit LastFmCoverProvider(QObject *parent = nullptr);
bool StartSearch(const QString &artist, const QString &album, int id);
@@ -45,10 +45,10 @@ public:
static const char *kApiKey;
static const char *kSecret;
-private slots:
+ private slots:
void QueryFinished(QNetworkReply *reply, int id);
-private:
+ private:
QNetworkAccessManager *network_;
QMap pending_queries_;
diff --git a/src/device/filesystemdevice.cpp b/src/device/filesystemdevice.cpp
index 36f0939d4..362ce011f 100644
--- a/src/device/filesystemdevice.cpp
+++ b/src/device/filesystemdevice.cpp
@@ -46,7 +46,7 @@ FilesystemDevice::FilesystemDevice(const QUrl &url, DeviceLister *lister, const
watcher_->set_backend(backend_);
watcher_->set_task_manager(app_->task_manager());
- connect(backend_, SIGNAL(DirectoryDiscovered(Directory,SubdirectoryList)), watcher_, SLOT(AddDirectory(Directory,SubdirectoryList)));
+ connect(backend_, SIGNAL(DirectoryDiscovered(Directory, SubdirectoryList)), watcher_, SLOT(AddDirectory(Directory, SubdirectoryList)));
connect(backend_, SIGNAL(DirectoryDeleted(Directory)), watcher_, SLOT(RemoveDirectory(Directory)));
connect(watcher_, SIGNAL(NewOrUpdatedSongs(SongList)), backend_, SLOT(AddOrUpdateSongs(SongList)));
connect(watcher_, SIGNAL(SongsMTimeUpdated(SongList)), backend_, SLOT(UpdateMTimesOnly(SongList)));
diff --git a/src/engine/alsadevicefinder.cpp b/src/engine/alsadevicefinder.cpp
index 75483da74..20fd4aed1 100644
--- a/src/engine/alsadevicefinder.cpp
+++ b/src/engine/alsadevicefinder.cpp
@@ -101,6 +101,8 @@ QList AlsaDeviceFinder::ListDevices() {
device.description = QString("%1 %2").arg(snd_ctl_card_info_get_name(cardinfo)).arg(snd_pcm_info_get_name(pcminfo));
device.value = QString("hw:%1,%2").arg(card).arg(dev);
device.iconname = GuessIconName(device.description);
+ device.card = card;
+ device.device = dev;
ret.append(device);
}
diff --git a/src/engine/devicefinder.h b/src/engine/devicefinder.h
index b218425b1..9ee9602c5 100644
--- a/src/engine/devicefinder.h
+++ b/src/engine/devicefinder.h
@@ -38,6 +38,8 @@ class DeviceFinder {
QString description;
QVariant value;
QString iconname;
+ int card;
+ int device;
};
virtual ~DeviceFinder() {}
diff --git a/src/engine/enginebase.h b/src/engine/enginebase.h
index 847c7e8ac..254057942 100644
--- a/src/engine/enginebase.h
+++ b/src/engine/enginebase.h
@@ -41,6 +41,7 @@
#include "engine_fwd.h"
#include "enginetype.h"
+#include "enginedevice.h"
namespace Engine {
@@ -119,6 +120,8 @@ public:
static const int kScopeSize = 1024;
+ QVariant device() { return device_; }
+
public slots:
virtual void SetEqualizerEnabled(bool) {}
virtual void SetEqualizerParameters(int preamp, const QList &bandGains) {}
diff --git a/src/lyrics/apiseedslyricsprovider.cpp b/src/lyrics/apiseedslyricsprovider.cpp
new file mode 100644
index 000000000..ab9ea6636
--- /dev/null
+++ b/src/lyrics/apiseedslyricsprovider.cpp
@@ -0,0 +1,183 @@
+/*
+ * Strawberry Music Player
+ * Copyright 2018, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "core/closure.h"
+#include "core/logging.h"
+#include "core/network.h"
+#include "core/utilities.h"
+#include "lyricsprovider.h"
+#include "lyricsfetcher.h"
+#include "apiseedslyricsprovider.h"
+
+const char *APISeedsLyricsProvider::kUrlSearch = "https://orion.apiseeds.com/api/music/lyric";
+const char *APISeedsLyricsProvider::kAPIKeyB64 = "REdWenJhR245Qm03cnE5NlhoS1pTd0V5UVNCNjBtTWVEZlp0ZEttVXhKZTRRdnZSbTRYcmlaUVlaMlM3c0JQUw==";
+
+APISeedsLyricsProvider::APISeedsLyricsProvider(QObject *parent) : LyricsProvider("APISeeds", parent), network_(new NetworkAccessManager(this)) {}
+
+bool APISeedsLyricsProvider::StartSearch(const QString &artist, const QString &album, const QString &title, quint64 id) {
+
+ typedef QPair Arg;
+ typedef QList ArgList;
+ typedef QPair EncodedArg;
+
+ ArgList args = ArgList();
+ args.append(Arg("apikey", QByteArray::fromBase64(kAPIKeyB64)));
+
+ QUrlQuery url_query;
+ for (const Arg &arg : args) {
+ EncodedArg encoded_arg(QUrl::toPercentEncoding(arg.first), QUrl::toPercentEncoding(arg.second));
+ url_query.addQueryItem(encoded_arg.first, encoded_arg.second);
+ }
+
+ QUrl url(QString("%1/%2/%3").arg(kUrlSearch).arg(artist).arg(title));
+ url.setQuery(url_query);
+ QNetworkReply *reply = network_->get(QNetworkRequest(url));
+ NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleSearchReply(QNetworkReply*, quint64, QString, QString)), reply, id, artist, title);
+
+ return true;
+
+}
+
+void APISeedsLyricsProvider::CancelSearch(quint64 id) {
+}
+
+void APISeedsLyricsProvider::HandleSearchReply(QNetworkReply *reply, quint64 id, const QString artist, const QString title) {
+
+ reply->deleteLater();
+
+ QJsonObject json_obj = ExtractResult(reply, id);
+ if (json_obj.isEmpty()) return;
+
+ if (!json_obj.contains("artist") || !json_obj.contains("track")) {
+ Error(id, "APISeedsLyrics: Invalid Json reply, result is missing artist or track.", json_obj);
+ return;
+ }
+ QJsonObject json_artist(json_obj["artist"].toObject());
+ QJsonObject json_track(json_obj["track"].toObject());
+ if (!json_track.contains("text")) {
+ Error(id, "APISeedsLyrics: Invalid Json reply, track is missing text.", json_obj);
+ return;
+ }
+
+ LyricsSearchResults results;
+ LyricsSearchResult result;
+ result.artist = json_artist["name"].toString();
+ result.title = json_track["name"].toString();
+ result.lyrics = json_track["text"].toString();
+ result.score = 0.0;
+ if (result.artist.toLower() == artist.toLower()) result.score += 1.0;
+ if (result.title.toLower() == title.toLower()) result.score += 1.0;
+
+ results << result;
+
+ emit SearchFinished(id, results);
+
+}
+
+QJsonObject APISeedsLyricsProvider::ExtractJsonObj(QNetworkReply *reply, quint64 id) {
+
+ if (reply->error() != QNetworkReply::NoError) {
+ QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
+ Error(id, failure_reason);
+ return QJsonObject();
+ }
+
+ QByteArray data(reply->readAll());
+
+ QJsonParseError error;
+ QJsonDocument json_doc = QJsonDocument::fromJson(data, &error);
+
+ if (error.error != QJsonParseError::NoError) {
+ Error(id, "Reply from server missing Json data.");
+ return QJsonObject();
+ }
+
+ if (json_doc.isNull() || json_doc.isEmpty()) {
+ Error(id, "Received empty Json document.");
+ return QJsonObject();
+ }
+
+ if (!json_doc.isObject()) {
+ Error(id, "Json document is not an object.");
+ return QJsonObject();
+ }
+
+ QJsonObject json_obj = json_doc.object();
+ if (json_obj.isEmpty()) {
+ Error(id, "Received empty Json object.");
+ return QJsonObject();
+ }
+
+ return json_obj;
+
+}
+
+QJsonObject APISeedsLyricsProvider::ExtractResult(QNetworkReply *reply, quint64 id) {
+
+ QJsonObject json_obj = ExtractJsonObj(reply, id);
+ if (json_obj.isEmpty()) return QJsonObject();
+
+ if (json_obj.contains("error")) {
+ Error(id, json_obj["error"].toString(), json_obj);
+ return QJsonObject();
+ }
+
+ if (!json_obj.contains("result")) {
+ Error(id, "Json reply is missing result.", json_obj);
+ return QJsonObject();
+ }
+
+ QJsonObject json_result = json_obj["result"].toObject();
+ if (json_result.isEmpty()) {
+ Error(id, "Json result object is empty.");
+ return QJsonObject();
+ }
+ return json_result;
+
+}
+
+void APISeedsLyricsProvider::Error(quint64 id, QString error, QVariant debug) {
+ LyricsSearchResults results;
+ qLog(Error) << "APISeedsLyrics:" << error;
+ if (debug.isValid()) qLog(Debug) << debug;
+ emit SearchFinished(id, results);
+}
diff --git a/src/lyrics/apiseedslyricsprovider.h b/src/lyrics/apiseedslyricsprovider.h
new file mode 100644
index 000000000..bdbdcae6c
--- /dev/null
+++ b/src/lyrics/apiseedslyricsprovider.h
@@ -0,0 +1,62 @@
+/*
+ * Strawberry Music Player
+ * Copyright 2018, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#ifndef APISEEDSLYRICSPROVIDER_H
+#define APISEEDSLYRICSPROVIDER_H
+
+#include "config.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "lyricsprovider.h"
+#include "lyricsfetcher.h"
+
+class APISeedsLyricsProvider : public LyricsProvider {
+ Q_OBJECT
+
+ public:
+ explicit APISeedsLyricsProvider(QObject *parent = nullptr);
+
+ bool StartSearch(const QString &artist, const QString &album, const QString &title, quint64 id);
+ void CancelSearch(quint64 id);
+
+ private slots:
+ void HandleSearchReply(QNetworkReply *reply, quint64 id, const QString artist, const QString title);
+
+ private:
+ static const char *kUrlSearch;
+ static const char *kAPIKeyB64;
+ QNetworkAccessManager *network_;
+ void Error(quint64 id, QString error, QVariant debug = QVariant());
+
+ QJsonObject ExtractJsonObj(QNetworkReply *reply, quint64 id);
+ QJsonObject ExtractResult(QNetworkReply *reply, quint64 id);
+
+};
+
+#endif // APISEEDSLYRICSPROVIDER_H
+
diff --git a/src/lyrics/auddlyricsprovider.cpp b/src/lyrics/auddlyricsprovider.cpp
new file mode 100644
index 000000000..105f8f21e
--- /dev/null
+++ b/src/lyrics/auddlyricsprovider.cpp
@@ -0,0 +1,215 @@
+/*
+ * Strawberry Music Player
+ * Copyright 2018, Jonas Kvinge
+ *
+ * Strawberry is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Strawberry is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Strawberry. If not, see .
+ *
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include