1 /*
2  * Copyright (C) 2019-2021 Ashar Khan <ashar786khan@gmail.com>
3  *
4  * This file is part of CP Editor.
5  *
6  * CP Editor is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * I will not be responsible if CP Editor behaves in unexpected way and
12  * causes your ratings to go down and or lose any important contest.
13  *
14  * Believe Software is "Software" and it isn't immune to bugs.
15  *
16  */
17 
18 #include "Settings/PreferencesPage.hpp"
19 #include "Settings/SettingsManager.hpp"
20 #include "Settings/ValueWrapper.hpp"
21 #include <QApplication>
22 #include <QFormLayout>
23 #include <QLabel>
24 #include <QMessageBox>
25 #include <QPushButton>
26 #include <QScrollArea>
27 #include <QStyle>
28 #include <QVBoxLayout>
29 
PreferencesPage(QWidget * parent)30 PreferencesPage::PreferencesPage(QWidget *parent) : QWidget(parent)
31 {
32     // construct widgets
33     auto *mainLayout = new QVBoxLayout(this);
34     titleLabel = new QLabel();
35     scrollArea = new QScrollArea();
36     scrollAreaWidget = new QWidget();
37     settingsLayout = new QVBoxLayout(scrollAreaWidget);
38     auto *buttonsLayout = new QHBoxLayout();
39     defaultButton =
40         new QPushButton(QApplication::style()->standardIcon(QStyle::SP_FileDialogDetailedView), tr("Default"));
41     defaultButton->setShortcut({"Ctrl+D"});
42     resetButton = new QPushButton(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton), tr("Reset"));
43     resetButton->setShortcut({"Ctrl+R"});
44     applyButton = new QPushButton(QApplication::style()->standardIcon(QStyle::SP_DialogApplyButton), tr("Apply"));
45     applyButton->setShortcut({"Ctrl+S"});
46 
47     // set up the UI
48     buttonsLayout->addWidget(defaultButton);
49     buttonsLayout->addWidget(resetButton);
50     buttonsLayout->addStretch();
51     buttonsLayout->addWidget(applyButton);
52     scrollArea->setWidgetResizable(true);
53     scrollArea->setWidget(scrollAreaWidget);
54     mainLayout->addWidget(titleLabel);
55     mainLayout->addWidget(scrollArea);
56     mainLayout->addLayout(buttonsLayout);
57 
58     // add tooltips
59     defaultButton->setToolTip(tr("Restore the default settings on the current page. (Ctrl+D)"));
60     resetButton->setToolTip(tr("Discard all changes on the current page. (Ctrl+R)"));
61     applyButton->setToolTip(tr("Save the changes on the current page. (Ctrl+S)"));
62 
63     // set the font for title
64     auto labelFont = font();
65     labelFont.setPointSizeF(font().pointSizeF() * 1.6);
66     titleLabel->setFont(labelFont);
67 
68     // connect the signals and slots
69     connect(defaultButton, &QPushButton::clicked, this, &PreferencesPage::loadDefault);
70     connect(resetButton, &QPushButton::clicked, this, &PreferencesPage::loadSettings);
71     connect(applyButton, &QPushButton::clicked, this, &PreferencesPage::applySettings);
72 }
73 
aboutToExit()74 bool PreferencesPage::aboutToExit()
75 {
76     // exit if there is no unsaved change
77     if (!areSettingsChanged())
78         return true;
79 
80     // otherwise, ask the user whether to save, discard or cancel
81     auto response = QMessageBox::warning(
82         this, tr("Unsaved Settings"), tr("The settings are changed. Do you want to save the settings or discard them?"),
83         QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
84 
85     switch (response)
86     {
87     case QMessageBox::Save:
88         applySettings();
89         return true;
90     case QMessageBox::Discard:
91         loadSettings();
92         return true;
93     case QMessageBox::Cancel:
94         return false;
95     default:
96         Q_UNREACHABLE();
97         break;
98     }
99 }
100 
path() const101 QString PreferencesPage::path() const
102 {
103     return m_path;
104 }
105 
trPath() const106 QString PreferencesPage::trPath() const
107 {
108     return m_trPath;
109 }
110 
setPath(const QString & path,const QString & trPath)111 void PreferencesPage::setPath(const QString &path, const QString &trPath)
112 {
113     m_path = path;
114     m_trPath = trPath;
115     emit pathChanged(m_path);
116 }
117 
setTitle(const QString & title)118 void PreferencesPage::setTitle(const QString &title)
119 {
120     titleLabel->setText(title);
121 }
122 
content()123 QStringList PreferencesPage::content()
124 {
125     return QStringList();
126 }
127 
loadSettings()128 void PreferencesPage::loadSettings()
129 {
130     makeUITheSameAsSettings();
131     updateButtons();
132 }
133 
addLayout(QLayout * layout)134 void PreferencesPage::addLayout(QLayout *layout)
135 {
136     settingsLayout->addLayout(layout);
137 }
138 
addWidget(QWidget * widget)139 void PreferencesPage::addWidget(QWidget *widget)
140 {
141     settingsLayout->addWidget(widget);
142 }
143 
addItem(QLayoutItem * item)144 void PreferencesPage::addItem(QLayoutItem *item)
145 {
146     settingsLayout->addItem(item);
147 }
148 
registerWidget(const QString & key,ValueWidget * widget) const149 void PreferencesPage::registerWidget(const QString &key, ValueWidget *widget) const
150 {
151     // PreferencesPageTemplate::PreferencesPageTemplate uses Qt::DirectConnection
152     QObject::connect(widget, &ValueWidget::valueChanged, this, &PreferencesPage::updateButtons, Qt::QueuedConnection);
153 
154     SettingsManager::setWidget(key, widget->coreWidget());
155 }
156 
loadDefault()157 void PreferencesPage::loadDefault()
158 {
159     makeUITheSameAsDefault();
160     updateButtons();
161 }
162 
applySettings()163 void PreferencesPage::applySettings()
164 {
165     makeSettingsTheSameAsUI();
166     updateButtons();
167     emit settingsApplied(m_path);
168 }
169 
updateButtons()170 void PreferencesPage::updateButtons()
171 {
172     bool changed = areSettingsChanged();
173     resetButton->setEnabled(changed);
174     applyButton->setEnabled(changed);
175 }
176