/* This file is part of Clementine. Copyright 2010, David Sansome Clementine 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. Clementine 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 Clementine. If not, see . */ #include "songinfobase.h" #include "core/network.h" #include #include #include #include #include #include const char* SongInfoBase::kSettingsGroup = "SongInfo"; SongInfoBase::SongInfoBase(QWidget* parent) : QWidget(parent), network_(new NetworkAccessManager(this)), fetcher_(new SongInfoFetcher(this)), current_request_id_(-1), scroll_area_(new QScrollArea), container_(new QVBoxLayout), section_container_(nullptr), fader_(new WidgetFadeHelper(this, 1000)), dirty_(false) { // Add the top-level scroll area setLayout(new QVBoxLayout); layout()->setContentsMargins(0, 0, 0, 0); layout()->addWidget(scroll_area_); // Add a container widget to the scroll area QWidget* container_widget = new QWidget; container_widget->setLayout(container_); container_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); container_widget->setBackgroundRole(QPalette::Base); container_->setSizeConstraint(QLayout::SetMinAndMaxSize); container_->setContentsMargins(0, 0, 0, 0); container_->setSpacing(6); scroll_area_->setWidget(container_widget); scroll_area_->setWidgetResizable(true); // Add a spacer to the bottom of the container container_->addStretch(); // Set stylesheet QFile stylesheet(":/songinfo.css"); stylesheet.open(QIODevice::ReadOnly); setStyleSheet(QString::fromAscii(stylesheet.readAll())); connect(fetcher_, SIGNAL(ResultReady(int, SongInfoFetcher::Result)), SLOT(ResultReady(int, SongInfoFetcher::Result))); connect(fetcher_, SIGNAL(InfoResultReady(int, CollapsibleInfoPane::Data)), SLOT(InfoResultReady(int, CollapsibleInfoPane::Data))); } void SongInfoBase::Clear() { fader_->StartFade(); qDeleteAll(widgets_); widgets_.clear(); delete section_container_; sections_.clear(); // Container for collapsible 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(0, section_container_); } void SongInfoBase::AddSection(CollapsibleInfoPane* section) { int index = 0; for (; index < sections_.count(); ++index) { if (section->data() < sections_[index]->data()) break; } ConnectWidget(section->data().contents_); sections_.insert(index, section); qobject_cast(section_container_->layout()) ->insertWidget(index, section); section->show(); } void SongInfoBase::AddWidget(QWidget* widget) { ConnectWidget(widget); container_->insertWidget(container_->count() - 2, widget); widgets_ << widget; } void SongInfoBase::SongChanged(const Song& metadata) { if (isVisible()) { MaybeUpdate(metadata); dirty_ = false; } else { queued_metadata_ = metadata; dirty_ = true; } } void SongInfoBase::SongFinished() { dirty_ = false; } void SongInfoBase::showEvent(QShowEvent* e) { if (dirty_) { 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; } void SongInfoBase::Update(const Song& metadata) { current_request_id_ = fetcher_->FetchInfo(metadata); // Do this after the new pane has been shown otherwise it'll just grab a // black rectangle. Clear(); QTimer::singleShot(0, fader_, SLOT(StartBlur())); } void SongInfoBase::InfoResultReady(int id, const CollapsibleInfoPane::Data& data) {} void SongInfoBase::ResultReady(int id, const SongInfoFetcher::Result& result) { for (const CollapsibleInfoPane::Data& data : result.info_) { delete data.contents_; } } void SongInfoBase::CollapseSections() { QSettings s; s.beginGroup(kSettingsGroup); // Sections are already sorted by type and relevance, so the algorithm we use // to determine which ones to show by default is: // * In the absence of any user preference, show the first (highest // relevance section of each type and hide the rest) // * If one or more sections in a type have been explicitly hidden/shown // by the user before then hide all sections in that type and show only // the ones that are explicitly shown. QMap types_; QSet has_user_preference_; for (CollapsibleInfoPane* pane : sections_) { const CollapsibleInfoPane::Data::Type type = pane->data().type_; types_.insertMulti(type, pane); QVariant preference = s.value(pane->data().id_); if (preference.isValid()) { has_user_preference_.insert(type); if (preference.toBool()) { pane->Expand(); } } } for (CollapsibleInfoPane::Data::Type type : types_.keys()) { if (!has_user_preference_.contains(type)) { // Expand the first one types_.values(type).last()->Expand(); } } for (CollapsibleInfoPane* pane : sections_) { connect(pane, SIGNAL(Toggled(bool)), SLOT(SectionToggled(bool))); } } void SongInfoBase::SectionToggled(bool value) { CollapsibleInfoPane* pane = qobject_cast(sender()); if (!pane || !sections_.contains(pane)) return; QSettings s; s.beginGroup(kSettingsGroup); s.setValue(pane->data().id_, value); } void SongInfoBase::ReloadSettings() { for (CollapsibleInfoPane* pane : sections_) { QWidget* contents = pane->data().contents_; if (!contents) continue; QMetaObject::invokeMethod(contents, "ReloadSettings"); } } void SongInfoBase::ConnectWidget(QWidget* widget) { const QMetaObject* m = widget->metaObject(); if (m->indexOfSignal("ShowSettingsDialog()") != -1) { connect(widget, SIGNAL(ShowSettingsDialog()), SIGNAL(ShowSettingsDialog())); } if (m->indexOfSignal("DoGlobalSearch(QString)") != -1) { connect(widget, SIGNAL(DoGlobalSearch(QString)), SIGNAL(DoGlobalSearch(QString))); } }