Store the HighlightNickType as int instead of custom enum
[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 <QTableWidget>
22 #include <QtWidgets/QHeaderView>
23
24 #include "corehighlightsettingspage.h"
25 #include "qtui.h"
26 #include "client.h"
27
28 CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget *parent)
29     : SettingsPage(tr("Interface"), tr("Core-Side Highlights"), parent)
30 {
31     ui.setupUi(this);
32
33     setupRuleTable(ui.highlightTable);
34     setupRuleTable(ui.ignoredTable);
35
36     ui.highlightNicksComboBox->addItem(tr("All Nicks from Identity"), QVariant(HighlightRuleManager::AllNicks));
37     ui.highlightNicksComboBox->addItem(tr("Current Nick"), QVariant(HighlightRuleManager::CurrentNick));
38     ui.highlightNicksComboBox->addItem(tr("None"), QVariant(HighlightRuleManager::NoNick));
39
40     connect(ui.highlightAdd, SIGNAL(clicked(bool)), this, SLOT(addNewHighlightRow()));
41     connect(ui.highlightRemove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedHighlightRows()));
42
43     connect(ui.ignoredAdd, SIGNAL(clicked(bool)), this, SLOT(addNewIgnoredRow()));
44     connect(ui.ignoredRemove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedIgnoredRows()));
45
46     // TODO: search for a better signal (one that emits everytime a selection has been changed for one item)
47     connect(ui.highlightTable,
48             SIGNAL(itemClicked(QTableWidgetItem * )),
49             this,
50             SLOT(selectHighlightRow(QTableWidgetItem * )));
51     connect(ui.ignoredTable,
52             SIGNAL(itemClicked(QTableWidgetItem * )),
53             this,
54             SLOT(selectIgnoredRow(QTableWidgetItem * )));
55
56
57     connect(ui.highlightNicksComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
58     connect(ui.nicksCaseSensitive, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
59
60     connect(ui.highlightAdd, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
61     connect(ui.highlightRemove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
62
63     connect(ui.ignoredAdd, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
64     connect(ui.ignoredRemove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
65
66     connect(ui.highlightTable,
67             SIGNAL(itemChanged(QTableWidgetItem * )),
68             this,
69             SLOT(highlightTableChanged(QTableWidgetItem * )));
70
71     connect(ui.ignoredTable,
72             SIGNAL(itemChanged(QTableWidgetItem * )),
73             this,
74             SLOT(ignoredTableChanged(QTableWidgetItem * )));
75
76     connect(Client::instance(), SIGNAL(connected()), this, SLOT(clientConnected()));
77 }
78 void CoreHighlightSettingsPage::setupRuleTable(QTableWidget *table) const
79 {
80     table->verticalHeader()->hide();
81     table->setShowGrid(false);
82
83     table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setToolTip(
84         tr("<b>RegEx</b>: This option determines if the highlight rule should be interpreted as a <b>regular expression</b> or just as a keyword."));
85     table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setWhatsThis(
86         tr("<b>RegEx</b>: This option determines if the highlight rule should be interpreted as a <b>regular expression</b> or just as a keyword."));
87
88     table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setToolTip(
89         tr("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>."));
90     table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setWhatsThis(
91         tr("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>."));
92
93     table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setToolTip(
94         tr("<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."));
95     table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setWhatsThis(
96         tr("<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."));
97
98 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
99     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
100     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
101     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
102     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
103     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
104 #else
105     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
106     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
107     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
108     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
109     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
110 #endif
111 }
112
113 void CoreHighlightSettingsPage::clientConnected()
114 {
115     connect(Client::highlightRuleManager(), SIGNAL(updated()), SLOT(revert()));
116 }
117
118 void CoreHighlightSettingsPage::revert()
119 {
120     if (!hasChanged())
121         return;
122
123     setChangedState(false);
124     load();
125 }
126
127 bool CoreHighlightSettingsPage::hasDefaults() const
128 {
129     return true;
130 }
131
132 void CoreHighlightSettingsPage::defaults()
133 {
134     int highlightNickType = HighlightRuleManager::HighlightNickType::CurrentNick;
135     int defaultIndex = ui.highlightNicksComboBox->findData(QVariant(highlightNickType));
136     ui.highlightNicksComboBox->setCurrentIndex(defaultIndex);
137     ui.nicksCaseSensitive->setChecked(false);
138     emptyHighlightTable();
139     emptyIgnoredTable();
140
141     widgetHasChanged();
142 }
143
144 void CoreHighlightSettingsPage::addNewHighlightRow(bool enable, const QString &name, bool regex, bool cs,
145                                                    const QString &sender, const QString &chanName, bool self)
146 {
147     ui.highlightTable->setRowCount(ui.highlightTable->rowCount() + 1);
148
149     auto *nameItem = new QTableWidgetItem(name);
150
151     auto *regexItem = new QTableWidgetItem("");
152     if (regex)
153         regexItem->setCheckState(Qt::Checked);
154     else
155         regexItem->setCheckState(Qt::Unchecked);
156     regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
157
158     auto *csItem = new QTableWidgetItem("");
159     if (cs)
160         csItem->setCheckState(Qt::Checked);
161     else
162         csItem->setCheckState(Qt::Unchecked);
163     csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
164
165     auto *enableItem = new QTableWidgetItem("");
166     if (enable)
167         enableItem->setCheckState(Qt::Checked);
168     else
169         enableItem->setCheckState(Qt::Unchecked);
170     enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
171
172     auto *chanNameItem = new QTableWidgetItem(chanName);
173
174     auto *senderItem = new QTableWidgetItem(sender);
175
176     int lastRow = ui.highlightTable->rowCount() - 1;
177     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
178     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
179     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
180     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
181     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
182     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
183
184     if (!self)
185         ui.highlightTable->setCurrentItem(nameItem);
186
187     highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, false, sender, chanName);
188 }
189
190 void CoreHighlightSettingsPage::addNewIgnoredRow(bool enable, const QString &name, bool regex, bool cs,
191                                                  const QString &sender, const QString &chanName, bool self)
192 {
193     ui.ignoredTable->setRowCount(ui.ignoredTable->rowCount() + 1);
194
195     auto *nameItem = new QTableWidgetItem(name);
196
197     auto *regexItem = new QTableWidgetItem("");
198     if (regex)
199         regexItem->setCheckState(Qt::Checked);
200     else
201         regexItem->setCheckState(Qt::Unchecked);
202     regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
203
204     auto *csItem = new QTableWidgetItem("");
205     if (cs)
206         csItem->setCheckState(Qt::Checked);
207     else
208         csItem->setCheckState(Qt::Unchecked);
209     csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
210
211     auto *enableItem = new QTableWidgetItem("");
212     if (enable)
213         enableItem->setCheckState(Qt::Checked);
214     else
215         enableItem->setCheckState(Qt::Unchecked);
216     enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
217
218     auto *chanNameItem = new QTableWidgetItem(chanName);
219
220     auto *senderItem = new QTableWidgetItem(sender);
221
222     int lastRow = ui.ignoredTable->rowCount() - 1;
223     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
224     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
225     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
226     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
227     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
228     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
229
230     if (!self)
231         ui.ignoredTable->setCurrentItem(nameItem);
232
233     ignoredList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, true, sender, chanName);
234 }
235
236 void CoreHighlightSettingsPage::removeSelectedHighlightRows()
237 {
238     QList<int> selectedRows;
239     QList<QTableWidgetItem *> selectedItemList = ui.highlightTable->selectedItems();
240     for (auto selectedItem : selectedItemList) {
241         selectedRows.append(selectedItem->row());
242     }
243     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
244     int lastRow = -1;
245     for (auto row : selectedRows) {
246         if (row != lastRow) {
247             ui.highlightTable->removeRow(row);
248             highlightList.removeAt(row);
249         }
250         lastRow = row;
251     }
252 }
253
254 void CoreHighlightSettingsPage::removeSelectedIgnoredRows()
255 {
256     QList<int> selectedRows;
257     QList<QTableWidgetItem *> selectedItemList = ui.ignoredTable->selectedItems();
258     for (auto selectedItem : selectedItemList) {
259         selectedRows.append(selectedItem->row());
260     }
261     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
262     int lastRow = -1;
263     for (auto row : selectedRows) {
264         if (row != lastRow) {
265             ui.ignoredTable->removeRow(row);
266             ignoredList.removeAt(row);
267         }
268         lastRow = row;
269     }
270 }
271
272 void CoreHighlightSettingsPage::selectHighlightRow(QTableWidgetItem *item)
273 {
274     int row = item->row();
275     bool selected = item->isSelected();
276     ui.highlightTable
277         ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
278                            selected);
279 }
280
281 void CoreHighlightSettingsPage::selectIgnoredRow(QTableWidgetItem *item)
282 {
283     int row = item->row();
284     bool selected = item->isSelected();
285     ui.ignoredTable
286         ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
287                            selected);
288 }
289
290 void CoreHighlightSettingsPage::emptyHighlightTable()
291 {
292     // ui.highlight and highlightList should have the same size, but just to make sure.
293     if (ui.highlightTable->rowCount() != highlightList.size()) {
294         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
295     }
296     ui.highlightTable->clearContents();
297     highlightList.clear();
298 }
299
300 void CoreHighlightSettingsPage::emptyIgnoredTable()
301 {
302     // ui.highlight and highlightList should have the same size, but just to make sure.
303     if (ui.ignoredTable->rowCount() != ignoredList.size()) {
304         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
305     }
306     ui.ignoredTable->clearContents();
307     ignoredList.clear();
308 }
309
310 void CoreHighlightSettingsPage::highlightTableChanged(QTableWidgetItem *item)
311 {
312     if (item->row() + 1 > highlightList.size())
313         return;
314
315     auto highlightRule = highlightList.value(item->row());
316
317
318     switch (item->column()) {
319         case CoreHighlightSettingsPage::EnableColumn:
320             highlightRule.isEnabled = (item->checkState() == Qt::Checked);
321         case CoreHighlightSettingsPage::NameColumn:
322             if (item->text() == "")
323                 item->setText(tr("this shouldn't be empty"));
324             highlightRule.name = item->text();
325             break;
326         case CoreHighlightSettingsPage::RegExColumn:
327             highlightRule.isRegEx = (item->checkState() == Qt::Checked);
328             break;
329         case CoreHighlightSettingsPage::CsColumn:
330             highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked);
331             break;
332         case CoreHighlightSettingsPage::SenderColumn:
333             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
334                 item->setText("");
335             highlightRule.sender = item->text();
336             break;
337         case CoreHighlightSettingsPage::ChanColumn:
338             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
339                 item->setText("");
340             highlightRule.chanName = item->text();
341             break;
342     }
343     highlightList[item->row()] = highlightRule;
344     emit widgetHasChanged();
345 }
346
347 void CoreHighlightSettingsPage::ignoredTableChanged(QTableWidgetItem *item)
348 {
349     if (item->row() + 1 > ignoredList.size())
350         return;
351
352     auto ignoredRule = ignoredList.value(item->row());
353
354
355     switch (item->column()) {
356         case CoreHighlightSettingsPage::EnableColumn:
357             ignoredRule.isEnabled = (item->checkState() == Qt::Checked);
358         case CoreHighlightSettingsPage::NameColumn:
359             if (item->text() == "")
360                 item->setText(tr("this shouldn't be empty"));
361             ignoredRule.name = item->text();
362             break;
363         case CoreHighlightSettingsPage::RegExColumn:
364             ignoredRule.isRegEx = (item->checkState() == Qt::Checked);
365             break;
366         case CoreHighlightSettingsPage::CsColumn:
367             ignoredRule.isCaseSensitive = (item->checkState() == Qt::Checked);
368             break;
369         case CoreHighlightSettingsPage::SenderColumn:
370             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
371                 item->setText("");
372             ignoredRule.sender = item->text();
373             break;
374         case CoreHighlightSettingsPage::ChanColumn:
375             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
376                 item->setText("");
377             ignoredRule.chanName = item->text();
378             break;
379     }
380     ignoredList[item->row()] = ignoredRule;
381     emit widgetHasChanged();
382 }
383
384 void CoreHighlightSettingsPage::load()
385 {
386     emptyHighlightTable();
387     emptyIgnoredTable();
388
389     auto ruleManager = Client::highlightRuleManager();
390     if (ruleManager) {
391         for (auto &rule : ruleManager->highlightRuleList()) {
392             if (rule.isInverse) {
393                 addNewIgnoredRow(rule.isEnabled,
394                                  rule.name,
395                                  rule.isRegEx,
396                                  rule.isCaseSensitive,
397                                  rule.sender,
398                                  rule.chanName);
399             }
400             else {
401                 addNewHighlightRow(rule.isEnabled, rule.name, rule.isRegEx, rule.isCaseSensitive, rule.sender,
402                                    rule.chanName);
403             }
404         }
405
406         int highlightNickType = ruleManager->highlightNick();
407         ui.highlightNicksComboBox->setCurrentIndex(ui.highlightNicksComboBox->findData(QVariant(highlightNickType)));
408         ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive());
409
410         setChangedState(false);
411         _initialized = true;
412     } else {
413         defaults();
414     }
415 }
416
417 void CoreHighlightSettingsPage::save()
418 {
419     if (!hasChanged())
420         return;
421
422     if (!_initialized)
423         return;
424
425     auto ruleManager = Client::highlightRuleManager();
426     if (ruleManager == nullptr)
427         return;
428
429     auto clonedManager = HighlightRuleManager();
430     clonedManager.fromVariantMap(ruleManager->toVariantMap());
431     clonedManager.clear();
432
433     for (auto &rule : highlightList) {
434         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, false,
435                                        rule.sender, rule.chanName);
436     }
437
438     for (auto &rule : ignoredList) {
439         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, true,
440                                        rule.sender, rule.chanName);
441     }
442
443     auto highlightNickType = ui.highlightNicksComboBox->currentData().value<int>();
444
445     clonedManager.setHighlightNick(HighlightRuleManager::HighlightNickType(highlightNickType));
446     clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked());
447
448     ruleManager->requestUpdate(clonedManager.toVariantMap());
449     setChangedState(false);
450     load();
451 }
452
453 void CoreHighlightSettingsPage::widgetHasChanged()
454 {
455     setChangedState(true);
456 }