/* * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome * Copyright 2019-2021, 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 "core/application.h" #include "core/player.h" #include "widgets/groupediconview.h" #include "collection/collectionmodel.h" #include "settingsdialog.h" #include "settingspage.h" #include "behavioursettingspage.h" #include "collectionsettingspage.h" #include "backendsettingspage.h" #include "playlistsettingspage.h" #include "scrobblersettingspage.h" #include "coverssettingspage.h" #include "lyricssettingspage.h" #include "transcodersettingspage.h" #include "networkproxysettingspage.h" #include "appearancesettingspage.h" #include "contextsettingspage.h" #include "notificationssettingspage.h" #include "globalshortcutssettingspage.h" #ifdef HAVE_MOODBAR # include "moodbarsettingspage.h" #endif #ifdef HAVE_SUBSONIC # include "subsonicsettingspage.h" #endif #ifdef HAVE_TIDAL # include "tidalsettingspage.h" #endif #ifdef HAVE_QOBUZ # include "qobuzsettingspage.h" #endif #include "ui_settingsdialog.h" const char *SettingsDialog::kSettingsGroup = "SettingsDialog"; SettingsItemDelegate::SettingsItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {} QSize SettingsItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &idx) const { const bool is_separator = idx.data(SettingsDialog::Role_IsSeparator).toBool(); QSize ret = QStyledItemDelegate::sizeHint(option, idx); if (is_separator) { ret.setHeight(ret.height() * 2); } return ret; } void SettingsItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const { const bool is_separator = idx.data(SettingsDialog::Role_IsSeparator).toBool(); if (is_separator) { GroupedIconView::DrawHeader(painter, option.rect, option.font, option.palette, idx.data().toString()); } else { QStyledItemDelegate::paint(painter, option, idx); } } SettingsDialog::SettingsDialog(Application *app, OSDBase *osd, QMainWindow *mainwindow, QWidget *parent) : QDialog(parent), mainwindow_(mainwindow), app_(app), osd_(osd), player_(app_->player()), engine_(app_->player()->engine()), model_(app_->collection_model()->directory_model()), manager_(nullptr), ui_(new Ui_SettingsDialog), loading_settings_(false) { ui_->setupUi(this); ui_->list->setItemDelegate(new SettingsItemDelegate(this)); QTreeWidgetItem *general = AddCategory(tr("General")); AddPage(Page_Behaviour, new BehaviourSettingsPage(this, this), general); AddPage(Page_Collection, new CollectionSettingsPage(this, this), general); AddPage(Page_Backend, new BackendSettingsPage(this, this), general); AddPage(Page_Playlist, new PlaylistSettingsPage(this, this), general); AddPage(Page_Scrobbler, new ScrobblerSettingsPage(this, this), general); AddPage(Page_Covers, new CoversSettingsPage(this, this), general); AddPage(Page_Lyrics, new LyricsSettingsPage(this, this), general); #ifdef HAVE_GSTREAMER AddPage(Page_Transcoding, new TranscoderSettingsPage(this, this), general); #endif AddPage(Page_Proxy, new NetworkProxySettingsPage(this, this), general); QTreeWidgetItem *iface = AddCategory(tr("User interface")); AddPage(Page_Appearance, new AppearanceSettingsPage(this, this), iface); AddPage(Page_Context, new ContextSettingsPage(this, this), iface); AddPage(Page_Notifications, new NotificationsSettingsPage(this, this), iface); #ifdef HAVE_GLOBALSHORTCUTS AddPage(Page_GlobalShortcuts, new GlobalShortcutsSettingsPage(this, this), iface); #endif #ifdef HAVE_MOODBAR AddPage(Page_Moodbar, new MoodbarSettingsPage(this, this), iface); #endif #if defined(HAVE_SUBSONIC) || defined(HAVE_TIDAL) || defined(HAVE_QOBUZ) QTreeWidgetItem *streaming = AddCategory(tr("Streaming")); #endif #ifdef HAVE_SUBSONIC AddPage(Page_Subsonic, new SubsonicSettingsPage(this, this), streaming); #endif #ifdef HAVE_TIDAL AddPage(Page_Tidal, new TidalSettingsPage(this, this), streaming); #endif #ifdef HAVE_QOBUZ AddPage(Page_Qobuz, new QobuzSettingsPage(this, this), streaming); #endif // List box QObject::connect(ui_->list, &QTreeWidget::currentItemChanged, this, &SettingsDialog::CurrentItemChanged); ui_->list->setCurrentItem(pages_[Page_Behaviour].item_); // Make sure the list is big enough to show all the items ui_->list->setMinimumWidth(qobject_cast(ui_->list)->sizeHintForColumn(0)); // clazy:exclude=unneeded-cast ui_->buttonBox->button(QDialogButtonBox::Cancel)->setShortcut(QKeySequence::Close); QObject::connect(ui_->buttonBox, &QDialogButtonBox::clicked, this, &SettingsDialog::DialogButtonClicked); } SettingsDialog::~SettingsDialog() { delete ui_; } void SettingsDialog::showEvent(QShowEvent *e) { if (!e->spontaneous()) { LoadGeometry(); // Load settings loading_settings_ = true; QList pages = pages_.values(); for (const PageData &page : pages) { page.page_->Load(); } loading_settings_ = false; } QDialog::showEvent(e); } void SettingsDialog::closeEvent(QCloseEvent*) { SaveGeometry(); } void SettingsDialog::accept() { QList pages = pages_.values(); for (const PageData &page : pages) { page.page_->Accept(); } emit ReloadSettings(); SaveGeometry(); QDialog::accept(); } void SettingsDialog::reject() { // Notify each page that user clicks on Cancel QList pages = pages_.values(); for (const PageData &page : pages) { page.page_->Reject(); } SaveGeometry(); QDialog::reject(); } void SettingsDialog::LoadGeometry() { QSettings s; s.beginGroup(kSettingsGroup); if (s.contains("geometry")) { restoreGeometry(s.value("geometry").toByteArray()); } s.endGroup(); // Center the dialog on the same screen as mainwindow. #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QScreen *screen = mainwindow_->screen(); #else QScreen *screen = (mainwindow_->window() && mainwindow_->window()->windowHandle() ? mainwindow_->window()->windowHandle()->screen() : nullptr); #endif if (screen) { const QRect sr = screen->availableGeometry(); const QRect wr({}, size().boundedTo(sr.size())); resize(wr.size()); move(sr.center() - wr.center()); } } void SettingsDialog::SaveGeometry() { QSettings s; s.beginGroup(kSettingsGroup); s.setValue("geometry", saveGeometry()); s.endGroup(); } QTreeWidgetItem *SettingsDialog::AddCategory(const QString &name) { QTreeWidgetItem *item = new QTreeWidgetItem; item->setText(0, name); item->setData(0, Role_IsSeparator, true); item->setFlags(Qt::ItemIsEnabled); ui_->list->invisibleRootItem()->addChild(item); item->setExpanded(true); return item; } void SettingsDialog::AddPage(const Page id, SettingsPage *page, QTreeWidgetItem *parent) { if (!parent) parent = ui_->list->invisibleRootItem(); // Connect page's signals to the settings dialog's signals QObject::connect(page, &SettingsPage::NotificationPreview, this, &SettingsDialog::NotificationPreview); // Create the list item QTreeWidgetItem *item = new QTreeWidgetItem; item->setText(0, page->windowTitle()); item->setIcon(0, page->windowIcon()); item->setData(0, Role_IsSeparator, false); if (!page->IsEnabled()) { item->setFlags(Qt::NoItemFlags); } parent->addChild(item); // Create a scroll area containing the page QScrollArea *area = new QScrollArea; area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); area->setWidget(page); area->setWidgetResizable(true); area->setFrameShape(QFrame::NoFrame); area->setMinimumWidth(page->layout()->minimumSize().width()); // Add the page to the stack ui_->stacked_widget->addWidget(area); // Remember where the page is PageData page_data; page_data.item_ = item; page_data.scroll_area_ = area; page_data.page_ = page; pages_[id] = page_data; } void SettingsDialog::Save() { QList pages = pages_.values(); for (const PageData &page : pages) { page.page_->Apply(); } emit ReloadSettings(); } void SettingsDialog::DialogButtonClicked(QAbstractButton *button) { // While we only connect Apply at the moment, this might change in the future if (ui_->buttonBox->button(QDialogButtonBox::Apply) == button) { QList pages = pages_.values(); for (const PageData &page : pages) { page.page_->Apply(); } emit ReloadSettings(); } } void SettingsDialog::OpenAtPage(Page page) { if (!pages_.contains(page)) { return; } ui_->list->setCurrentItem(pages_[page].item_); show(); } void SettingsDialog::CurrentItemChanged(QTreeWidgetItem *item) { if (!(item->flags() & Qt::ItemIsSelectable)) { return; } // Set the title ui_->title->setText("" + item->text(0) + ""); // Display the right page QList pages = pages_.values(); for (const PageData &page : pages) { if (page.item_ == item) { ui_->stacked_widget->setCurrentWidget(page.scroll_area_); break; } } }