Only enable the relevant highlight depending on core features
[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     bool disabledDueToConnection = !Client::isConnected() && sp->needsCoreConnection();
85     bool disabledDueToOwnChoice = !sp->isSelectable();
86     item->setDisabled(disabledDueToConnection || disabledDueToOwnChoice);
87 }
88
89
90 void SettingsDlg::registerSettingsPage(SettingsPage *sp)
91 {
92     sp->setParent(ui.settingsStack);
93     ui.settingsStack->addWidget(sp);
94
95     connect(sp, SIGNAL(changed(bool)), this, SLOT(setButtonStates()));
96
97     QTreeWidgetItem *cat;
98     QList<QTreeWidgetItem *> cats = ui.settingsTree->findItems(sp->category(), Qt::MatchExactly);
99     if (!cats.count()) {
100         cat = new QTreeWidgetItem(ui.settingsTree, QStringList(sp->category()));
101         cat->setExpanded(true);
102         cat->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
103     }
104     else {
105         cat = cats[0];
106     }
107
108     QTreeWidgetItem *item;
109     if (sp->title().isEmpty())
110         item = cat;
111     else
112         item = new QTreeWidgetItem(cat, QStringList(sp->title()));
113
114     item->setData(0, SettingsPageRole, QVariant::fromValue<QObject *>(sp));
115     ui.settingsTree->setMinimumWidth(ui.settingsTree->header()->sectionSizeHint(0) + 5);
116     pageIsLoaded[sp] = false;
117     if (!ui.settingsTree->selectedItems().count())
118         ui.settingsTree->setCurrentItem(item);
119
120     setItemState(item);
121 }
122
123
124 void SettingsDlg::selectPage(SettingsPage *sp)
125 {
126     if (!sp) {
127         _currentPage = 0;
128         ui.settingsStack->setCurrentIndex(0);
129         ui.pageTitle->setText(tr("Settings"));
130         return;
131     }
132
133     if (!pageIsLoaded[sp]) {
134         sp->load();
135         pageIsLoaded[sp] = true;
136     }
137
138     if (sp != currentPage() && currentPage() != 0 && currentPage()->hasChanged()) {
139         int ret = QMessageBox::warning(this, tr("Save changes"),
140             tr("There are unsaved changes on the current configuration page. Would you like to apply your changes now?"),
141             QMessageBox::Discard|QMessageBox::Save|QMessageBox::Cancel, QMessageBox::Cancel);
142         if (ret == QMessageBox::Save) {
143             if (!applyChanges()) sp = currentPage();
144         }
145         else if (ret == QMessageBox::Discard) {
146             undoChanges();
147         }
148         else sp = currentPage();
149     }
150
151     if (sp != currentPage()) {
152         if (sp->title().isEmpty()) {
153             ui.pageTitle->setText(sp->category());
154             setWindowTitle(tr("Configure %1").arg(sp->category()));
155         }
156         else {
157             ui.pageTitle->setText(sp->title());
158             setWindowTitle(tr("Configure %1").arg(sp->title()));
159         }
160
161         ui.settingsStack->setCurrentWidget(sp);
162         _currentPage = sp;
163     }
164     setButtonStates();
165 }
166
167
168 void SettingsDlg::itemSelected()
169 {
170     QList<QTreeWidgetItem *> items = ui.settingsTree->selectedItems();
171     SettingsPage *sp = 0;
172     if (!items.isEmpty()) {
173         sp = qobject_cast<SettingsPage *>(items[0]->data(0, SettingsPageRole).value<QObject *>());
174     }
175     selectPage(sp);
176 }
177
178
179 void SettingsDlg::setButtonStates()
180 {
181     SettingsPage *sp = currentPage();
182     ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(sp && sp->hasChanged());
183     ui.buttonBox->button(QDialogButtonBox::Reset)->setEnabled(sp && sp->hasChanged());
184     ui.buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(sp && sp->hasDefaults());
185 }
186
187
188 void SettingsDlg::buttonClicked(QAbstractButton *button)
189 {
190     switch (ui.buttonBox->standardButton(button)) {
191     case QDialogButtonBox::Ok:
192         if (currentPage() && currentPage()->hasChanged()) {
193             if (applyChanges()) accept();
194         }
195         else accept();
196         break;
197     case QDialogButtonBox::Apply:
198         applyChanges();
199         break;
200     case QDialogButtonBox::Cancel:
201         undoChanges();
202         reject();
203         break;
204     case QDialogButtonBox::Reset:
205         reload();
206         break;
207     case QDialogButtonBox::RestoreDefaults:
208         loadDefaults();
209         break;
210     default:
211         break;
212     }
213 }
214
215
216 bool SettingsDlg::applyChanges()
217 {
218     if (!currentPage()) return false;
219     if (currentPage()->aboutToSave()) {
220         currentPage()->save();
221         return true;
222     }
223     return false;
224 }
225
226
227 void SettingsDlg::undoChanges()
228 {
229     if (currentPage()) {
230         currentPage()->load();
231     }
232 }
233
234
235 void SettingsDlg::reload()
236 {
237     if (!currentPage()) return;
238     int ret = QMessageBox::question(this, tr("Reload Settings"), tr("Do you like to reload the settings, undoing your changes on this page?"),
239         QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
240     if (ret == QMessageBox::Yes) {
241         currentPage()->load();
242     }
243 }
244
245
246 void SettingsDlg::loadDefaults()
247 {
248     if (!currentPage()) return;
249     int ret = QMessageBox::question(this, tr("Restore Defaults"), tr("Do you like to restore the default values for this page?"),
250         QMessageBox::RestoreDefaults|QMessageBox::Cancel, QMessageBox::Cancel);
251     if (ret == QMessageBox::RestoreDefaults) {
252         currentPage()->defaults();
253     }
254 }