modernize: Use override instead of virtual
[quassel.git] / src / qtui / settingspages / ignorelistsettingspage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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 "ignorelistsettingspage.h"
22
23 #include <QHeaderView>
24 #include <QItemSelectionModel>
25 #include <QModelIndex>
26 #include <QPainter>
27 #include <QMessageBox>
28 #include <QString>
29 #include <QEvent>
30 #include <QDebug>
31
32 #include "expressionmatch.h"
33
34 #include "icon.h"
35
36 IgnoreListSettingsPage::IgnoreListSettingsPage(QWidget *parent)
37     : SettingsPage(tr("IRC"), tr("Ignore List"), parent)
38 {
39     ui.setupUi(this);
40     _delegate = new IgnoreListDelegate(ui.ignoreListView);
41     ui.newIgnoreRuleButton->setIcon(icon::get("list-add"));
42     ui.deleteIgnoreRuleButton->setIcon(icon::get("edit-delete"));
43     ui.editIgnoreRuleButton->setIcon(icon::get("configure"));
44
45     ui.ignoreListView->setSelectionBehavior(QAbstractItemView::SelectRows);
46     ui.ignoreListView->setSelectionMode(QAbstractItemView::SingleSelection);
47     ui.ignoreListView->setAlternatingRowColors(true);
48     ui.ignoreListView->setTabKeyNavigation(false);
49     ui.ignoreListView->setModel(&_ignoreListModel);
50     // ui.ignoreListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
51
52     // ui.ignoreListView->setSortingEnabled(true);
53     ui.ignoreListView->verticalHeader()->hide();
54     ui.ignoreListView->hideColumn(1);
55     ui.ignoreListView->resizeColumnToContents(0);
56     ui.ignoreListView->horizontalHeader()->setStretchLastSection(true);
57     ui.ignoreListView->setItemDelegateForColumn(0, _delegate);
58     ui.ignoreListView->viewport()->setAttribute(Qt::WA_Hover);
59     ui.ignoreListView->viewport()->setMouseTracking(true);
60
61     connect(ui.ignoreListView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
62     connect(ui.newIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(newIgnoreRule()));
63     connect(ui.deleteIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(deleteSelectedIgnoreRule()));
64     connect(ui.editIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(editSelectedIgnoreRule()));
65     connect(&_ignoreListModel, SIGNAL(configChanged(bool)), this, SLOT(setChangedState(bool)));
66     connect(&_ignoreListModel, SIGNAL(modelReady(bool)), this, SLOT(enableDialog(bool)));
67
68     enableDialog(_ignoreListModel.isReady());
69 }
70
71
72 IgnoreListSettingsPage::~IgnoreListSettingsPage()
73 {
74     delete _delegate;
75 }
76
77
78 void IgnoreListSettingsPage::load()
79 {
80     if (_ignoreListModel.configChanged())
81         _ignoreListModel.revert();
82     ui.ignoreListView->selectionModel()->reset();
83     ui.editIgnoreRuleButton->setEnabled(false);
84 }
85
86
87 void IgnoreListSettingsPage::defaults()
88 {
89     _ignoreListModel.loadDefaults();
90 }
91
92
93 void IgnoreListSettingsPage::save()
94 {
95     if (_ignoreListModel.configChanged()) {
96         _ignoreListModel.commit();
97     }
98     ui.ignoreListView->selectionModel()->reset();
99     ui.editIgnoreRuleButton->setEnabled(false);
100 }
101
102
103 void IgnoreListSettingsPage::enableDialog(bool enabled)
104 {
105     ui.newIgnoreRuleButton->setEnabled(enabled);
106     setEnabled(enabled);
107 }
108
109
110 void IgnoreListSettingsPage::selectionChanged(const QItemSelection &selection, const QItemSelection &)
111 {
112     bool state = !selection.isEmpty();
113     ui.deleteIgnoreRuleButton->setEnabled(state);
114     ui.editIgnoreRuleButton->setEnabled(state);
115 }
116
117
118 void IgnoreListSettingsPage::deleteSelectedIgnoreRule()
119 {
120     if (!ui.ignoreListView->selectionModel()->hasSelection())
121         return;
122
123     _ignoreListModel.removeIgnoreRule(ui.ignoreListView->selectionModel()->selectedIndexes()[0].row());
124 }
125
126
127 void IgnoreListSettingsPage::newIgnoreRule(QString rule)
128 {
129     IgnoreListManager::IgnoreListItem newItem = IgnoreListManager::IgnoreListItem();
130     newItem.setStrictness(IgnoreListManager::SoftStrictness);
131     newItem.setScope(IgnoreListManager::GlobalScope);
132     newItem.setIsRegEx(false);
133     newItem.setIsEnabled(true);
134
135     bool enableOkButton = false;
136     if (!rule.isEmpty()) {
137         // we're called from contextmenu
138         newItem.setContents(rule);
139         enableOkButton = true;
140     }
141
142     IgnoreListEditDlg *dlg = new IgnoreListEditDlg(newItem, this, enableOkButton);
143     dlg->enableOkButton(enableOkButton);
144     while (dlg->exec() == QDialog::Accepted) {
145         if (!_ignoreListModel.newIgnoreRule(dlg->ignoreListItem())) {
146             if (QMessageBox::warning(this,
147                     tr("Rule already exists"),
148                     tr("There is already a rule\n\"%1\"\nPlease choose another rule.")
149                     .arg(dlg->ignoreListItem().contents()),
150                     QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok)
151                 == QMessageBox::Cancel)
152                 break;
153
154             IgnoreListManager::IgnoreListItem item = dlg->ignoreListItem();
155             delete dlg;
156             dlg = new IgnoreListEditDlg(item, this);
157         }
158         else {
159             break;
160         }
161     }
162     dlg->deleteLater();
163 }
164
165
166 void IgnoreListSettingsPage::editSelectedIgnoreRule()
167 {
168     if (!ui.ignoreListView->selectionModel()->hasSelection())
169         return;
170     int row = ui.ignoreListView->selectionModel()->selectedIndexes()[0].row();
171     IgnoreListEditDlg dlg(_ignoreListModel.ignoreListItemAt(row), this);
172     dlg.setAttribute(Qt::WA_DeleteOnClose, false);
173     if (dlg.exec() == QDialog::Accepted) {
174         _ignoreListModel.setIgnoreListItemAt(row, dlg.ignoreListItem());
175     }
176 }
177
178
179 void IgnoreListSettingsPage::editIgnoreRule(const QString &ignoreRule)
180 {
181     ui.ignoreListView->selectionModel()->select(_ignoreListModel.indexOf(ignoreRule), QItemSelectionModel::Select);
182     if (ui.ignoreListView->selectionModel()->hasSelection()) // && ui.ignoreListView->selectionModel()->selectedIndexes()[0].row() != -1)
183         editSelectedIgnoreRule();
184     else
185         newIgnoreRule(ignoreRule);
186 }
187
188
189 /*
190   IgnoreListDelegate
191 */
192 void IgnoreListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
193 {
194     if (index.column() == 0) {
195         QStyle *style = QApplication::style();
196         if (option.state & QStyle::State_Selected)
197             painter->fillRect(option.rect, option.palette.highlight());
198
199         QStyleOptionButton opts;
200         opts.direction = option.direction;
201         opts.rect = option.rect;
202         opts.rect.moveLeft(option.rect.center().rx()-10);
203         opts.state = option.state;
204         opts.state |= index.data().toBool() ? QStyle::State_On : QStyle::State_Off;
205         style->drawControl(QStyle::CE_CheckBox, &opts, painter);
206     }
207     else
208         QStyledItemDelegate::paint(painter, option, index);
209 }
210
211
212 // provide interactivity for the checkboxes
213 bool IgnoreListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
214     const QStyleOptionViewItem &option, const QModelIndex &index)
215 {
216     Q_UNUSED(option)
217     switch (event->type()) {
218     case QEvent::MouseButtonRelease:
219         model->setData(index, !index.data().toBool());
220         return true;
221     // don't show the default editor for the column
222     case QEvent::MouseButtonDblClick:
223         return true;
224     default:
225         return false;
226     }
227 }
228
229
230 /*
231   IgnoreListEditDlg
232 */
233 IgnoreListEditDlg::IgnoreListEditDlg(const IgnoreListManager::IgnoreListItem &item, QWidget *parent, bool enabled)
234     : QDialog(parent), _ignoreListItem(item), _hasChanged(enabled)
235 {
236     ui.setupUi(this);
237     setAttribute(Qt::WA_DeleteOnClose, false);
238     setModal(true);
239     // FIXME patch out the bugger completely if it's good without it
240     ui.isActiveCheckBox->hide();
241
242     // setup buttongroups
243     // this could be moved to .ui file with qt4.5
244     _typeButtonGroup.addButton(ui.senderTypeButton, 0);
245     _typeButtonGroup.addButton(ui.messageTypeButton, 1);
246     _typeButtonGroup.addButton(ui.ctcpTypeButton, 2);
247     _strictnessButtonGroup.addButton(ui.dynamicStrictnessButton, 0);
248     _strictnessButtonGroup.addButton(ui.permanentStrictnessButton, 1);
249     _scopeButtonGroup.addButton(ui.globalScopeButton, 0);
250     _scopeButtonGroup.addButton(ui.networkScopeButton, 1);
251     _scopeButtonGroup.addButton(ui.channelScopeButton, 2);
252
253     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
254
255     ui.ignoreRuleLineEdit->setText(item.contents());
256
257     if (item.type() == IgnoreListManager::MessageIgnore)
258         ui.messageTypeButton->setChecked(true);
259     else if (item.type() == IgnoreListManager::CtcpIgnore)
260         ui.ctcpTypeButton->setChecked(true);
261     else
262         ui.senderTypeButton->setChecked(true);
263
264     ui.isRegExCheckBox->setChecked(item.isRegEx());
265     ui.isActiveCheckBox->setChecked(item.isEnabled());
266
267     if (item.strictness() == IgnoreListManager::HardStrictness)
268         ui.permanentStrictnessButton->setChecked(true);
269     else
270         ui.dynamicStrictnessButton->setChecked(true);
271
272     switch (item.scope()) {
273     case IgnoreListManager::NetworkScope:
274         ui.networkScopeButton->setChecked(true);
275         ui.scopeRuleTextEdit->setEnabled(true);
276         break;
277     case IgnoreListManager::ChannelScope:
278         ui.channelScopeButton->setChecked(true);
279         ui.scopeRuleTextEdit->setEnabled(true);
280         break;
281     default:
282         ui.globalScopeButton->setChecked(true);
283         ui.scopeRuleTextEdit->setEnabled(false);
284     }
285
286     if (item.scope() == IgnoreListManager::GlobalScope)
287         ui.scopeRuleTextEdit->clear();
288     else
289         ui.scopeRuleTextEdit->setPlainText(item.scopeRule());
290
291     connect(ui.ignoreRuleLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(widgetHasChanged()));
292     connect(ui.scopeRuleTextEdit, SIGNAL(textChanged()), this, SLOT(widgetHasChanged()));
293     connect(&_typeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged()));
294     connect(&_strictnessButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged()));
295     connect(&_scopeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged()));
296     connect(ui.isRegExCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged()));
297     connect(ui.isActiveCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged()));
298
299     connect(ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(aboutToAccept()));
300     widgetHasChanged();
301 }
302
303
304 void IgnoreListEditDlg::widgetHasChanged()
305 {
306     if (ui.messageTypeButton->isChecked())
307         _clonedIgnoreListItem.setType(IgnoreListManager::MessageIgnore);
308     else if (ui.ctcpTypeButton->isChecked())
309         _clonedIgnoreListItem.setType(IgnoreListManager::CtcpIgnore);
310     else
311         _clonedIgnoreListItem.setType(IgnoreListManager::SenderIgnore);
312
313     if (ui.permanentStrictnessButton->isChecked())
314         _clonedIgnoreListItem.setStrictness(IgnoreListManager::HardStrictness);
315     else
316         _clonedIgnoreListItem.setStrictness(IgnoreListManager::SoftStrictness);
317
318     if (ui.networkScopeButton->isChecked()) {
319         _clonedIgnoreListItem.setScope(IgnoreListManager::NetworkScope);
320         ui.scopeRuleTextEdit->setEnabled(true);
321     }
322     else if (ui.channelScopeButton->isChecked()) {
323         _clonedIgnoreListItem.setScope(IgnoreListManager::ChannelScope);
324         ui.scopeRuleTextEdit->setEnabled(true);
325     }
326     else {
327         _clonedIgnoreListItem.setScope(IgnoreListManager::GlobalScope);
328         ui.scopeRuleTextEdit->setEnabled(false);
329     }
330
331     if (_clonedIgnoreListItem.scope() == IgnoreListManager::GlobalScope) {
332         _clonedIgnoreListItem.setScopeRule(QString());
333     }
334     else {
335         // Trim the resulting MultiWildcard expression
336         _clonedIgnoreListItem.setScopeRule(
337                     ExpressionMatch::trimMultiWildcardWhitespace(
338                         ui.scopeRuleTextEdit->toPlainText()));
339     }
340
341     _clonedIgnoreListItem.setContents(ui.ignoreRuleLineEdit->text());
342     _clonedIgnoreListItem.setIsRegEx(ui.isRegExCheckBox->isChecked());
343     _clonedIgnoreListItem.setIsEnabled(ui.isActiveCheckBox->isChecked());
344
345     if (!_clonedIgnoreListItem.contents().isEmpty() && _clonedIgnoreListItem != _ignoreListItem)
346         _hasChanged = true;
347     else
348         _hasChanged = false;
349     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(_hasChanged);
350 }
351
352
353 void IgnoreListEditDlg::enableOkButton(bool state)
354 {
355     ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(state);
356 }