X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fsettingspages%2Fcorehighlightsettingspage.cpp;h=e07c87d90d476904afa18cf6b443d09498564d9e;hp=8e9b852eb39ca2b4ee4243ccec777dd971762c57;hb=HEAD;hpb=ebe14ade2f3496aefc8926ce704a161c4451fedd diff --git a/src/qtui/settingspages/corehighlightsettingspage.cpp b/src/qtui/settingspages/corehighlightsettingspage.cpp index 8e9b852e..cb134899 100644 --- a/src/qtui/settingspages/corehighlightsettingspage.cpp +++ b/src/qtui/settingspages/corehighlightsettingspage.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2016 by the Quassel Project * + * Copyright (C) 2005-2022 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -20,69 +20,261 @@ #include "corehighlightsettingspage.h" -#include "qtui.h" -#include "uisettings.h" +#include #include -#include +#include +#include + +#include "client.h" +#include "icon.h" +#include "qtui.h" +#include "util.h" -CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget *parent) - : SettingsPage(tr("Interface"), tr("Core-Side Highlights"), parent) +CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget* parent) + : SettingsPage(tr("Interface"), tr("Highlights"), + parent) { ui.setupUi(this); - ui.highlightTable->verticalHeader()->hide(); - ui.highlightTable->setShowGrid(false); - - ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setToolTip("RegEx: This option determines if the highlight rule should be interpreted as a regular expression or just as a keyword."); - ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setWhatsThis("RegEx: This option determines if the highlight rule should be interpreted as a regular expression or just as a keyword."); - - ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setToolTip("CS: This option determines if the highlight rule should be interpreted case sensitive."); - ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setWhatsThis("CS: This option determines if the highlight rule should be interpreted case sensitive."); - - ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setToolTip("Channel: This regular expression determines for which channels the highlight rule works. Leave blank to match any channel. Put ! in the beginning to negate. Case insensitive."); - ui.highlightTable->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setWhatsThis("Channel: This regular expression determines for which channels the highlight rule works. Leave blank to match any channel. Put ! in the beginning to negate. Case insensitive."); - -#if QT_VERSION < 0x050000 - ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch); - ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents); -#else - ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch); - ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::InverseColumn, QHeaderView::ResizeToContents); - ui.highlightTable->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents); -#endif - - connect(ui.add, SIGNAL(clicked(bool)), this, SLOT(addNewRow())); - connect(ui.remove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedRows())); - //TODO: search for a better signal (one that emits everytime a selection has been changed for one item) - connect(ui.highlightTable, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(selectRow(QTableWidgetItem *))); - - connect(ui.highlightAllNicks, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.highlightCurrentNick, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.highlightNoNick, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.nicksCaseSensitive, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); - connect(ui.add, SIGNAL(clicked()), this, SLOT(widgetHasChanged())); - connect(ui.remove, SIGNAL(clicked()), this, SLOT(widgetHasChanged())); - connect(ui.highlightTable, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(tableChanged(QTableWidgetItem *))); - - connect(Client::instance(), SIGNAL(connected()), this, SLOT(clientConnected())); + + setupRuleTable(ui.highlightTable); + setupRuleTable(ui.ignoredTable); + + ui.highlightNicksComboBox->addItem(tr("All Nicks from Identity"), QVariant(HighlightRuleManager::AllNicks)); + ui.highlightNicksComboBox->addItem(tr("Current Nick"), QVariant(HighlightRuleManager::CurrentNick)); + ui.highlightNicksComboBox->addItem(tr("None"), QVariant(HighlightRuleManager::NoNick)); + + coreConnectionStateChanged(Client::isConnected()); // need a core connection! + connect(Client::instance(), &Client::coreConnectionStateChanged, this, &CoreHighlightSettingsPage::coreConnectionStateChanged); + + connect(ui.highlightAdd, &QAbstractButton::clicked, this, [this]() { addNewHighlightRow(); }); + connect(ui.highlightRemove, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::removeSelectedHighlightRows); + connect(ui.highlightImport, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::importRules); + + connect(ui.ignoredAdd, &QAbstractButton::clicked, this, [this]() { addNewIgnoredRow(); }); + connect(ui.ignoredRemove, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::removeSelectedIgnoredRows); + + // TODO: search for a better signal (one that emits every time a selection has been changed for one item) + connect(ui.highlightTable, &QTableWidget::itemClicked, this, &CoreHighlightSettingsPage::selectHighlightRow); + connect(ui.ignoredTable, &QTableWidget::itemClicked, this, &CoreHighlightSettingsPage::selectIgnoredRow); + + // Update the "Case sensitive" checkbox + connect(ui.highlightNicksComboBox, + selectOverload(&QComboBox::currentIndexChanged), + this, + &CoreHighlightSettingsPage::highlightNicksChanged); + connect(ui.highlightNicksComboBox, selectOverload(&QComboBox::currentIndexChanged), this, &CoreHighlightSettingsPage::widgetHasChanged); + connect(ui.nicksCaseSensitive, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::widgetHasChanged); + + connect(ui.highlightAdd, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::widgetHasChanged); + connect(ui.highlightRemove, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::widgetHasChanged); + + connect(ui.ignoredAdd, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::widgetHasChanged); + connect(ui.ignoredRemove, &QAbstractButton::clicked, this, &CoreHighlightSettingsPage::widgetHasChanged); + + connect(ui.highlightTable, &QTableWidget::itemChanged, this, &CoreHighlightSettingsPage::highlightTableChanged); + + connect(ui.ignoredTable, &QTableWidget::itemChanged, this, &CoreHighlightSettingsPage::ignoredTableChanged); + + connect(Client::instance(), &Client::connected, this, &CoreHighlightSettingsPage::clientConnected); + + // Warning icon + ui.coreUnsupportedIcon->setPixmap(icon::get({"emblem-unavailable", "dialog-warning"}).pixmap(16)); + + // Set up client/monolithic remote highlights information + // Local highlights are considered legacy + ui.highlightImport->setText(tr("Import Legacy")); + ui.highlightImport->setToolTip( + tr("Import highlight rules configured in %1.").arg(tr("Legacy Highlights").replace(" ", " "))); + // Re-use translations of "Legacy Highlights" as this is a word-for-word reference, forcing + // all spaces to be non-breaking } -void CoreHighlightSettingsPage::clientConnected() +void CoreHighlightSettingsPage::coreConnectionStateChanged(bool state) { - connect(Client::highlightRuleManager(), SIGNAL(updated()), SLOT(revert())); + updateCoreSupportStatus(state); + setEnabled(state); + if (state) { + load(); + } + else { + revert(); + } } +void CoreHighlightSettingsPage::setupRuleTable(QTableWidget* table) const +{ + table->verticalHeader()->hide(); + table->setShowGrid(false); + + setupTableTooltips(table->horizontalHeaderItem(CoreHighlightSettingsPage::EnableColumn), + table->horizontalHeaderItem(CoreHighlightSettingsPage::NameColumn), + table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn), + table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn), + table->horizontalHeaderItem(CoreHighlightSettingsPage::SenderColumn), + table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)); + + table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents); + table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch); + table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents); + table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents); + table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::SenderColumn, QHeaderView::ResizeToContents); + table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents); +} -void CoreHighlightSettingsPage::revert() +QString CoreHighlightSettingsPage::getTableTooltip(column tableColumn) const +{ + switch (tableColumn) { + case CoreHighlightSettingsPage::EnableColumn: + return tr("Enable/disable this rule"); + + case CoreHighlightSettingsPage::NameColumn: + return tr("Phrase to match, leave blank to match any message"); + + case CoreHighlightSettingsPage::RegExColumn: + return tr("RegEx: This option determines if the highlight rule, Sender, and " + "Channel should be interpreted as regular expressions or just as " + "keywords."); + + case CoreHighlightSettingsPage::CsColumn: + return tr("CS: This option determines if the highlight rule, Sender, and " + "Channel should be interpreted case sensitive."); + + case CoreHighlightSettingsPage::SenderColumn: + return tr("

