1 /***************************************************************************
2 * Copyright (C) 2005-2018 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
21 #include <QHeaderView>
22 #include <QTableWidget>
25 #include "corehighlightsettingspage.h"
28 CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget *parent)
29 : SettingsPage(tr("Interface"), tr("Remote Highlights"), parent)
33 setupRuleTable(ui.highlightTable);
34 setupRuleTable(ui.ignoredTable);
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));
40 coreConnectionStateChanged(Client::isConnected()); // need a core connection!
41 connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool)));
43 connect(ui.highlightAdd, SIGNAL(clicked(bool)), this, SLOT(addNewHighlightRow()));
44 connect(ui.highlightRemove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedHighlightRows()));
45 connect(ui.highlightImport, SIGNAL(clicked(bool)), this, SLOT(importRules()));
47 connect(ui.ignoredAdd, SIGNAL(clicked(bool)), this, SLOT(addNewIgnoredRow()));
48 connect(ui.ignoredRemove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedIgnoredRows()));
50 // TODO: search for a better signal (one that emits everytime a selection has been changed for one item)
51 connect(ui.highlightTable,
52 SIGNAL(itemClicked(QTableWidgetItem * )),
54 SLOT(selectHighlightRow(QTableWidgetItem * )));
55 connect(ui.ignoredTable,
56 SIGNAL(itemClicked(QTableWidgetItem * )),
58 SLOT(selectIgnoredRow(QTableWidgetItem * )));
60 // Update the "Case sensitive" checkbox
61 connect(ui.highlightNicksComboBox,
62 SIGNAL(currentIndexChanged(int)),
64 SLOT(highlightNicksChanged(int)));
66 connect(ui.highlightNicksComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
67 connect(ui.nicksCaseSensitive, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
69 connect(ui.highlightAdd, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
70 connect(ui.highlightRemove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
72 connect(ui.ignoredAdd, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
73 connect(ui.ignoredRemove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
75 connect(ui.highlightTable,
76 SIGNAL(itemChanged(QTableWidgetItem * )),
78 SLOT(highlightTableChanged(QTableWidgetItem * )));
80 connect(ui.ignoredTable,
81 SIGNAL(itemChanged(QTableWidgetItem * )),
83 SLOT(ignoredTableChanged(QTableWidgetItem * )));
85 connect(Client::instance(), SIGNAL(connected()), this, SLOT(clientConnected()));
88 void CoreHighlightSettingsPage::coreConnectionStateChanged(bool state)
98 void CoreHighlightSettingsPage::setupRuleTable(QTableWidget *table) const
100 table->verticalHeader()->hide();
101 table->setShowGrid(false);
103 table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setToolTip(
104 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."));
105 table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setWhatsThis(
106 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."));
108 table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setToolTip(
109 tr("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>."));
110 table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setWhatsThis(
111 tr("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>."));
113 table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setToolTip(
114 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."));
115 table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setWhatsThis(
116 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."));
118 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
119 table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
120 table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
121 table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
122 table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
123 table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
125 table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
126 table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
127 table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
128 table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
129 table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
133 void CoreHighlightSettingsPage::clientConnected()
135 connect(Client::highlightRuleManager(), SIGNAL(updated()), SLOT(revert()));
138 void CoreHighlightSettingsPage::revert()
143 setChangedState(false);
147 bool CoreHighlightSettingsPage::hasDefaults() const
152 void CoreHighlightSettingsPage::defaults()
154 int highlightNickType = HighlightRuleManager::HighlightNickType::CurrentNick;
155 int defaultIndex = ui.highlightNicksComboBox->findData(QVariant(highlightNickType));
156 ui.highlightNicksComboBox->setCurrentIndex(defaultIndex);
157 ui.nicksCaseSensitive->setChecked(false);
158 emptyHighlightTable();
164 void CoreHighlightSettingsPage::addNewHighlightRow(bool enable, const QString &name, bool regex, bool cs,
165 const QString &sender, const QString &chanName, bool self)
167 ui.highlightTable->setRowCount(ui.highlightTable->rowCount() + 1);
169 auto *nameItem = new QTableWidgetItem(name);
171 auto *regexItem = new QTableWidgetItem("");
173 regexItem->setCheckState(Qt::Checked);
175 regexItem->setCheckState(Qt::Unchecked);
176 regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
178 auto *csItem = new QTableWidgetItem("");
180 csItem->setCheckState(Qt::Checked);
182 csItem->setCheckState(Qt::Unchecked);
183 csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
185 auto *enableItem = new QTableWidgetItem("");
187 enableItem->setCheckState(Qt::Checked);
189 enableItem->setCheckState(Qt::Unchecked);
190 enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
192 auto *chanNameItem = new QTableWidgetItem(chanName);
194 auto *senderItem = new QTableWidgetItem(sender);
196 int lastRow = ui.highlightTable->rowCount() - 1;
197 ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
198 ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
199 ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
200 ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
201 ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
202 ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
205 ui.highlightTable->setCurrentItem(nameItem);
207 highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, false, sender, chanName);
210 void CoreHighlightSettingsPage::addNewIgnoredRow(bool enable, const QString &name, bool regex, bool cs,
211 const QString &sender, const QString &chanName, bool self)
213 ui.ignoredTable->setRowCount(ui.ignoredTable->rowCount() + 1);
215 auto *nameItem = new QTableWidgetItem(name);
217 auto *regexItem = new QTableWidgetItem("");
219 regexItem->setCheckState(Qt::Checked);
221 regexItem->setCheckState(Qt::Unchecked);
222 regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
224 auto *csItem = new QTableWidgetItem("");
226 csItem->setCheckState(Qt::Checked);
228 csItem->setCheckState(Qt::Unchecked);
229 csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
231 auto *enableItem = new QTableWidgetItem("");
233 enableItem->setCheckState(Qt::Checked);
235 enableItem->setCheckState(Qt::Unchecked);
236 enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
238 auto *chanNameItem = new QTableWidgetItem(chanName);
240 auto *senderItem = new QTableWidgetItem(sender);
242 int lastRow = ui.ignoredTable->rowCount() - 1;
243 ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
244 ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
245 ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
246 ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
247 ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
248 ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
251 ui.ignoredTable->setCurrentItem(nameItem);
253 ignoredList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, true, sender, chanName);
256 void CoreHighlightSettingsPage::removeSelectedHighlightRows()
258 QList<int> selectedRows;
259 QList<QTableWidgetItem *> selectedItemList = ui.highlightTable->selectedItems();
260 for (auto selectedItem : selectedItemList) {
261 selectedRows.append(selectedItem->row());
263 qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
265 for (auto row : selectedRows) {
266 if (row != lastRow) {
267 ui.highlightTable->removeRow(row);
268 highlightList.removeAt(row);
274 void CoreHighlightSettingsPage::removeSelectedIgnoredRows()
276 QList<int> selectedRows;
277 QList<QTableWidgetItem *> selectedItemList = ui.ignoredTable->selectedItems();
278 for (auto selectedItem : selectedItemList) {
279 selectedRows.append(selectedItem->row());
281 qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
283 for (auto row : selectedRows) {
284 if (row != lastRow) {
285 ui.ignoredTable->removeRow(row);
286 ignoredList.removeAt(row);
292 void CoreHighlightSettingsPage::highlightNicksChanged(const int index) {
293 // Only allow toggling "Case sensitive" when a nickname will be highlighted
294 auto highlightNickType = ui.highlightNicksComboBox->itemData(index).value<int>();
295 ui.nicksCaseSensitive->setEnabled(highlightNickType != HighlightRuleManager::NoNick);
298 void CoreHighlightSettingsPage::selectHighlightRow(QTableWidgetItem *item)
300 int row = item->row();
301 bool selected = item->isSelected();
303 ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
307 void CoreHighlightSettingsPage::selectIgnoredRow(QTableWidgetItem *item)
309 int row = item->row();
310 bool selected = item->isSelected();
312 ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
316 void CoreHighlightSettingsPage::emptyHighlightTable()
318 // ui.highlight and highlightList should have the same size, but just to make sure.
319 if (ui.highlightTable->rowCount() != highlightList.size()) {
320 qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
322 while (ui.highlightTable->rowCount()) {
323 ui.highlightTable->removeRow(0);
325 highlightList.clear();
328 void CoreHighlightSettingsPage::emptyIgnoredTable()
330 // ui.highlight and highlightList should have the same size, but just to make sure.
331 if (ui.ignoredTable->rowCount() != ignoredList.size()) {
332 qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
334 while (ui.ignoredTable->rowCount()) {
335 ui.ignoredTable->removeRow(0);
340 void CoreHighlightSettingsPage::highlightTableChanged(QTableWidgetItem *item)
342 if (item->row() + 1 > highlightList.size())
345 auto highlightRule = highlightList.value(item->row());
348 switch (item->column()) {
349 case CoreHighlightSettingsPage::EnableColumn:
350 highlightRule.isEnabled = (item->checkState() == Qt::Checked);
352 case CoreHighlightSettingsPage::NameColumn:
353 if (item->text() == "")
354 item->setText(tr("this shouldn't be empty"));
355 highlightRule.name = item->text();
357 case CoreHighlightSettingsPage::RegExColumn:
358 highlightRule.isRegEx = (item->checkState() == Qt::Checked);
360 case CoreHighlightSettingsPage::CsColumn:
361 highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked);
363 case CoreHighlightSettingsPage::SenderColumn:
364 if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
366 highlightRule.sender = item->text();
368 case CoreHighlightSettingsPage::ChanColumn:
369 if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
371 highlightRule.chanName = item->text();
374 highlightList[item->row()] = highlightRule;
375 emit widgetHasChanged();
378 void CoreHighlightSettingsPage::ignoredTableChanged(QTableWidgetItem *item)
380 if (item->row() + 1 > ignoredList.size())
383 auto ignoredRule = ignoredList.value(item->row());
386 switch (item->column()) {
387 case CoreHighlightSettingsPage::EnableColumn:
388 ignoredRule.isEnabled = (item->checkState() == Qt::Checked);
390 case CoreHighlightSettingsPage::NameColumn:
391 if (item->text() == "")
392 item->setText(tr("this shouldn't be empty"));
393 ignoredRule.name = item->text();
395 case CoreHighlightSettingsPage::RegExColumn:
396 ignoredRule.isRegEx = (item->checkState() == Qt::Checked);
398 case CoreHighlightSettingsPage::CsColumn:
399 ignoredRule.isCaseSensitive = (item->checkState() == Qt::Checked);
401 case CoreHighlightSettingsPage::SenderColumn:
402 if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
404 ignoredRule.sender = item->text();
406 case CoreHighlightSettingsPage::ChanColumn:
407 if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
409 ignoredRule.chanName = item->text();
412 ignoredList[item->row()] = ignoredRule;
413 emit widgetHasChanged();
416 void CoreHighlightSettingsPage::load()
418 emptyHighlightTable();
421 auto ruleManager = Client::highlightRuleManager();
423 for (auto &rule : ruleManager->highlightRuleList()) {
424 if (rule.isInverse) {
425 addNewIgnoredRow(rule.isEnabled,
428 rule.isCaseSensitive,
433 addNewHighlightRow(rule.isEnabled, rule.name, rule.isRegEx, rule.isCaseSensitive, rule.sender,
438 int highlightNickType = ruleManager->highlightNick();
439 ui.highlightNicksComboBox->setCurrentIndex(ui.highlightNicksComboBox->findData(QVariant(highlightNickType)));
440 // Trigger the initial update of nicksCaseSensitive being enabled or not
441 highlightNicksChanged(ui.highlightNicksComboBox->currentIndex());
442 ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive());
444 setChangedState(false);
451 void CoreHighlightSettingsPage::save()
459 auto ruleManager = Client::highlightRuleManager();
460 if (ruleManager == nullptr)
463 auto clonedManager = HighlightRuleManager();
464 clonedManager.fromVariantMap(ruleManager->toVariantMap());
465 clonedManager.clear();
467 for (auto &rule : highlightList) {
468 clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, false,
469 rule.sender, rule.chanName);
472 for (auto &rule : ignoredList) {
473 clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, true,
474 rule.sender, rule.chanName);
477 auto highlightNickType = ui.highlightNicksComboBox->itemData(ui.highlightNicksComboBox->currentIndex()).value<int>();
479 clonedManager.setHighlightNick(HighlightRuleManager::HighlightNickType(highlightNickType));
480 clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked());
482 ruleManager->requestUpdate(clonedManager.toVariantMap());
483 setChangedState(false);
487 void CoreHighlightSettingsPage::widgetHasChanged()
489 setChangedState(true);
492 void CoreHighlightSettingsPage::importRules() {
493 NotificationSettings notificationSettings;
495 auto clonedManager = HighlightRuleManager();
496 clonedManager.fromVariantMap(Client::highlightRuleManager()->toVariantMap());
498 for (const auto &variant : notificationSettings.highlightList()) {
499 auto highlightRule = variant.toMap();
501 clonedManager.addHighlightRule(
502 highlightRule["Name"].toString(),
503 highlightRule["RegEx"].toBool(),
504 highlightRule["CS"].toBool(),
505 highlightRule["Enable"].toBool(),
508 highlightRule["Channel"].toString()
512 Client::highlightRuleManager()->requestUpdate(clonedManager.toVariantMap());
513 setChangedState(false);
517 bool CoreHighlightSettingsPage::isSelectable() const {
518 return Client::isConnected() && Client::isCoreFeatureEnabled(Quassel::Feature::CoreSideHighlights);