Implement sender matching for highlight rules
[quassel.git] / src / qtui / settingspages / corehighlightsettingspage.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 "corehighlightsettingspage.h"
22
23 #include "qtui.h"
24 #include "uisettings.h"
25
26 #include <QHeaderView>
27 #include <client.h>
28
29 CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget *parent)
30     : SettingsPage(tr("Interface"), tr("Core-Side Highlights"), parent)
31 {
32     ui.setupUi(this);
33     ui.highlightTable->verticalHeader()->hide();
34     ui.highlightTable->setShowGrid(false);
35
36     ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setToolTip("<b>RegEx</b>: This option determines if the highlight rule should be interpreted as a <b>regular expression</b> or just as a keyword.");
37     ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setWhatsThis("<b>RegEx</b>: This option determines if the highlight rule should be interpreted as a <b>regular expression</b> or just as a keyword.");
38
39     ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setToolTip("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>.");
40     ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setWhatsThis("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>.");
41
42     ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setToolTip("<b>Channel</b>: This regular expression determines for which <b>channels</b> the highlight rule works. Leave blank to match any channel. Put <b>!</b> in the beginning to negate. Case insensitive.");
43     ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setWhatsThis("<b>Channel</b>: This regular expression determines for which <b>channels</b> the highlight rule works. Leave blank to match any channel. Put <b>!</b> in the beginning to negate. Case insensitive.");
44
45 #if QT_VERSION < 0x050000
46     ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
47     ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
48     ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
49     ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
50     ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
51 #else
52     ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
53     ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
54     ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
55     ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
56     ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::InverseColumn, QHeaderView::ResizeToContents);
57     ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
58 #endif
59
60     connect(ui.add, SIGNAL(clicked(bool)), this, SLOT(addNewRow()));
61     connect(ui.remove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedRows()));
62     //TODO: search for a better signal (one that emits everytime a selection has been changed for one item)
63     connect(ui.highlightTable, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(selectRow(QTableWidgetItem *)));
64
65     connect(ui.highlightAllNicks, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
66     connect(ui.highlightCurrentNick, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
67     connect(ui.highlightNoNick, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
68     connect(ui.nicksCaseSensitive, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
69     connect(ui.add, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
70     connect(ui.remove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
71     connect(ui.highlightTable, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(tableChanged(QTableWidgetItem *)));
72
73     connect(Client::instance(), SIGNAL(connected()), this, SLOT(clientConnected()));
74 }
75
76 void CoreHighlightSettingsPage::clientConnected()
77 {
78     connect(Client::highlightRuleManager(), SIGNAL(updated()), SLOT(revert()));
79 }
80
81
82 void CoreHighlightSettingsPage::revert()
83 {
84     qWarning() << "revert()";
85
86     if (!hasChanged())
87         return;
88
89     setChangedState(false);
90     load();
91 }
92
93
94 bool CoreHighlightSettingsPage::hasDefaults() const
95 {
96     return true;
97 }
98
99
100 void CoreHighlightSettingsPage::defaults()
101 {
102     ui.highlightCurrentNick->setChecked(true);
103     ui.nicksCaseSensitive->setChecked(false);
104     emptyTable();
105
106     widgetHasChanged();
107 }
108
109
110 void CoreHighlightSettingsPage::addNewRow(const QString &name, bool regex, bool cs, bool enable, bool inverse, const QString &sender,
111                                           const QString &chanName, bool self)
112 {
113     ui.highlightTable->setRowCount(ui.highlightTable->rowCount()+1);
114
115     auto *nameItem = new QTableWidgetItem(name);
116
117     auto *regexItem = new QTableWidgetItem("");
118     if (regex)
119         regexItem->setCheckState(Qt::Checked);
120     else
121         regexItem->setCheckState(Qt::Unchecked);
122     regexItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
123
124     auto *csItem = new QTableWidgetItem("");
125     if (cs)
126         csItem->setCheckState(Qt::Checked);
127     else
128         csItem->setCheckState(Qt::Unchecked);
129     csItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
130
131     auto *enableItem = new QTableWidgetItem("");
132     if (enable)
133         enableItem->setCheckState(Qt::Checked);
134     else
135         enableItem->setCheckState(Qt::Unchecked);
136     enableItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
137
138     auto *inverseItem = new QTableWidgetItem("");
139     if (inverse)
140         inverseItem->setCheckState(Qt::Checked);
141     else
142         inverseItem->setCheckState(Qt::Unchecked);
143     inverseItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
144
145     auto *chanNameItem = new QTableWidgetItem(chanName);
146
147     auto *senderItem = new QTableWidgetItem(sender);
148
149     int lastRow = ui.highlightTable->rowCount()-1;
150     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
151     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
152     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
153     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
154     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::InverseColumn, inverseItem);
155     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
156     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
157
158     if (!self)
159         ui.highlightTable->setCurrentItem(nameItem);
160
161     highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, inverse, sender, chanName);
162 }
163
164
165 void CoreHighlightSettingsPage::removeSelectedRows()
166 {
167     QList<int> selectedRows;
168     QList<QTableWidgetItem *> selectedItemList = ui.highlightTable->selectedItems();
169     foreach(QTableWidgetItem *selectedItem, selectedItemList) {
170         selectedRows.append(selectedItem->row());
171     }
172     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
173     int lastRow = -1;
174     foreach(int row, selectedRows) {
175         if (row != lastRow) {
176             ui.highlightTable->removeRow(row);
177             highlightList.removeAt(row);
178         }
179         lastRow = row;
180     }
181 }
182
183
184 void CoreHighlightSettingsPage::selectRow(QTableWidgetItem *item)
185 {
186     int row = item->row();
187     bool selected = item->isSelected();
188     ui.highlightTable->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount-1), selected);
189 }
190
191
192 void CoreHighlightSettingsPage::emptyTable()
193 {
194     // ui.highlight and highlightList should have the same size, but just to make sure.
195     if (ui.highlightTable->rowCount() != highlightList.size()) {
196         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
197     }
198     while (ui.highlightTable->rowCount()) {
199         ui.highlightTable->removeRow(0);
200     }
201     while (highlightList.size()) {
202         highlightList.removeLast();
203     }
204 }
205
206
207 void CoreHighlightSettingsPage::tableChanged(QTableWidgetItem *item)
208 {
209     if (item->row()+1 > highlightList.size())
210         return;
211
212     auto highlightRule = highlightList.value(item->row());
213
214
215     switch (item->column())
216     {
217     case CoreHighlightSettingsPage::NameColumn:
218         if (item->text() == "")
219             item->setText(tr("this shouldn't be empty"));
220         highlightRule.name = item->text();
221         break;
222     case CoreHighlightSettingsPage::RegExColumn:
223         highlightRule.isRegEx = (item->checkState() == Qt::Checked);
224         break;
225     case CoreHighlightSettingsPage::CsColumn:
226         highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked);
227         break;
228     case CoreHighlightSettingsPage::EnableColumn:
229         highlightRule.isEnabled = (item->checkState() == Qt::Checked);
230         break;
231     case CoreHighlightSettingsPage::InverseColumn:
232         highlightRule.isInverse = (item->checkState() == Qt::Checked);
233         break;
234         case CoreHighlightSettingsPage::SenderColumn:
235             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
236                 item->setText("");
237             highlightRule.sender = item->text();
238             break;
239     case CoreHighlightSettingsPage::ChanColumn:
240         if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
241             item->setText("");
242         highlightRule.chanName = item->text();
243         break;
244     }
245     highlightList[item->row()] = highlightRule;
246     emit widgetHasChanged();
247 }
248
249
250 void CoreHighlightSettingsPage::load()
251 {
252     emptyTable();
253
254     auto ruleManager = Client::highlightRuleManager();
255     for (HighlightRuleManager::HighlightRule rule : ruleManager->highlightRuleList()) {
256         addNewRow(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, rule.sender, rule.chanName);
257     }
258
259     switch (ruleManager->highlightNick())
260     {
261     case HighlightRuleManager::NoNick:
262         ui.highlightNoNick->setChecked(true);
263         break;
264     case HighlightRuleManager::CurrentNick:
265         ui.highlightCurrentNick->setChecked(true);
266         break;
267     case HighlightRuleManager::AllNicks:
268         ui.highlightAllNicks->setChecked(true);
269         break;
270     }
271     ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive());
272
273     setChangedState(false);
274     _initialized = true;
275 }
276
277 void CoreHighlightSettingsPage::save()
278 {
279     if (!hasChanged())
280         return;
281
282     if (!_initialized)
283         return;
284
285     auto ruleManager = Client::highlightRuleManager();
286     if (ruleManager == nullptr)
287         return;
288
289     auto clonedManager = HighlightRuleManager();
290     clonedManager.fromVariantMap(ruleManager->toVariantMap());
291     clonedManager.clear();
292
293     for (const HighlightRuleManager::HighlightRule &rule : highlightList) {
294         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse,
295                                        rule.sender, rule.chanName);
296     }
297
298     HighlightRuleManager::HighlightNickType highlightNickType = HighlightRuleManager::NoNick;
299     if (ui.highlightCurrentNick->isChecked())
300         highlightNickType = HighlightRuleManager::CurrentNick;
301     if (ui.highlightAllNicks->isChecked())
302         highlightNickType = HighlightRuleManager::AllNicks;
303
304
305     clonedManager.setHighlightNick(highlightNickType);
306     clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked());
307
308     ruleManager->requestUpdate(clonedManager.toVariantMap());
309     setChangedState(false);
310     load();
311 }
312
313
314 void CoreHighlightSettingsPage::widgetHasChanged()
315 {
316     setChangedState(true);
317 }