Sender: Semicolon separated list of nick!ident@host names, " + "leave blank to match any nickname.

" + "

Example:
" + "Alice!*; Bob!*@example.com; Carol*!*; !Caroline!*
" + "would match on Alice, Bob with hostmask example.com, and " + "any nickname starting with Carol except for Caroline
" + "

If only inverted names are specified, it will match anything except for " + "what's specified (implicit wildcard).

" + "

Example:
" + "!Announce*!*; !Wheatley!aperture@*
" + "would match anything except for Wheatley with ident aperture or " + "any nickname starting with Announce

"); + + case CoreHighlightSettingsPage::ChanColumn: + return tr("

Channel: Semicolon separated list of channel/query names, leave blank " + "to match any name.

" + "

Example:
" + "#quassel*; #foobar; !#quasseldroid
" + "would match on #foobar and any channel starting with #quassel " + "except for #quasseldroid
" + "

If only inverted names are specified, it will match anything except for " + "what's specified (implicit wildcard).

" + "

Example:
" + "!#quassel*; !#foobar
" + "would match anything except for #foobar or any channel starting with " + "#quassel

"); + + default: + // This shouldn't happen + return "Invalid column type in CoreHighlightSettingsPage::getTableTooltip()"; + } +} + +void CoreHighlightSettingsPage::setupTableTooltips( + QWidget* enableWidget, QWidget* nameWidget, QWidget* regExWidget, QWidget* csWidget, QWidget* senderWidget, QWidget* chanWidget) const +{ + // Make sure everything's valid + Q_ASSERT(enableWidget); + Q_ASSERT(nameWidget); + Q_ASSERT(regExWidget); + Q_ASSERT(csWidget); + Q_ASSERT(senderWidget); + Q_ASSERT(chanWidget); + + // Set tooltips and "What's this?" prompts + // Enabled + enableWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::EnableColumn)); + enableWidget->setWhatsThis(enableWidget->toolTip()); + + // Name + nameWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::NameColumn)); + nameWidget->setWhatsThis(nameWidget->toolTip()); + + // RegEx enabled + regExWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::RegExColumn)); + regExWidget->setWhatsThis(regExWidget->toolTip()); + + // Case-sensitivity + csWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::CsColumn)); + csWidget->setWhatsThis(csWidget->toolTip()); + + // Sender + senderWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::SenderColumn)); + senderWidget->setWhatsThis(senderWidget->toolTip()); + + // Channel/buffer name + chanWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::ChanColumn)); + chanWidget->setWhatsThis(chanWidget->toolTip()); +} + +void CoreHighlightSettingsPage::setupTableTooltips(QTableWidgetItem* enableWidget, + QTableWidgetItem* nameWidget, + QTableWidgetItem* regExWidget, + QTableWidgetItem* csWidget, + QTableWidgetItem* senderWidget, + QTableWidgetItem* chanWidget) const +{ + // Make sure everything's valid + Q_ASSERT(enableWidget); + Q_ASSERT(nameWidget); + Q_ASSERT(regExWidget); + Q_ASSERT(csWidget); + Q_ASSERT(senderWidget); + Q_ASSERT(chanWidget); + + // Set tooltips and "What's this?" prompts + // Enabled + enableWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::EnableColumn)); + enableWidget->setWhatsThis(enableWidget->toolTip()); + + // Name + nameWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::NameColumn)); + nameWidget->setWhatsThis(nameWidget->toolTip()); + + // RegEx enabled + regExWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::RegExColumn)); + regExWidget->setWhatsThis(regExWidget->toolTip()); + + // Case-sensitivity + csWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::CsColumn)); + csWidget->setWhatsThis(csWidget->toolTip()); + + // Sender + senderWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::SenderColumn)); + senderWidget->setWhatsThis(senderWidget->toolTip()); + + // Channel/buffer name + chanWidget->setToolTip(getTableTooltip(CoreHighlightSettingsPage::ChanColumn)); + chanWidget->setWhatsThis(chanWidget->toolTip()); +} + +void CoreHighlightSettingsPage::updateCoreSupportStatus(bool state) +{ + // Assume connected state as enforced by the settings page UI + if (!state || Client::isCoreFeatureEnabled(Quassel::Feature::CoreSideHighlights)) { + // Either disconnected or core supports highlights, enable highlight configuration and hide + // warning. Don't show the warning needlessly when disconnected. + ui.highlightsConfigWidget->setEnabled(true); + ui.coreUnsupportedWidget->setVisible(false); + } + else { + // Core does not support highlights, show warning and disable highlight configuration + ui.highlightsConfigWidget->setEnabled(false); + ui.coreUnsupportedWidget->setVisible(true); + } +} + +void CoreHighlightSettingsPage::clientConnected() { - qWarning() << "revert()"; + connect(Client::highlightRuleManager(), &SyncableObject::updated, this, &CoreHighlightSettingsPage::revert); +} +void CoreHighlightSettingsPage::revert() +{ if (!hasChanged()) return; @@ -90,88 +282,137 @@ void CoreHighlightSettingsPage::revert() load(); } - bool CoreHighlightSettingsPage::hasDefaults() const { return true; } - void CoreHighlightSettingsPage::defaults() { - ui.highlightCurrentNick->setChecked(true); + int highlightNickType = HighlightRuleManager::HighlightNickType::CurrentNick; + int defaultIndex = ui.highlightNicksComboBox->findData(QVariant(highlightNickType)); + ui.highlightNicksComboBox->setCurrentIndex(defaultIndex); ui.nicksCaseSensitive->setChecked(false); - emptyTable(); + emptyHighlightTable(); + emptyIgnoredTable(); widgetHasChanged(); } - -void CoreHighlightSettingsPage::addNewRow(const QString &name, bool regex, bool cs, bool enable, bool inverse, const QString &sender, - const QString &chanName, bool self) +void CoreHighlightSettingsPage::addNewHighlightRow( + bool enable, int id, const QString& name, bool regex, bool cs, const QString& sender, const QString& chanName, bool self) { - ui.highlightTable->setRowCount(ui.highlightTable->rowCount()+1); + ui.highlightTable->setRowCount(ui.highlightTable->rowCount() + 1); + + if (id < 0) { + id = nextId(); + } - auto *nameItem = new QTableWidgetItem(name); + auto* nameItem = new QTableWidgetItem(name); - auto *regexItem = new QTableWidgetItem(""); + auto* regexItem = new QTableWidgetItem(""); if (regex) regexItem->setCheckState(Qt::Checked); else regexItem->setCheckState(Qt::Unchecked); - regexItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable); + regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - auto *csItem = new QTableWidgetItem(""); + auto* csItem = new QTableWidgetItem(""); if (cs) csItem->setCheckState(Qt::Checked); else csItem->setCheckState(Qt::Unchecked); - csItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable); + csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - auto *enableItem = new QTableWidgetItem(""); + auto* enableItem = new QTableWidgetItem(""); if (enable) enableItem->setCheckState(Qt::Checked); else enableItem->setCheckState(Qt::Unchecked); - enableItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable); + enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - auto *inverseItem = new QTableWidgetItem(""); - if (inverse) - inverseItem->setCheckState(Qt::Checked); - else - inverseItem->setCheckState(Qt::Unchecked); - inverseItem->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled|Qt::ItemIsSelectable); + auto* senderItem = new QTableWidgetItem(sender); - auto *chanNameItem = new QTableWidgetItem(chanName); + auto* chanNameItem = new QTableWidgetItem(chanName); - auto *senderItem = new QTableWidgetItem(sender); + setupTableTooltips(enableItem, nameItem, regexItem, csItem, senderItem, chanNameItem); - int lastRow = ui.highlightTable->rowCount()-1; + int lastRow = ui.highlightTable->rowCount() - 1; ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem); - ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::InverseColumn, inverseItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem); ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem); if (!self) ui.highlightTable->setCurrentItem(nameItem); - highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, inverse, sender, chanName); + highlightList << HighlightRuleManager::HighlightRule(id, name, regex, cs, enable, false, sender, chanName); } +void CoreHighlightSettingsPage::addNewIgnoredRow( + bool enable, int id, const QString& name, bool regex, bool cs, const QString& sender, const QString& chanName, bool self) +{ + ui.ignoredTable->setRowCount(ui.ignoredTable->rowCount() + 1); + + if (id < 0) { + id = nextId(); + } + + auto* nameItem = new QTableWidgetItem(name); + + auto* regexItem = new QTableWidgetItem(""); + if (regex) + regexItem->setCheckState(Qt::Checked); + else + regexItem->setCheckState(Qt::Unchecked); + regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + auto* csItem = new QTableWidgetItem(""); + if (cs) + csItem->setCheckState(Qt::Checked); + else + csItem->setCheckState(Qt::Unchecked); + csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); -void CoreHighlightSettingsPage::removeSelectedRows() + auto* enableItem = new QTableWidgetItem(""); + if (enable) + enableItem->setCheckState(Qt::Checked); + else + enableItem->setCheckState(Qt::Unchecked); + enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + auto* chanNameItem = new QTableWidgetItem(chanName); + + auto* senderItem = new QTableWidgetItem(sender); + + setupTableTooltips(enableItem, nameItem, regexItem, csItem, senderItem, chanNameItem); + + int lastRow = ui.ignoredTable->rowCount() - 1; + ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem); + ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem); + ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem); + ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem); + ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem); + ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem); + + if (!self) + ui.ignoredTable->setCurrentItem(nameItem); + + ignoredList << HighlightRuleManager::HighlightRule(id, name, regex, cs, enable, true, sender, chanName); +} + +void CoreHighlightSettingsPage::removeSelectedHighlightRows() { QList selectedRows; - QList selectedItemList = ui.highlightTable->selectedItems(); - foreach(QTableWidgetItem *selectedItem, selectedItemList) { + QList selectedItemList = ui.highlightTable->selectedItems(); + for (auto selectedItem : selectedItemList) { selectedRows.append(selectedItem->row()); } - qSort(selectedRows.begin(), selectedRows.end(), qGreater()); + std::sort(selectedRows.begin(), selectedRows.end(), std::greater<>()); int lastRow = -1; - foreach(int row, selectedRows) { + for (auto row : selectedRows) { if (row != lastRow) { ui.highlightTable->removeRow(row); highlightList.removeAt(row); @@ -180,16 +421,46 @@ void CoreHighlightSettingsPage::removeSelectedRows() } } +void CoreHighlightSettingsPage::removeSelectedIgnoredRows() +{ + QList selectedRows; + QList selectedItemList = ui.ignoredTable->selectedItems(); + for (auto selectedItem : selectedItemList) { + selectedRows.append(selectedItem->row()); + } + std::sort(selectedRows.begin(), selectedRows.end(), std::greater<>()); + int lastRow = -1; + for (auto row : selectedRows) { + if (row != lastRow) { + ui.ignoredTable->removeRow(row); + ignoredList.removeAt(row); + } + lastRow = row; + } +} -void CoreHighlightSettingsPage::selectRow(QTableWidgetItem *item) +void CoreHighlightSettingsPage::highlightNicksChanged(const int index) +{ + // Only allow toggling "Case sensitive" when a nickname will be highlighted + auto highlightNickType = ui.highlightNicksComboBox->itemData(index).value(); + ui.nicksCaseSensitive->setEnabled(highlightNickType != HighlightRuleManager::NoNick); +} + +void CoreHighlightSettingsPage::selectHighlightRow(QTableWidgetItem* item) { int row = item->row(); bool selected = item->isSelected(); - ui.highlightTable->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount-1), selected); + ui.highlightTable->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1), selected); } +void CoreHighlightSettingsPage::selectIgnoredRow(QTableWidgetItem* item) +{ + int row = item->row(); + bool selected = item->isSelected(); + ui.ignoredTable->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1), selected); +} -void CoreHighlightSettingsPage::emptyTable() +void CoreHighlightSettingsPage::emptyHighlightTable() { // ui.highlight and highlightList should have the same size, but just to make sure. if (ui.highlightTable->rowCount() != highlightList.size()) { @@ -198,80 +469,131 @@ void CoreHighlightSettingsPage::emptyTable() while (ui.highlightTable->rowCount()) { ui.highlightTable->removeRow(0); } - while (highlightList.size()) { - highlightList.removeLast(); - } + highlightList.clear(); } +void CoreHighlightSettingsPage::emptyIgnoredTable() +{ + // ui.highlight and highlightList should have the same size, but just to make sure. + if (ui.ignoredTable->rowCount() != ignoredList.size()) { + qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!"; + } + while (ui.ignoredTable->rowCount()) { + ui.ignoredTable->removeRow(0); + } + ignoredList.clear(); +} -void CoreHighlightSettingsPage::tableChanged(QTableWidgetItem *item) +void CoreHighlightSettingsPage::highlightTableChanged(QTableWidgetItem* item) { - if (item->row()+1 > highlightList.size()) + if (item->row() + 1 > highlightList.size()) return; auto highlightRule = highlightList.value(item->row()); - - switch (item->column()) - { + switch (item->column()) { + case CoreHighlightSettingsPage::EnableColumn: + highlightRule.setIsEnabled(item->checkState() == Qt::Checked); + break; case CoreHighlightSettingsPage::NameColumn: - if (item->text() == "") - item->setText(tr("this shouldn't be empty")); - highlightRule.name = item->text(); + highlightRule.setContents(item->text()); break; case CoreHighlightSettingsPage::RegExColumn: - highlightRule.isRegEx = (item->checkState() == Qt::Checked); + highlightRule.setIsRegEx(item->checkState() == Qt::Checked); break; case CoreHighlightSettingsPage::CsColumn: - highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked); - break; - case CoreHighlightSettingsPage::EnableColumn: - highlightRule.isEnabled = (item->checkState() == Qt::Checked); + highlightRule.setIsCaseSensitive(item->checkState() == Qt::Checked); break; - case CoreHighlightSettingsPage::InverseColumn: - highlightRule.isInverse = (item->checkState() == Qt::Checked); + case CoreHighlightSettingsPage::SenderColumn: + if (!item->text().isEmpty() && item->text().trimmed().isEmpty()) + item->setText(""); + highlightRule.setSender(item->text()); break; - case CoreHighlightSettingsPage::SenderColumn: - if (!item->text().isEmpty() && item->text().trimmed().isEmpty()) - item->setText(""); - highlightRule.sender = item->text(); - break; case CoreHighlightSettingsPage::ChanColumn: if (!item->text().isEmpty() && item->text().trimmed().isEmpty()) item->setText(""); - highlightRule.chanName = item->text(); + highlightRule.setChanName(item->text()); break; } highlightList[item->row()] = highlightRule; emit widgetHasChanged(); } - -void CoreHighlightSettingsPage::load() +void CoreHighlightSettingsPage::ignoredTableChanged(QTableWidgetItem* item) { - emptyTable(); + if (item->row() + 1 > ignoredList.size()) + return; - auto ruleManager = Client::highlightRuleManager(); - for (HighlightRuleManager::HighlightRule rule : ruleManager->highlightRuleList()) { - addNewRow(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, rule.sender, rule.chanName); - } + auto ignoredRule = ignoredList.value(item->row()); - switch (ruleManager->highlightNick()) - { - case HighlightRuleManager::NoNick: - ui.highlightNoNick->setChecked(true); + switch (item->column()) { + case CoreHighlightSettingsPage::EnableColumn: + ignoredRule.setIsEnabled(item->checkState() == Qt::Checked); + break; + case CoreHighlightSettingsPage::NameColumn: + ignoredRule.setContents(item->text()); + break; + case CoreHighlightSettingsPage::RegExColumn: + ignoredRule.setIsRegEx(item->checkState() == Qt::Checked); break; - case HighlightRuleManager::CurrentNick: - ui.highlightCurrentNick->setChecked(true); + case CoreHighlightSettingsPage::CsColumn: + ignoredRule.setIsCaseSensitive(item->checkState() == Qt::Checked); break; - case HighlightRuleManager::AllNicks: - ui.highlightAllNicks->setChecked(true); + case CoreHighlightSettingsPage::SenderColumn: + if (!item->text().isEmpty() && item->text().trimmed().isEmpty()) + item->setText(""); + ignoredRule.setSender(item->text()); + break; + case CoreHighlightSettingsPage::ChanColumn: + if (!item->text().isEmpty() && item->text().trimmed().isEmpty()) + item->setText(""); + ignoredRule.setChanName(item->text()); break; } - ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive()); + ignoredList[item->row()] = ignoredRule; + emit widgetHasChanged(); +} - setChangedState(false); - _initialized = true; +void CoreHighlightSettingsPage::load() +{ + emptyHighlightTable(); + emptyIgnoredTable(); + + auto ruleManager = Client::highlightRuleManager(); + if (ruleManager) { + for (auto& rule : ruleManager->highlightRuleList()) { + if (rule.isInverse()) { + addNewIgnoredRow(rule.isEnabled(), + rule.id(), + rule.contents(), + rule.isRegEx(), + rule.isCaseSensitive(), + rule.sender(), + rule.chanName()); + } + else { + addNewHighlightRow(rule.isEnabled(), + rule.id(), + rule.contents(), + rule.isRegEx(), + rule.isCaseSensitive(), + rule.sender(), + rule.chanName()); + } + } + + int highlightNickType = ruleManager->highlightNick(); + ui.highlightNicksComboBox->setCurrentIndex(ui.highlightNicksComboBox->findData(QVariant(highlightNickType))); + // Trigger the initial update of nicksCaseSensitive being enabled or not + highlightNicksChanged(ui.highlightNicksComboBox->currentIndex()); + ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive()); + + setChangedState(false); + _initialized = true; + } + else { + defaults(); + } } void CoreHighlightSettingsPage::save() @@ -286,23 +608,35 @@ void CoreHighlightSettingsPage::save() if (ruleManager == nullptr) return; - auto clonedManager = HighlightRuleManager(); + HighlightRuleManager clonedManager; clonedManager.fromVariantMap(ruleManager->toVariantMap()); clonedManager.clear(); - for (const HighlightRuleManager::HighlightRule &rule : highlightList) { - clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, rule.isInverse, - rule.sender, rule.chanName); + for (auto& rule : highlightList) { + clonedManager.addHighlightRule(rule.id(), + rule.contents(), + rule.isRegEx(), + rule.isCaseSensitive(), + rule.isEnabled(), + false, + rule.sender(), + rule.chanName()); } - HighlightRuleManager::HighlightNickType highlightNickType = HighlightRuleManager::NoNick; - if (ui.highlightCurrentNick->isChecked()) - highlightNickType = HighlightRuleManager::CurrentNick; - if (ui.highlightAllNicks->isChecked()) - highlightNickType = HighlightRuleManager::AllNicks; + for (auto& rule : ignoredList) { + clonedManager.addHighlightRule(rule.id(), + rule.contents(), + rule.isRegEx(), + rule.isCaseSensitive(), + rule.isEnabled(), + true, + rule.sender(), + rule.chanName()); + } + auto highlightNickType = ui.highlightNicksComboBox->itemData(ui.highlightNicksComboBox->currentIndex()).value(); - clonedManager.setHighlightNick(highlightNickType); + clonedManager.setHighlightNick(HighlightRuleManager::HighlightNickType(highlightNickType)); clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked()); ruleManager->requestUpdate(clonedManager.toVariantMap()); @@ -310,8 +644,142 @@ void CoreHighlightSettingsPage::save() load(); } +int CoreHighlightSettingsPage::nextId() +{ + int max = 0; + for (int i = 0; i < highlightList.count(); i++) { + int id = highlightList[i].id(); + if (id > max) { + max = id; + } + } + for (int i = 0; i < ignoredList.count(); i++) { + int id = ignoredList[i].id(); + if (id > max) { + max = id; + } + } + return max + 1; +} void CoreHighlightSettingsPage::widgetHasChanged() { setChangedState(true); -} \ No newline at end of file +} + +void CoreHighlightSettingsPage::on_coreUnsupportedDetails_clicked() +{ + // Re-use translations of "Legacy Highlights" as this is a word-for-word reference, forcing all + // spaces to non-breaking + const QString localHighlightsName = tr("Legacy Highlights").replace(" ", " "); + + const QString remoteHighlightsMsgText = QString("

