Implement core-side highlights
[quassel.git] / src / qtui / settingsdlg.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QIcon>
22 #include <QMessageBox>
23 #include <QPushButton>
24
25 #include "settingsdlg.h"
26
27 #include "client.h"
28
29 SettingsDlg::SettingsDlg(QWidget *parent)
30     : QDialog(parent),
31     _currentPage(0)
32 {
33     ui.setupUi(this);
34     setModal(true);
35     setAttribute(Qt::WA_DeleteOnClose, true);
36     setWindowIcon(QIcon::fromTheme("configure"));
37
38     updateGeometry();
39
40     ui.settingsTree->setRootIsDecorated(false);
41
42     connect(ui.settingsTree, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelected()));
43     connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonClicked(QAbstractButton *)));
44
45     connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), SLOT(coreConnectionStateChanged()));
46
47     setButtonStates();
48
49     // Some settings panes can take a good bit of space.  To avoid squashing the settings tree, try
50     // to resize the dialog.  If needed, it can always be resized by the user to take less space.
51     //
52     // Only try to resize if the sizes are valid.  This shouldn't happen.. but better to be safe.
53     // See http://www.qtcentre.org/threads/3427-invalid-sizeHint()
54     if (ui.settingsTree->sizeHint().isValid() && ui.settingsTree->size().isValid()) {
55         // Find out how much width would make the settings tree happy
56         int wantedExtraWidth = ui.settingsTree->sizeHint().width()
57                 - ui.settingsTree->size().width();
58         // If more space is needed, try to resize to allow for it.  Qt should keep the dialog within
59         // the bounds of the screen.
60         if (wantedExtraWidth > 0) {
61             this->resize(this->width() + wantedExtraWidth, this->height());
62         }
63     }
64 }
65
66
67 void SettingsDlg::coreConnectionStateChanged()
68 {
69     for (int i = 0; i < ui.settingsTree->topLevelItemCount(); i++) {
70         QTreeWidgetItem *catItem = ui.settingsTree->topLevelItem(i);
71         for (int j = 0; j < catItem->childCount(); j++) {
72             QTreeWidgetItem *item = catItem->child(j);
73             setItemState(item);
74         }
75         setItemState(catItem);
76     }
77 }
78
79
80 void SettingsDlg::setItemState(QTreeWidgetItem *item)
81 {
82     SettingsPage *sp = qobject_cast<SettingsPage *>(item->data(0, SettingsPageRole).value<QObject *>());
83     Q_ASSERT(sp);
84     item->setDisabled(!Client::isConnected() && sp->needsCoreConnection());
85 }
86
87
88 void SettingsDlg::registerSettingsPage(SettingsPage *sp)
89 {
90     sp->setParent(ui.settingsStack);
91     ui.settingsStack->addWidget(sp);
92
93     connect(sp, SIGNAL(changed(bool)), this, SLOT(setButtonStates()));
94
95     QTreeWidgetItem *cat;
96     QList<QTreeWidgetItem *> cats = ui.settingsTree->findItems(sp->category(), Qt::MatchExactly);
97     if (!cats.count()) {
98         cat = new QTreeWidgetItem(ui.settingsTree, QStringList(sp->category()));
99         cat->setExpanded(true);
100         cat->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
101     }
102     else {
103         cat = cats[0];
104     }
105
106     QTreeWidgetItem *item;
107     if (sp->title().isEmpty())
108         item = cat;
109     else
110         item = new QTreeWidgetItem(cat, QStringList(sp->title()));
111
112     item->setData(0, SettingsPageRole, QVariant::fromValue<QObject *>(sp));
113     ui.settingsTree->setMinimumWidth(ui.settingsTree->header()->sectionSizeHint(0) + 5);
114     pageIsLoaded[sp] = false;
115     if (!ui.settingsTree->selectedItems().count())
116         ui.settingsTree->setCurrentItem(item);
117
118     setItemState(item);
119 }
120
121
122 void SettingsDlg::selectPage(SettingsPage *sp)
123 {
124     if (!sp) {
125         _currentPage = 0;
126         ui.settingsStack->setCurrentIndex(0);
127         ui.pageTitle->setText(tr("Settings"));
128         return;
129     }
130
131     if (!pageIsLoaded[sp]) {
132         sp->load();
133         pageIsLoaded[sp] = true;
134     }
135
136     if (sp != currentPage() && currentPage() != 0 && currentPage()->hasChanged()) {
137         int ret = QMessageBox::warning(this, tr("Save changes"),
138             tr("There are unsaved changes on the current configuration page. Would you like to apply your changes now?"),
139             QMessageBox::Discard|QMessageBox::Save|QMessageBox::Cancel, QMessageBox::Cancel);
140         if (ret == QMessageBox::Save) {
141             if (!applyChanges()) sp = currentPage();
142         }
143         else if (ret == QMessageBox::Discard) {
144             undoChanges();
145         }
146         else sp = currentPage();
147     }
148
149     if (sp != currentPage()) {
150         if (sp->title().isEmpty()) {
151             ui.pageTitle->setText(sp->category());
152             setWindowTitle(tr("Configure %1").arg(sp->category()));
153         }
154         else {
155             ui.pageTitle->setText(sp->title());
156             setWindowTitle(tr("Configure %1").arg(sp->title()));
157         }
158
159         ui.settingsStack->setCurrentWidget(sp);
160         _currentPage = sp;
161     }
162     setButtonStates();
163 }
164
165
166 void SettingsDlg::itemSelected()
167 {
168     QList<QTreeWidgetItem *> items = ui.settingsTree->selectedItems();
169     SettingsPage *sp = 0;
170     if (!items.isEmpty()) {
171         sp = qobject_cast<SettingsPage *>(items[0]->data(0, SettingsPageRole).value<QObject *>());
172     }
173     selectPage(sp);
174 }
175
176
177 void SettingsDlg::setButtonStates()
178 {
179     SettingsPage *sp = currentPage();
180     ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(sp && sp->hasChanged());
181     ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(sp && sp->hasChanged());
182     ui.buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(sp && sp->hasDefaults());
183 }
184
185
186 void SettingsDlg::buttonClicked(QAbstractButton *button)
187 {
188     switch (ui.buttonBox->standardButton(button)) {
189     case QDialogButtonBox::Ok:
190         if (currentPage() && currentPage()->hasChanged()) {
191             if (applyChanges()) accept();
192         }
193         else accept();
194         break;
195     case QDialogButtonBox::Apply:
196         applyChanges();
197         break;
198     case QDialogButtonBox::Cancel:
199         undoChanges();
200         reject();
201         break;
202     case QDialogButtonBox::Reset:
203         reload();
204         break;
205     case QDialogButtonBox::RestoreDefaults:
206         loadDefaults();
207         break;
208     default:
209         break;
210     }
211 }
212
213
214 bool SettingsDlg::applyChanges()
215 {
216     if (!currentPage()) return false;
217     if (currentPage()->aboutToSave()) {
218         currentPage()->save();
219         return true;
220     }
221     return false;
222 }
223
224
225 void SettingsDlg::undoChanges()
226 {
227     if (currentPage()) {
228         currentPage()->load();
229     }
230 }
231
232
233 void SettingsDlg::reload()
234 {
235     if (!currentPage()) return;
236     int ret = QMessageBox::question(this, tr("Reload Settings"), tr("Do you like to reload the settings, undoing your changes on this page?"),
237         QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
238     if (ret == QMessageBox::Yes) {
239         currentPage()->load();
240     }
241 }
242
243
244 void SettingsDlg::loadDefaults()
245 {
246     if (!currentPage()) return;
247     int ret = QMessageBox::question(this, tr("Restore Defaults"), tr("Do you like to restore the default values for this page?"),
248         QMessageBox::RestoreDefaults|QMessageBox::Cancel, QMessageBox::Cancel);
249     if (ret == QMessageBox::RestoreDefaults) {
250         currentPage()->defaults();
251     }
252 }