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