%1


%2


%3

") + .arg(tr("Your Quassel core is too old to support remote highlights"), + tr("You need a Quassel core v0.13.0 or newer to configure remote " + "highlights."), + tr("You can still configure highlights for this device only in " + "%1.") + .arg(localHighlightsName)); + + QMessageBox::warning(this, tr("Remote Highlights unsupported"), remoteHighlightsMsgText); +} + +void CoreHighlightSettingsPage::importRules() +{ + NotificationSettings notificationSettings; + + const auto localHighlightList = notificationSettings.highlightList(); + + // Re-use translations of "Legacy/Local Highlights" as this is a word-for-word reference, + // forcing all spaces to non-breaking + // "Local Highlights" has been removed; it's always called "Legacy" now. + QString localHighlightsName = tr("Legacy Highlights").replace(" ", " "); + + if (localHighlightList.count() == 0) { + // No highlight rules exist to import, do nothing + QMessageBox::information(this, tr("No highlights to import"), tr("No highlight rules in %1.").arg(localHighlightsName)); + return; + } + + int ret = QMessageBox::question(this, + tr("Import highlights?"), + tr("Import all highlight rules from %1?").arg(localHighlightsName), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (ret != QMessageBox::Yes) { + // Only two options, Yes or No, return if not Yes + return; + } + + if (hasChanged()) { + // Save existing changes first to avoid overwriting them + save(); + } + + HighlightRuleManager clonedManager; + clonedManager.fromVariantMap(Client::highlightRuleManager()->toVariantMap()); + + for (const auto& variant : notificationSettings.highlightList()) { + auto highlightRule = variant.toMap(); + + clonedManager.addHighlightRule(clonedManager.nextId(), + highlightRule["Name"].toString(), + highlightRule["RegEx"].toBool(), + highlightRule["CS"].toBool(), + highlightRule["Enable"].toBool(), + false, + "", + highlightRule["Channel"].toString()); + } + + // Copy nickname highlighting settings + clonedManager.setNicksCaseSensitive(notificationSettings.nicksCaseSensitive()); + if (notificationSettings.highlightNick() == NotificationSettings::HighlightNickType::AllNicks) { + clonedManager.setHighlightNick(HighlightRuleManager::HighlightNickType::AllNicks); + } + else if (notificationSettings.highlightNick() == NotificationSettings::HighlightNickType::CurrentNick) { + clonedManager.setHighlightNick(HighlightRuleManager::HighlightNickType::CurrentNick); + } + // else - Don't copy "NoNick", "NoNick" is now default and should be ignored + + Client::highlightRuleManager()->requestUpdate(clonedManager.toVariantMap()); + setChangedState(false); + load(); + + // Give a heads-up that all succeeded, ask about removing old rules + // + // Hypothetically, someone might use a common set of highlight rules across multiple cores. + // This won't matter once client highlights are disabled entirely on newer cores. + // + // Remove this once client-side highlights are disabled for newer cores. + ret = QMessageBox::question(this, tr("Imported highlights"), + QString("

%1


%2

").arg( + tr("%1 highlight rules successfully imported.").arg(QString::number(localHighlightList.count())), + tr("Clean up old, duplicate highlight rules?")), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes); + + if (ret != QMessageBox::Yes) { + // Only two options, Yes or No, return if not Yes + return; + } + + // Remove all local highlight rules + notificationSettings.setHighlightList({}); + // Disable local nickname highlighting + notificationSettings.setHighlightNick(NotificationSettings::HighlightNickType::NoNick); + // Disable nickname sensitivity + // This isn't needed to disable local highlights, but it's part of appearing reset-to-default + notificationSettings.setNicksCaseSensitive(false); + + // Refresh HighlightSettingsPage in case it was already loaded + emit localHighlightsChanged(); +} + +bool CoreHighlightSettingsPage::isSelectable() const +{ + return Client::isConnected(); + // We check for Quassel::Feature::CoreSideHighlights when loading this page, allowing us to show + // a friendly error message. +}