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