55266d0560af7a7960614670428567529794a0d3
[quassel.git] / src / qtui / settingspages / corehighlightsettingspage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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 <QHeaderView>
22 #include <QTableWidget>
23
24 #include "client.h"
25 #include "corehighlightsettingspage.h"
26 #include "qtui.h"
27
28 CoreHighlightSettingsPage::CoreHighlightSettingsPage(QWidget *parent)
29     : SettingsPage(tr("Interface"), tr("Remote 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     coreConnectionStateChanged(Client::isConnected()); // need a core connection!
41     connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool)));
42
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()));
46
47     connect(ui.ignoredAdd, SIGNAL(clicked(bool)), this, SLOT(addNewIgnoredRow()));
48     connect(ui.ignoredRemove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedIgnoredRows()));
49
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 * )),
53             this,
54             SLOT(selectHighlightRow(QTableWidgetItem * )));
55     connect(ui.ignoredTable,
56             SIGNAL(itemClicked(QTableWidgetItem * )),
57             this,
58             SLOT(selectIgnoredRow(QTableWidgetItem * )));
59
60     // Update the "Case sensitive" checkbox
61     connect(ui.highlightNicksComboBox,
62             SIGNAL(currentIndexChanged(int)),
63             this,
64             SLOT(highlightNicksChanged(int)));
65
66     connect(ui.highlightNicksComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged()));
67     connect(ui.nicksCaseSensitive, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged()));
68
69     connect(ui.highlightAdd, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
70     connect(ui.highlightRemove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
71
72     connect(ui.ignoredAdd, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
73     connect(ui.ignoredRemove, SIGNAL(clicked()), this, SLOT(widgetHasChanged()));
74
75     connect(ui.highlightTable,
76             SIGNAL(itemChanged(QTableWidgetItem * )),
77             this,
78             SLOT(highlightTableChanged(QTableWidgetItem * )));
79
80     connect(ui.ignoredTable,
81             SIGNAL(itemChanged(QTableWidgetItem * )),
82             this,
83             SLOT(ignoredTableChanged(QTableWidgetItem * )));
84
85     connect(Client::instance(), SIGNAL(connected()), this, SLOT(clientConnected()));
86 }
87
88 void CoreHighlightSettingsPage::coreConnectionStateChanged(bool state)
89 {
90     setEnabled(state);
91     if (state) {
92         load();
93     } else {
94         revert();
95     }
96 }
97
98 void CoreHighlightSettingsPage::setupRuleTable(QTableWidget *table) const
99 {
100     table->verticalHeader()->hide();
101     table->setShowGrid(false);
102
103     table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setToolTip(
104                 tr("<b>RegEx</b>: This option determines if the highlight rule should be "
105                    "interpreted as a <b>regular expression</b> or just as a keyword."));
106     table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->setWhatsThis(
107                 table->horizontalHeaderItem(CoreHighlightSettingsPage::RegExColumn)->toolTip());
108
109     table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setToolTip(
110                 tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
111                    "<b>case sensitive</b>."));
112     table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->setWhatsThis(
113                 table->horizontalHeaderItem(CoreHighlightSettingsPage::CsColumn)->toolTip());
114
115     table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setToolTip(
116                 tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
117                    "<p><i>Example:</i><br />"
118                    "<i>#quassel*; #foobar; !#quasseldroid</i><br />"
119                    "would match on #foobar and on any channel starting with <i>#quassel</i> except "
120                    "for <i>#quasseldroid</i><br />"
121                    "<p>If only inverted names are specified, it will match anything except for "
122                    "what's specified (implicit wildcard).</p>"
123                    "<p><i>Example:</i><br />"
124                    "<i>!#quassel*; !#foobar</i><br />"
125                    "would match anything except for #foobar or any channel starting with "
126                    "<i>#quassel</i></p>"));
127     table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->setWhatsThis(
128                 table->horizontalHeaderItem(CoreHighlightSettingsPage::ChanColumn)->toolTip());
129
130 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
131     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
132     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
133     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
134     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
135     table->horizontalHeader()->setResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
136 #else
137     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::EnableColumn, QHeaderView::ResizeToContents);
138     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::NameColumn, QHeaderView::Stretch);
139     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::RegExColumn, QHeaderView::ResizeToContents);
140     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::CsColumn, QHeaderView::ResizeToContents);
141     table->horizontalHeader()->setSectionResizeMode(CoreHighlightSettingsPage::ChanColumn, QHeaderView::ResizeToContents);
142 #endif
143 }
144
145 void CoreHighlightSettingsPage::clientConnected()
146 {
147     connect(Client::highlightRuleManager(), SIGNAL(updated()), SLOT(revert()));
148 }
149
150 void CoreHighlightSettingsPage::revert()
151 {
152     if (!hasChanged())
153         return;
154
155     setChangedState(false);
156     load();
157 }
158
159 bool CoreHighlightSettingsPage::hasDefaults() const
160 {
161     return true;
162 }
163
164 void CoreHighlightSettingsPage::defaults()
165 {
166     int highlightNickType = HighlightRuleManager::HighlightNickType::CurrentNick;
167     int defaultIndex = ui.highlightNicksComboBox->findData(QVariant(highlightNickType));
168     ui.highlightNicksComboBox->setCurrentIndex(defaultIndex);
169     ui.nicksCaseSensitive->setChecked(false);
170     emptyHighlightTable();
171     emptyIgnoredTable();
172
173     widgetHasChanged();
174 }
175
176 void CoreHighlightSettingsPage::addNewHighlightRow(bool enable, const QString &name, bool regex, bool cs,
177                                                    const QString &sender, const QString &chanName, bool self)
178 {
179     ui.highlightTable->setRowCount(ui.highlightTable->rowCount() + 1);
180
181     auto *nameItem = new QTableWidgetItem(name);
182
183     auto *regexItem = new QTableWidgetItem("");
184     if (regex)
185         regexItem->setCheckState(Qt::Checked);
186     else
187         regexItem->setCheckState(Qt::Unchecked);
188     regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
189
190     auto *csItem = new QTableWidgetItem("");
191     if (cs)
192         csItem->setCheckState(Qt::Checked);
193     else
194         csItem->setCheckState(Qt::Unchecked);
195     csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
196
197     auto *enableItem = new QTableWidgetItem("");
198     if (enable)
199         enableItem->setCheckState(Qt::Checked);
200     else
201         enableItem->setCheckState(Qt::Unchecked);
202     enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
203
204     auto *chanNameItem = new QTableWidgetItem(chanName);
205
206     auto *senderItem = new QTableWidgetItem(sender);
207
208     enableItem->setToolTip(tr("Enable/disable this rule"));
209     nameItem->setToolTip(tr("Phrase to match"));
210     regexItem->setToolTip(
211                 tr("<b>RegEx</b>: This option determines if the highlight rule should be "
212                    "interpreted as a <b>regular expression</b> or just as a keyword."));
213     csItem->setToolTip(
214                 tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
215                    "<b>case sensitive</b>."));
216     senderItem->setToolTip(
217                 tr("<b>Sender</b>: This option specifies which sender to match.  Leave blank to "
218                    "match any nickname."));
219     chanNameItem->setToolTip(
220                 tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
221                    "<p><i>Example:</i><br />"
222                    "<i>#quassel*; #foobar; !#quasseldroid</i><br />"
223                    "would match on #foobar and on any channel starting with <i>#quassel</i> except "
224                    "for <i>#quasseldroid</i><br />"
225                    "<p>If only inverted names are specified, it will match anything except for "
226                    "what's specified (implicit wildcard).</p>"
227                    "<p><i>Example:</i><br />"
228                    "<i>!#quassel*; !#foobar</i><br />"
229                    "would match anything except for #foobar or any channel starting with "
230                    "<i>#quassel</i></p>"));
231
232     int lastRow = ui.highlightTable->rowCount() - 1;
233     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
234     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
235     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
236     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
237     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
238     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
239
240     if (!self)
241         ui.highlightTable->setCurrentItem(nameItem);
242
243     highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, false, sender, chanName);
244 }
245
246 void CoreHighlightSettingsPage::addNewIgnoredRow(bool enable, const QString &name, bool regex, bool cs,
247                                                  const QString &sender, const QString &chanName, bool self)
248 {
249     ui.ignoredTable->setRowCount(ui.ignoredTable->rowCount() + 1);
250
251     auto *nameItem = new QTableWidgetItem(name);
252
253     auto *regexItem = new QTableWidgetItem("");
254     if (regex)
255         regexItem->setCheckState(Qt::Checked);
256     else
257         regexItem->setCheckState(Qt::Unchecked);
258     regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
259
260     auto *csItem = new QTableWidgetItem("");
261     if (cs)
262         csItem->setCheckState(Qt::Checked);
263     else
264         csItem->setCheckState(Qt::Unchecked);
265     csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
266
267     auto *enableItem = new QTableWidgetItem("");
268     if (enable)
269         enableItem->setCheckState(Qt::Checked);
270     else
271         enableItem->setCheckState(Qt::Unchecked);
272     enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
273
274     auto *chanNameItem = new QTableWidgetItem(chanName);
275
276     auto *senderItem = new QTableWidgetItem(sender);
277
278     enableItem->setToolTip(tr("Enable/disable this rule"));
279     nameItem->setToolTip(tr("Phrase to match"));
280     regexItem->setToolTip(
281                 tr("<b>RegEx</b>: This option determines if the highlight rule should be "
282                    "interpreted as a <b>regular expression</b> or just as a keyword."));
283     csItem->setToolTip(
284                 tr("<b>CS</b>: This option determines if the highlight rule should be interpreted "
285                    "<b>case sensitive</b>."));
286     senderItem->setToolTip(
287                 tr("<b>Sender</b>: This option specifies which sender nicknames match.  Leave "
288                    "blank to match any nickname."));
289     chanNameItem->setToolTip(
290                 tr("<p><b>Channel</b>: Semicolon separated list of channel names.</p>"
291                    "<p><i>Example:</i><br />"
292                    "<i>#quassel*; #foobar; !#quasseldroid</i><br />"
293                    "would match on #foobar and on any channel starting with <i>#quassel</i> except "
294                    "for <i>#quasseldroid</i><br />"
295                    "<p>If only inverted names are specified, it will match anything except for "
296                    "what's specified (implicit wildcard).</p>"
297                    "<p><i>Example:</i><br />"
298                    "<i>!#quassel*; !#foobar</i><br />"
299                    "would match anything except for #foobar or any channel starting with "
300                    "<i>#quassel</i></p>"));
301
302     int lastRow = ui.ignoredTable->rowCount() - 1;
303     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
304     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
305     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
306     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
307     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
308     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
309
310     if (!self)
311         ui.ignoredTable->setCurrentItem(nameItem);
312
313     ignoredList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, true, sender, chanName);
314 }
315
316 void CoreHighlightSettingsPage::removeSelectedHighlightRows()
317 {
318     QList<int> selectedRows;
319     QList<QTableWidgetItem *> selectedItemList = ui.highlightTable->selectedItems();
320     for (auto selectedItem : selectedItemList) {
321         selectedRows.append(selectedItem->row());
322     }
323     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
324     int lastRow = -1;
325     for (auto row : selectedRows) {
326         if (row != lastRow) {
327             ui.highlightTable->removeRow(row);
328             highlightList.removeAt(row);
329         }
330         lastRow = row;
331     }
332 }
333
334 void CoreHighlightSettingsPage::removeSelectedIgnoredRows()
335 {
336     QList<int> selectedRows;
337     QList<QTableWidgetItem *> selectedItemList = ui.ignoredTable->selectedItems();
338     for (auto selectedItem : selectedItemList) {
339         selectedRows.append(selectedItem->row());
340     }
341     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
342     int lastRow = -1;
343     for (auto row : selectedRows) {
344         if (row != lastRow) {
345             ui.ignoredTable->removeRow(row);
346             ignoredList.removeAt(row);
347         }
348         lastRow = row;
349     }
350 }
351
352 void CoreHighlightSettingsPage::highlightNicksChanged(const int index) {
353     // Only allow toggling "Case sensitive" when a nickname will be highlighted
354     auto highlightNickType = ui.highlightNicksComboBox->itemData(index).value<int>();
355     ui.nicksCaseSensitive->setEnabled(highlightNickType != HighlightRuleManager::NoNick);
356 }
357
358 void CoreHighlightSettingsPage::selectHighlightRow(QTableWidgetItem *item)
359 {
360     int row = item->row();
361     bool selected = item->isSelected();
362     ui.highlightTable
363         ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
364                            selected);
365 }
366
367 void CoreHighlightSettingsPage::selectIgnoredRow(QTableWidgetItem *item)
368 {
369     int row = item->row();
370     bool selected = item->isSelected();
371     ui.ignoredTable
372         ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
373                            selected);
374 }
375
376 void CoreHighlightSettingsPage::emptyHighlightTable()
377 {
378     // ui.highlight and highlightList should have the same size, but just to make sure.
379     if (ui.highlightTable->rowCount() != highlightList.size()) {
380         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
381     }
382     while (ui.highlightTable->rowCount()) {
383         ui.highlightTable->removeRow(0);
384     }
385     highlightList.clear();
386 }
387
388 void CoreHighlightSettingsPage::emptyIgnoredTable()
389 {
390     // ui.highlight and highlightList should have the same size, but just to make sure.
391     if (ui.ignoredTable->rowCount() != ignoredList.size()) {
392         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
393     }
394     while (ui.ignoredTable->rowCount()) {
395         ui.ignoredTable->removeRow(0);
396     }
397     ignoredList.clear();
398 }
399
400 void CoreHighlightSettingsPage::highlightTableChanged(QTableWidgetItem *item)
401 {
402     if (item->row() + 1 > highlightList.size())
403         return;
404
405     auto highlightRule = highlightList.value(item->row());
406
407
408     switch (item->column()) {
409         case CoreHighlightSettingsPage::EnableColumn:
410             highlightRule.isEnabled = (item->checkState() == Qt::Checked);
411             break;
412         case CoreHighlightSettingsPage::NameColumn:
413             if (item->text() == "")
414                 item->setText(tr("this shouldn't be empty"));
415             highlightRule.name = item->text();
416             break;
417         case CoreHighlightSettingsPage::RegExColumn:
418             highlightRule.isRegEx = (item->checkState() == Qt::Checked);
419             break;
420         case CoreHighlightSettingsPage::CsColumn:
421             highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked);
422             break;
423         case CoreHighlightSettingsPage::SenderColumn:
424             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
425                 item->setText("");
426             highlightRule.sender = item->text();
427             break;
428         case CoreHighlightSettingsPage::ChanColumn:
429             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
430                 item->setText("");
431             highlightRule.chanName = item->text();
432             break;
433     }
434     highlightList[item->row()] = highlightRule;
435     emit widgetHasChanged();
436 }
437
438 void CoreHighlightSettingsPage::ignoredTableChanged(QTableWidgetItem *item)
439 {
440     if (item->row() + 1 > ignoredList.size())
441         return;
442
443     auto ignoredRule = ignoredList.value(item->row());
444
445
446     switch (item->column()) {
447         case CoreHighlightSettingsPage::EnableColumn:
448             ignoredRule.isEnabled = (item->checkState() == Qt::Checked);
449             break;
450         case CoreHighlightSettingsPage::NameColumn:
451             if (item->text() == "")
452                 item->setText(tr("this shouldn't be empty"));
453             ignoredRule.name = item->text();
454             break;
455         case CoreHighlightSettingsPage::RegExColumn:
456             ignoredRule.isRegEx = (item->checkState() == Qt::Checked);
457             break;
458         case CoreHighlightSettingsPage::CsColumn:
459             ignoredRule.isCaseSensitive = (item->checkState() == Qt::Checked);
460             break;
461         case CoreHighlightSettingsPage::SenderColumn:
462             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
463                 item->setText("");
464             ignoredRule.sender = item->text();
465             break;
466         case CoreHighlightSettingsPage::ChanColumn:
467             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
468                 item->setText("");
469             ignoredRule.chanName = item->text();
470             break;
471     }
472     ignoredList[item->row()] = ignoredRule;
473     emit widgetHasChanged();
474 }
475
476 void CoreHighlightSettingsPage::load()
477 {
478     emptyHighlightTable();
479     emptyIgnoredTable();
480
481     auto ruleManager = Client::highlightRuleManager();
482     if (ruleManager) {
483         for (auto &rule : ruleManager->highlightRuleList()) {
484             if (rule.isInverse) {
485                 addNewIgnoredRow(rule.isEnabled,
486                                  rule.name,
487                                  rule.isRegEx,
488                                  rule.isCaseSensitive,
489                                  rule.sender,
490                                  rule.chanName);
491             }
492             else {
493                 addNewHighlightRow(rule.isEnabled, rule.name, rule.isRegEx, rule.isCaseSensitive, rule.sender,
494                                    rule.chanName);
495             }
496         }
497
498         int highlightNickType = ruleManager->highlightNick();
499         ui.highlightNicksComboBox->setCurrentIndex(ui.highlightNicksComboBox->findData(QVariant(highlightNickType)));
500         // Trigger the initial update of nicksCaseSensitive being enabled or not
501         highlightNicksChanged(ui.highlightNicksComboBox->currentIndex());
502         ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive());
503
504         setChangedState(false);
505         _initialized = true;
506     } else {
507         defaults();
508     }
509 }
510
511 void CoreHighlightSettingsPage::save()
512 {
513     if (!hasChanged())
514         return;
515
516     if (!_initialized)
517         return;
518
519     auto ruleManager = Client::highlightRuleManager();
520     if (ruleManager == nullptr)
521         return;
522
523     auto clonedManager = HighlightRuleManager();
524     clonedManager.fromVariantMap(ruleManager->toVariantMap());
525     clonedManager.clear();
526
527     for (auto &rule : highlightList) {
528         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, false,
529                                        rule.sender, rule.chanName);
530     }
531
532     for (auto &rule : ignoredList) {
533         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, true,
534                                        rule.sender, rule.chanName);
535     }
536
537     auto highlightNickType = ui.highlightNicksComboBox->itemData(ui.highlightNicksComboBox->currentIndex()).value<int>();
538
539     clonedManager.setHighlightNick(HighlightRuleManager::HighlightNickType(highlightNickType));
540     clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked());
541
542     ruleManager->requestUpdate(clonedManager.toVariantMap());
543     setChangedState(false);
544     load();
545 }
546
547 void CoreHighlightSettingsPage::widgetHasChanged()
548 {
549     setChangedState(true);
550 }
551
552 void CoreHighlightSettingsPage::importRules() {
553     NotificationSettings notificationSettings;
554
555     auto clonedManager = HighlightRuleManager();
556     clonedManager.fromVariantMap(Client::highlightRuleManager()->toVariantMap());
557
558     for (const auto &variant : notificationSettings.highlightList()) {
559         auto highlightRule = variant.toMap();
560
561         clonedManager.addHighlightRule(
562                 highlightRule["Name"].toString(),
563                 highlightRule["RegEx"].toBool(),
564                 highlightRule["CS"].toBool(),
565                 highlightRule["Enable"].toBool(),
566                 false,
567                 "",
568                 highlightRule["Channel"].toString()
569         );
570     }
571
572     Client::highlightRuleManager()->requestUpdate(clonedManager.toVariantMap());
573     setChangedState(false);
574     load();
575 }
576
577 bool CoreHighlightSettingsPage::isSelectable() const {
578     return Client::isConnected() && Client::isCoreFeatureEnabled(Quassel::Feature::CoreSideHighlights);
579 }