Clementine-audio-player-Mac.../src/songinfo/songinfobase.cpp

227 lines
6.9 KiB
C++

/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "songinfobase.h"
#include "core/network.h"
#include <QFile>
#include <QScrollArea>
#include <QSettings>
#include <QSpacerItem>
#include <QTimer>
#include <QVBoxLayout>
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<QVBoxLayout*>(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<CollapsibleInfoPane::Data::Type, CollapsibleInfoPane*> types_;
QSet<CollapsibleInfoPane::Data::Type> 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<CollapsibleInfoPane*>(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)));
}
}