diff --git a/data/data.qrc b/data/data.qrc index 1ac53717f..f9fa31171 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -32,7 +32,7 @@ last.fm/my_friends.png last.fm/my_neighbours.png last.fm/user_purple.png - somafm.png + providers/somafm.png schema-1.sql schema-2.sql nocover.png @@ -45,7 +45,7 @@ schema-7.sql tiny-pause.png tiny-start.png - magnatune.png + providers/magnatune.png schema-8.sql schema-9.sql icons/22x22/application-exit.png @@ -266,5 +266,12 @@ schema-17.sql lyrics/ultimate_providers.xml sidebar_background.png + providers/wikipedia.png + providers/aol.png + providers/amazon.png + providers/myspace.png + providers/mog.png + providers/mtvmusic.png + providers/cdbaby.png diff --git a/data/providers/amazon.png b/data/providers/amazon.png new file mode 100644 index 000000000..d7a2ba023 Binary files /dev/null and b/data/providers/amazon.png differ diff --git a/data/providers/aol.png b/data/providers/aol.png new file mode 100644 index 000000000..003a84999 Binary files /dev/null and b/data/providers/aol.png differ diff --git a/data/providers/cdbaby.png b/data/providers/cdbaby.png new file mode 100644 index 000000000..bd771b901 Binary files /dev/null and b/data/providers/cdbaby.png differ diff --git a/data/magnatune.png b/data/providers/magnatune.png similarity index 100% rename from data/magnatune.png rename to data/providers/magnatune.png diff --git a/data/providers/mog.png b/data/providers/mog.png new file mode 100644 index 000000000..c65fa3359 Binary files /dev/null and b/data/providers/mog.png differ diff --git a/data/providers/mtvmusic.png b/data/providers/mtvmusic.png new file mode 100644 index 000000000..321b784b4 Binary files /dev/null and b/data/providers/mtvmusic.png differ diff --git a/data/providers/myspace.png b/data/providers/myspace.png new file mode 100644 index 000000000..ce6e9e05f Binary files /dev/null and b/data/providers/myspace.png differ diff --git a/data/somafm.png b/data/providers/somafm.png similarity index 100% rename from data/somafm.png rename to data/providers/somafm.png diff --git a/data/providers/wikipedia.png b/data/providers/wikipedia.png new file mode 100644 index 000000000..dbcf2a165 Binary files /dev/null and b/data/providers/wikipedia.png differ diff --git a/src/radio/magnatunedownloaddialog.ui b/src/radio/magnatunedownloaddialog.ui index 5796b103d..d2925964b 100644 --- a/src/radio/magnatunedownloaddialog.ui +++ b/src/radio/magnatunedownloaddialog.ui @@ -15,7 +15,7 @@ - :/magnatune.png:/magnatune.png + :/providers/magnatune.png:/providers/magnatune.png @@ -39,9 +39,6 @@ false - - false - Name diff --git a/src/radio/magnatuneservice.cpp b/src/radio/magnatuneservice.cpp index b98860602..df6156600 100644 --- a/src/radio/magnatuneservice.cpp +++ b/src/radio/magnatuneservice.cpp @@ -106,7 +106,7 @@ void MagnatuneService::ReloadSettings() { RadioItem* MagnatuneService::CreateRootItem(RadioItem *parent) { root_ = new RadioItem(this, RadioItem::Type_Service, kServiceName, parent); - root_->icon = QIcon(":magnatune.png"); + root_->icon = QIcon(":/providers/magnatune.png"); return root_; } diff --git a/src/radio/somafmservice.cpp b/src/radio/somafmservice.cpp index d34543258..bda276c68 100644 --- a/src/radio/somafmservice.cpp +++ b/src/radio/somafmservice.cpp @@ -51,7 +51,7 @@ SomaFMService::~SomaFMService() { RadioItem* SomaFMService::CreateRootItem(RadioItem* parent) { root_ = new RadioItem(this, RadioItem::Type_Service, kServiceName, parent); - root_->icon = QIcon(":somafm.png"); + root_->icon = QIcon(":/providers/somafm.png"); return root_; } diff --git a/src/songinfo/artistinfofetcher.cpp b/src/songinfo/artistinfofetcher.cpp index 2a7f1407a..9f326ffab 100644 --- a/src/songinfo/artistinfofetcher.cpp +++ b/src/songinfo/artistinfofetcher.cpp @@ -27,7 +27,7 @@ ArtistInfoFetcher::ArtistInfoFetcher(QObject* parent) void ArtistInfoFetcher::AddProvider(ArtistInfoProvider* provider) { providers_ << provider; connect(provider, SIGNAL(ImageReady(int,QUrl)), SIGNAL(ImageReady(int,QUrl))); - connect(provider, SIGNAL(InfoReady(int,QString,QWidget*)), SIGNAL(InfoReady(int,QString,QWidget*))); + connect(provider, SIGNAL(InfoReady(int,CollapsibleInfoPane::Data)), SIGNAL(InfoReady(int,CollapsibleInfoPane::Data))); } int ArtistInfoFetcher::FetchInfo(const QString& artist) { diff --git a/src/songinfo/artistinfofetcher.h b/src/songinfo/artistinfofetcher.h index 82e8076d8..e329391be 100644 --- a/src/songinfo/artistinfofetcher.h +++ b/src/songinfo/artistinfofetcher.h @@ -20,6 +20,8 @@ #include #include +#include "collapsibleinfopane.h" + class ArtistInfoProvider; class ArtistInfoFetcher : public QObject { @@ -32,7 +34,7 @@ public: signals: void ImageReady(int id, const QUrl& url); - void InfoReady(int id, const QString& title, QWidget* widget); + void InfoReady(int id, const CollapsibleInfoPane::Data&); private: void AddProvider(ArtistInfoProvider* provider); diff --git a/src/songinfo/artistinfoprovider.h b/src/songinfo/artistinfoprovider.h index 9bcbd6458..a42caebb5 100644 --- a/src/songinfo/artistinfoprovider.h +++ b/src/songinfo/artistinfoprovider.h @@ -20,6 +20,8 @@ #include #include +#include "collapsibleinfopane.h" + class ArtistInfoProvider : public QObject { Q_OBJECT @@ -30,7 +32,7 @@ public: signals: void ImageReady(int id, const QUrl& url); - void InfoReady(int id, const QString& title, QWidget* widget); + void InfoReady(int id, const CollapsibleInfoPane::Data& data); }; #endif // ARTISTINFOPROVIDER_H diff --git a/src/songinfo/artistinfoview.cpp b/src/songinfo/artistinfoview.cpp index c5a1db6bd..8a66aed48 100644 --- a/src/songinfo/artistinfoview.cpp +++ b/src/songinfo/artistinfoview.cpp @@ -30,10 +30,12 @@ ArtistInfoView::ArtistInfoView(NetworkAccessManager* network, QWidget *parent) fetcher_(new ArtistInfoFetcher(this)), current_request_id_(-1), scroll_area_(new QScrollArea), - container_(new QVBoxLayout) + container_(new QVBoxLayout), + image_view_(NULL), + section_container_(NULL) { connect(fetcher_, SIGNAL(ImageReady(int,QUrl)), SLOT(ImageReady(int,QUrl))); - connect(fetcher_, SIGNAL(InfoReady(int,QString,QWidget*)), SLOT(InfoReady(int,QString,QWidget*))); + connect(fetcher_, SIGNAL(InfoReady(int,CollapsibleInfoPane::Data)), SLOT(InfoReady(int,CollapsibleInfoPane::Data))); // Add the top-level scroll area setLayout(new QVBoxLayout); @@ -47,6 +49,7 @@ ArtistInfoView::ArtistInfoView(NetworkAccessManager* network, QWidget *parent) container_widget->setBackgroundRole(QPalette::Base); container_->setSizeConstraint(QLayout::SetMinAndMaxSize); container_->setContentsMargins(0, 0, 0, 0); + container_->setSpacing(0); scroll_area_->setWidget(container_widget); scroll_area_->setWidgetResizable(true); @@ -62,23 +65,43 @@ ArtistInfoView::ArtistInfoView(NetworkAccessManager* network, QWidget *parent) ArtistInfoView::~ArtistInfoView() { } -void ArtistInfoView::AddChild(QWidget* widget) { - children_ << widget; - container_->insertWidget(container_->count() - 1, widget); - widget->show(); +void ArtistInfoView::AddSection(CollapsibleInfoPane* section) { + int index = 0; + for ( ; indexdata() < sections_[index]->data()) + break; + } + + sections_.insert(index, section); + qobject_cast(section_container_->layout())->insertWidget(index, section); + section->show(); } void ArtistInfoView::Clear() { - qDeleteAll(children_); - children_.clear(); + delete image_view_; + delete section_container_; + sections_.clear(); +} + +bool ArtistInfoView::NeedsUpdate(const Song& old_metadata, const Song& new_metadata) const { + return old_metadata.artist() != new_metadata.artist(); } void ArtistInfoView::Update(const Song& metadata) { Clear(); current_request_id_ = fetcher_->FetchInfo(metadata.artist()); + // Image view goes at the top image_view_ = new PrettyImageView(network_); - AddChild(image_view_); + container_->insertWidget(0, image_view_); + + // Container for collapsable sections goes below + section_container_ = new QWidget; + section_container_->setLayout(new QVBoxLayout); + section_container_->layout()->setContentsMargins(0, 0, 0, 0); + section_container_->layout()->setSpacing(1); + section_container_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + container_->insertWidget(1, section_container_); } void ArtistInfoView::ImageReady(int id, const QUrl& url) { @@ -88,14 +111,11 @@ void ArtistInfoView::ImageReady(int id, const QUrl& url) { image_view_->AddImage(url); } -void ArtistInfoView::InfoReady(int id, const QString& title, QWidget* widget) { +void ArtistInfoView::InfoReady(int id, const CollapsibleInfoPane::Data& data) { if (id != current_request_id_) { - delete widget; + delete data.contents_; return; } - CollapsibleInfoPane* pane = new CollapsibleInfoPane(this); - pane->SetTitle(title); - pane->SetWidget(widget); - AddChild(pane); + AddSection(new CollapsibleInfoPane(data, this)); } diff --git a/src/songinfo/artistinfoview.h b/src/songinfo/artistinfoview.h index 4a395ad85..1648cb2a2 100644 --- a/src/songinfo/artistinfoview.h +++ b/src/songinfo/artistinfoview.h @@ -17,6 +17,7 @@ #ifndef ARTISTINFOVIEW_H #define ARTISTINFOVIEW_H +#include "collapsibleinfopane.h" #include "songinfobase.h" class ArtistInfoFetcher; @@ -34,14 +35,15 @@ public: protected: void Update(const Song& metadata); + bool NeedsUpdate(const Song& old_metadata, const Song& new_metadata) const; private: - void AddChild(QWidget* widget); + void AddSection(CollapsibleInfoPane* section); void Clear(); private slots: void ImageReady(int id, const QUrl& url); - void InfoReady(int id, const QString& title, QWidget* widget); + void InfoReady(int id, const CollapsibleInfoPane::Data& data); private: ArtistInfoFetcher* fetcher_; @@ -51,7 +53,8 @@ private: QVBoxLayout* container_; PrettyImageView* image_view_; - QList children_; + QWidget* section_container_; + QList sections_; }; #endif // ARTISTINFOVIEW_H diff --git a/src/songinfo/collapsibleinfoheader.cpp b/src/songinfo/collapsibleinfoheader.cpp index ec5b61b76..611b5fc56 100644 --- a/src/songinfo/collapsibleinfoheader.cpp +++ b/src/songinfo/collapsibleinfoheader.cpp @@ -22,6 +22,7 @@ #include const int CollapsibleInfoHeader::kHeight = 20; +const int CollapsibleInfoHeader::kIconSize = 16; CollapsibleInfoHeader::CollapsibleInfoHeader(QWidget* parent) : QWidget(parent), @@ -38,6 +39,11 @@ void CollapsibleInfoHeader::SetTitle(const QString& title) { update(); } +void CollapsibleInfoHeader::SetIcon(const QIcon& icon) { + icon_ = icon; + update(); +} + void CollapsibleInfoHeader::SetExpanded(bool expanded) { expanded_ = expanded; @@ -62,8 +68,9 @@ void CollapsibleInfoHeader::paintEvent(QPaintEvent* e) { QPainter p(this); QRect indicator_rect(0, 0, height(), height()); + QRect icon_rect(height() + 2, (kHeight - kIconSize) / 2, kIconSize, kIconSize); QRect text_rect(rect()); - text_rect.setLeft(height() + 6); + text_rect.setLeft(icon_rect.right() + 4); // Draw the background const QColor bg_color_1(palette().color(QPalette::Highlight).lighter(120)); @@ -96,6 +103,9 @@ void CollapsibleInfoHeader::paintEvent(QPaintEvent* e) { // will trigger QStyleSheetStyle's recursion guard (I don't know why). QApplication::style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, &p, this); + // Draw the icon + p.drawPixmap(icon_rect, icon_.pixmap(kIconSize)); + // Draw the title text QFont bold_font(font()); bold_font.setBold(true); diff --git a/src/songinfo/collapsibleinfoheader.h b/src/songinfo/collapsibleinfoheader.h index c74b0c6d8..9e91525d0 100644 --- a/src/songinfo/collapsibleinfoheader.h +++ b/src/songinfo/collapsibleinfoheader.h @@ -17,6 +17,7 @@ #ifndef COLLAPSIBLEINFOHEADER_H #define COLLAPSIBLEINFOHEADER_H +#include #include class CollapsibleInfoHeader : public QWidget { @@ -26,14 +27,17 @@ public: CollapsibleInfoHeader(QWidget* parent = 0); static const int kHeight; + static const int kIconSize; bool expanded() const { return expanded_; } bool hovering() const { return hovering_; } const QString& title() const { return title_; } + const QIcon& icon() const { return icon_; } public slots: void SetExpanded(bool expanded); void SetTitle(const QString& title); + void SetIcon(const QIcon& icon); signals: void Expanded(); @@ -50,6 +54,7 @@ private: bool expanded_; bool hovering_; QString title_; + QIcon icon_; }; #endif // COLLAPSIBLEINFOHEADER_H diff --git a/src/songinfo/collapsibleinfopane.cpp b/src/songinfo/collapsibleinfopane.cpp index 99f60805c..f52808317 100644 --- a/src/songinfo/collapsibleinfopane.cpp +++ b/src/songinfo/collapsibleinfopane.cpp @@ -19,10 +19,10 @@ #include -CollapsibleInfoPane::CollapsibleInfoPane(QWidget* parent) +CollapsibleInfoPane::CollapsibleInfoPane(const Data& data, QWidget* parent) : QWidget(parent), - header_(new CollapsibleInfoHeader(this)), - widget_(NULL) + data_(data), + header_(new CollapsibleInfoHeader(this)) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -31,24 +31,16 @@ CollapsibleInfoPane::CollapsibleInfoPane(QWidget* parent) setLayout(layout); layout->addWidget(header_); + layout->addWidget(data.contents_); + + header_->SetTitle(data.title_); + header_->SetIcon(data.icon_); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); connect(header_, SIGNAL(ExpandedToggled(bool)), SLOT(ExpandedToggled(bool))); } -void CollapsibleInfoPane::SetTitle(const QString& title) { - header_->SetTitle(title); -} - -void CollapsibleInfoPane::SetWidget(QWidget* widget) { - if (widget_) - delete widget_; - - widget_ = widget; - layout()->addWidget(widget); -} - void CollapsibleInfoPane::Collapse() { header_->SetExpanded(false); } @@ -58,5 +50,12 @@ void CollapsibleInfoPane::Expand() { } void CollapsibleInfoPane::ExpandedToggled(bool expanded) { - widget_->setVisible(expanded); + data_.contents_->setVisible(expanded); +} + +bool CollapsibleInfoPane::Data::operator <(const CollapsibleInfoPane::Data& other) const { + const int my_score = type_ * 1000 + relevance_; + const int other_score = other.type_ * 1000 + other.relevance_; + + return my_score > other_score; } diff --git a/src/songinfo/collapsibleinfopane.h b/src/songinfo/collapsibleinfopane.h index 8847de39b..4c029aab1 100644 --- a/src/songinfo/collapsibleinfopane.h +++ b/src/songinfo/collapsibleinfopane.h @@ -17,6 +17,7 @@ #ifndef COLLAPSIBLEINFOPANE_H #define COLLAPSIBLEINFOPANE_H +#include #include class CollapsibleInfoHeader; @@ -25,10 +26,28 @@ class CollapsibleInfoPane : public QWidget { Q_OBJECT public: - CollapsibleInfoPane(QWidget* parent = 0); + struct Data { + Data() : type_(Type_Biography), relevance_(0) {} - void SetTitle(const QString& title); - void SetWidget(QWidget* widget); + bool operator <(const Data& other) const; + + enum Type { + Type_Biography = 0, + + TypeCount + }; + + QString title_; + QIcon icon_; + Type type_; + int relevance_; + + QWidget* contents_; + }; + + CollapsibleInfoPane(const Data& data, QWidget* parent = 0); + + const Data& data() const { return data_; } public slots: void Expand(); @@ -38,8 +57,8 @@ private slots: void ExpandedToggled(bool expanded); private: + Data data_; CollapsibleInfoHeader* header_; - QWidget* widget_; }; #endif // COLLAPSIBLEINFOPANE_H diff --git a/src/songinfo/echonestartistinfo.cpp b/src/songinfo/echonestartistinfo.cpp index 0dc2602b7..ec4b8c2b9 100644 --- a/src/songinfo/echonestartistinfo.cpp +++ b/src/songinfo/echonestartistinfo.cpp @@ -32,6 +32,20 @@ struct EchoNestArtistInfo::Request { EchoNestArtistInfo::EchoNestArtistInfo(QObject* parent) : ArtistInfoProvider(parent) { + site_relevance_["wikipedia"] = 100; + site_relevance_["last.fm"] = 60; + site_relevance_["lastfm"] = 60; + site_relevance_["amazon"] = 30; + + site_icons_["amazon"] = QIcon(":/providers/amazon.png"); + site_icons_["aol"] = QIcon(":/providers/aol.png"); + site_icons_["cdbaby"] = QIcon(":/providers/cdbaby.png"); + site_icons_["lastfm"] = QIcon(":/last.fm/as.png"); + site_icons_["last.fm"] = QIcon(":/last.fm/as.png"); + site_icons_["mog"] = QIcon(":/providers/mog.png"); + site_icons_["mtvmusic"] = QIcon(":/providers/mtvmusic.png"); + site_icons_["myspace"] = QIcon(":/providers/myspace.png"); + site_icons_["wikipedia"] = QIcon(":/providers/wikipedia.png"); } void EchoNestArtistInfo::FetchInfo(int id, const QString& artist_name) { @@ -84,10 +98,27 @@ void EchoNestArtistInfo::ImagesFinished() { void EchoNestArtistInfo::BiographiesFinished() { RequestPtr request = ReplyFinished(qobject_cast(sender())); - foreach (const Echonest::Biography& bio, request->artist_->biographies()) { - QTextEdit* editor = new AutoSizedTextEdit; - editor->setHtml(bio.text()); + QSet already_seen; - emit InfoReady(request->id_, tr("Biography from %1").arg(bio.site()), editor); + foreach (const Echonest::Biography& bio, request->artist_->biographies()) { + if (already_seen.contains(bio.text())) + continue; + already_seen.insert(bio.text()); + + CollapsibleInfoPane::Data data; + data.title_ = tr("Biography from %1").arg(bio.site()); + data.type_ = CollapsibleInfoPane::Data::Type_Biography; + + const QString site = bio.site().toLower(); + if (site_relevance_.contains(site)) + data.relevance_ = site_relevance_[site]; + if (site_icons_.contains(site)) + data.icon_ = site_icons_[site]; + + AutoSizedTextEdit* editor = new AutoSizedTextEdit; + editor->setHtml(bio.text()); + data.contents_ = editor; + + emit InfoReady(request->id_, data); } } diff --git a/src/songinfo/echonestartistinfo.h b/src/songinfo/echonestartistinfo.h index cb174deb1..7a344d187 100644 --- a/src/songinfo/echonestartistinfo.h +++ b/src/songinfo/echonestartistinfo.h @@ -19,6 +19,8 @@ #include "artistinfoprovider.h" +#include + #include class QNetworkReply; @@ -44,6 +46,8 @@ private: private: QList requests_; + QMap site_relevance_; + QMap site_icons_; }; #endif // ECHONESTARTISTINFO_H diff --git a/src/songinfo/songinfobase.cpp b/src/songinfo/songinfobase.cpp index cd7b9e154..8d473bd71 100644 --- a/src/songinfo/songinfobase.cpp +++ b/src/songinfo/songinfobase.cpp @@ -25,7 +25,7 @@ SongInfoBase::SongInfoBase(NetworkAccessManager* network, QWidget* parent) void SongInfoBase::SongChanged(const Song& metadata) { if (isVisible()) { - Update(metadata); + MaybeUpdate(metadata); dirty_ = false; } else { queued_metadata_ = metadata; @@ -39,8 +39,19 @@ void SongInfoBase::SongFinished() { void SongInfoBase::showEvent(QShowEvent* e) { if (dirty_) { - Update(queued_metadata_); + MaybeUpdate(queued_metadata_); dirty_ = false; } QWidget::showEvent(e); } + +void SongInfoBase::MaybeUpdate(const Song& metadata) { + if (old_metadata_.is_valid()) { + if (!NeedsUpdate(old_metadata_, metadata)) { + return; + } + } + + Update(metadata); + old_metadata_ = metadata; +} diff --git a/src/songinfo/songinfobase.h b/src/songinfo/songinfobase.h index 4e3ee3c3a..0e9885964 100644 --- a/src/songinfo/songinfobase.h +++ b/src/songinfo/songinfobase.h @@ -37,11 +37,16 @@ protected: void showEvent(QShowEvent* e); virtual void Update(const Song& metadata) {} + virtual bool NeedsUpdate(const Song& old_metadata, const Song& new_metadata) const { return true; } NetworkAccessManager* network_; +private: + void MaybeUpdate(const Song& metadata); + private: Song queued_metadata_; + Song old_metadata_; bool dirty_; }; diff --git a/src/ui/settingsdialog.ui b/src/ui/settingsdialog.ui index 3e245ae8a..106dd6e35 100644 --- a/src/ui/settingsdialog.ui +++ b/src/ui/settingsdialog.ui @@ -99,7 +99,7 @@ - :/magnatune.png:/magnatune.png + :/providers/magnatune.png:/providers/magnatune.png diff --git a/src/widgets/autosizedtextedit.cpp b/src/widgets/autosizedtextedit.cpp index 1d6103456..d29a9986e 100644 --- a/src/widgets/autosizedtextedit.cpp +++ b/src/widgets/autosizedtextedit.cpp @@ -16,6 +16,8 @@ #include "autosizedtextedit.h" +#include + AutoSizedTextEdit::AutoSizedTextEdit(QWidget* parent) : QTextEdit(parent) { @@ -28,8 +30,14 @@ void AutoSizedTextEdit::resizeEvent(QResizeEvent* e) { document()->setTextWidth(w); setMinimumHeight(document()->size().height()); + + QTextEdit::resizeEvent(e); } QSize AutoSizedTextEdit::sizeHint() const { return minimumSize(); } + +void AutoSizedTextEdit::wheelEvent(QWheelEvent* e) { + e->ignore(); +} diff --git a/src/widgets/autosizedtextedit.h b/src/widgets/autosizedtextedit.h index 766c715dd..c9f09fb10 100644 --- a/src/widgets/autosizedtextedit.h +++ b/src/widgets/autosizedtextedit.h @@ -29,6 +29,7 @@ public: protected: void resizeEvent(QResizeEvent* e); + void wheelEvent(QWheelEvent* e); }; #endif // AUTOSIZEDTEXTEDIT_H diff --git a/src/widgets/osd.cpp b/src/widgets/osd.cpp index 186a99b06..cfef14abb 100644 --- a/src/widgets/osd.cpp +++ b/src/widgets/osd.cpp @@ -152,7 +152,7 @@ void OSD::MagnatuneDownloadFinished(const QStringList& albums) { message = tr("%1 albums").arg(albums.count()); ShowMessage(tr("Magnatune download finished"), message, QString(), - QImage(":magnatune.png")); + QImage(":/providers/magnatune.png")); } void OSD::ShowMessage(const QString& summary,