Implement inverse 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(QString name, bool regex, bool cs, bool enable, bool inverse, QString chanName, bool self)
111 {
112     ui.highlightTable->setRowCount(ui.highlightTable->rowCount()+1);
113
114     auto *nameItem = new QTableWidgetItem(name);
115
116     auto *regexItem = new QTableWidgetItem("");
117     if (regex)
118         regexItem->setCheckState(Qt::Checked);
119     else
120         regexItem->setCheckState(Qt::Unchecked);
121     regexItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
122
123     auto *csItem = new QTableWidgetItem("");
124     if (cs)
125         csItem->setCheckState(Qt::Checked);
126     else
127         csItem->setCheckState(Qt::Unchecked);
128     csItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
129
130     auto *enableItem = new QTableWidgetItem("");
131     if (enable)
132         enableItem->setCheckState(Qt::Checked);
133     else
134         enableItem->setCheckState(Qt::Unchecked);
135     enableItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
136
137     auto *inverseItem = new QTableWidgetItem("");
138     if (inverse)
139         inverseItem->setCheckState(Qt::Checked);
140     else
141         inverseItem->setCheckState(Qt::Unchecked);
142     inverseItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable);
143
144     auto *chanNameItem = new QTableWidgetItem(chanName);
145
146     int lastRow = ui.highlightTable->rowCount()-1;
147     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
148     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
149     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
150     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
151     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::InverseColumn, inverseItem);
152     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
153
154     if (!self)
155         ui.highlightTable->setCurrentItem(nameItem);
156
157     highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, inverse, chanName);
158 }
159
160
161 void CoreHighlightSettingsPage::removeSelectedRows()
162 {
163     QList<int> selectedRows;
164     QList<QTableWidgetItem *> selectedItemList = ui.highlightTable->selectedItems();
165     foreach(QTableWidgetItem *selectedItem, selectedItemList) {
166         selectedRows.append(selectedItem->row());
167     }
168     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
169     int lastRow = -1;
170     foreach(int row, selectedRows) {
171         if (row != lastRow) {
172             ui.highlightTable->removeRow(row);
173             highlightList.removeAt(row);
174         }
175         lastRow = row;
176     }
177 }
178
179
180 void CoreHighlightSettingsPage::selectRow(QTableWidgetItem *item)
181 {
182     int row = item->row();
183     bool selected = item->isSelected();
184     ui.highlightTable->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount-1), selected);
185 }
186
187
188 void CoreHighlightSettingsPage::emptyTable()
189 {
190     // ui.highlight and highlightList should have the same size, but just to make sure.
191     if (ui.highlightTable->rowCount() != highlightList.size()) {
192         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
193     }
194     while (ui.highlightTable->rowCount()) {
195         ui.highlightTable->removeRow(0);
196     }
197     while (highlightList.size()) {
198         highlightList.removeLast();
199     }
200 }
201
202
203 void CoreHighlightSettingsPage::tableChanged(QTableWidgetItem *item)
204 {
205     if (item->row()+1 > highlightList.size())
206         return;
207
208     auto highlightRule = highlightList.value(item->row());
209
210
211     switch (item->column())
212     {
213     case CoreHighlightSettingsPage::NameColumn:
214         if (item->text() == "")
215             item->setText(tr("this shouldn't be empty"));
216         highlightRule.name = item->text();
217         break;
218     case CoreHighlightSettingsPage::RegExColumn:
219         highlightRule.isRegEx = (item->checkState() == Qt::Checked);
220         break;
221     case CoreHighlightSettingsPage::CsColumn:
222         highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked);
223         break;
224     case CoreHighlightSettingsPage::EnableColumn:
225         highlightRule.isEnabled = (item->checkState() == Qt::Checked);
226         break;
227     case CoreHighlightSettingsPage::InverseColumn:
228         highlightRule.isInverse = (item->checkState() == Qt::Checked);
229         break;
230     case CoreHighlightSettingsPage::ChanColumn:
231         if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
232             item->setText("");
233         highlightRule.chanName = item->text();
234         break;
235     }
236     highlightList[item->row()] = highlightRule;
237     emit widgetHasChanged();
238 }
239
240
241 void CoreHighlightSettingsPage::load()
242 {
243     emptyTable();
244
245     auto ruleManager = Client::highlightRuleManager();
246     for (HighlightRuleManager::HighlightRule rule : ruleManager->highlightRuleList()) {
247         addNewRow(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, rule.chanName);
248     }
249
250     switch (ruleManager->highlightNick())
251     {
252     case HighlightRuleManager::NoNick:
253         ui.highlightNoNick->setChecked(true);
254         break;
255     case HighlightRuleManager::CurrentNick:
256         ui.highlightCurrentNick->setChecked(true);
257         break;
258     case HighlightRuleManager::AllNicks:
259         ui.highlightAllNicks->setChecked(true);
260         break;
261     }
262     ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive());
263
264     setChangedState(false);
265     _initialized = true;
266 }
267
268 void CoreHighlightSettingsPage::save()
269 {
270     if (!hasChanged())
271         return;
272
273     if (!_initialized)
274         return;
275
276     auto ruleManager = Client::highlightRuleManager();
277     if (ruleManager == nullptr)
278         return;
279
280     auto clonedManager = HighlightRuleManager();
281     clonedManager.fromVariantMap(ruleManager->toVariantMap());
282     clonedManager.clear();
283
284     for (const HighlightRuleManager::HighlightRule &rule : highlightList) {
285         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, rule.chanName);
286     }
287
288     HighlightRuleManager::HighlightNickType highlightNickType = HighlightRuleManager::NoNick;
289     if (ui.highlightCurrentNick->isChecked())
290         highlightNickType = HighlightRuleManager::CurrentNick;
291     if (ui.highlightAllNicks->isChecked())
292         highlightNickType = HighlightRuleManager::AllNicks;
293
294
295     clonedManager.setHighlightNick(highlightNickType);
296     clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked());
297
298     ruleManager->requestUpdate(clonedManager.toVariantMap());
299     setChangedState(false);
300     load();
301 }
302
303
304 void CoreHighlightSettingsPage::widgetHasChanged()
305 {
306     setChangedState(true);
307 }