7bedc00d4f35fb5661ac49855b9aed02d112ab2f
[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"), HighlightRuleManager::AllNicks);
37     ui.highlightNicksComboBox->addItem(tr("Current Nick"), HighlightRuleManager::CurrentNick);
38     ui.highlightNicksComboBox->addItem(tr("None"), 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(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(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(CsColumn)->setToolTip(
89         tr("<b>CS</b>: This option determines if the highlight rule should be interpreted <b>case sensitive</b>."));
90     table->horizontalHeaderItem(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(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(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(EnableColumn, QHeaderView::ResizeToContents);
106     table->horizontalHeader()->setSectionResizeMode(NameColumn, QHeaderView::Stretch);
107     table->horizontalHeader()->setSectionResizeMode(RegExColumn, QHeaderView::ResizeToContents);
108     table->horizontalHeader()->setSectionResizeMode(CsColumn, QHeaderView::ResizeToContents);
109     table->horizontalHeader()->setSectionResizeMode(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     ui.highlightNicksComboBox->setCurrentIndex(ui.highlightNicksComboBox
135                                                    ->findData(QVariant(HighlightRuleManager::HighlightNickType::CurrentNick)));
136     ui.nicksCaseSensitive->setChecked(false);
137     emptyHighlightTable();
138     emptyIgnoredTable();
139
140     widgetHasChanged();
141 }
142
143 void CoreHighlightSettingsPage::addNewHighlightRow(bool enable, const QString &name, bool regex, bool cs,
144                                                    const QString &sender, const QString &chanName, bool self)
145 {
146     ui.highlightTable->setRowCount(ui.highlightTable->rowCount() + 1);
147
148     auto *nameItem = new QTableWidgetItem(name);
149
150     auto *regexItem = new QTableWidgetItem("");
151     if (regex)
152         regexItem->setCheckState(Qt::Checked);
153     else
154         regexItem->setCheckState(Qt::Unchecked);
155     regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
156
157     auto *csItem = new QTableWidgetItem("");
158     if (cs)
159         csItem->setCheckState(Qt::Checked);
160     else
161         csItem->setCheckState(Qt::Unchecked);
162     csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
163
164     auto *enableItem = new QTableWidgetItem("");
165     if (enable)
166         enableItem->setCheckState(Qt::Checked);
167     else
168         enableItem->setCheckState(Qt::Unchecked);
169     enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
170
171     auto *chanNameItem = new QTableWidgetItem(chanName);
172
173     auto *senderItem = new QTableWidgetItem(sender);
174
175     int lastRow = ui.highlightTable->rowCount() - 1;
176     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
177     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
178     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
179     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
180     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
181     ui.highlightTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
182
183     if (!self)
184         ui.highlightTable->setCurrentItem(nameItem);
185
186     highlightList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, false, sender, chanName);
187 }
188
189 void CoreHighlightSettingsPage::addNewIgnoredRow(bool enable, const QString &name, bool regex, bool cs,
190                                                  const QString &sender, const QString &chanName, bool self)
191 {
192     ui.ignoredTable->setRowCount(ui.ignoredTable->rowCount() + 1);
193
194     auto *nameItem = new QTableWidgetItem(name);
195
196     auto *regexItem = new QTableWidgetItem("");
197     if (regex)
198         regexItem->setCheckState(Qt::Checked);
199     else
200         regexItem->setCheckState(Qt::Unchecked);
201     regexItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
202
203     auto *csItem = new QTableWidgetItem("");
204     if (cs)
205         csItem->setCheckState(Qt::Checked);
206     else
207         csItem->setCheckState(Qt::Unchecked);
208     csItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
209
210     auto *enableItem = new QTableWidgetItem("");
211     if (enable)
212         enableItem->setCheckState(Qt::Checked);
213     else
214         enableItem->setCheckState(Qt::Unchecked);
215     enableItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
216
217     auto *chanNameItem = new QTableWidgetItem(chanName);
218
219     auto *senderItem = new QTableWidgetItem(sender);
220
221     int lastRow = ui.ignoredTable->rowCount() - 1;
222     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::NameColumn, nameItem);
223     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::RegExColumn, regexItem);
224     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::CsColumn, csItem);
225     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::EnableColumn, enableItem);
226     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::SenderColumn, senderItem);
227     ui.ignoredTable->setItem(lastRow, CoreHighlightSettingsPage::ChanColumn, chanNameItem);
228
229     if (!self)
230         ui.ignoredTable->setCurrentItem(nameItem);
231
232     ignoredList << HighlightRuleManager::HighlightRule(name, regex, cs, enable, true, sender, chanName);
233 }
234
235 void CoreHighlightSettingsPage::removeSelectedHighlightRows()
236 {
237     QList<int> selectedRows;
238     QList<QTableWidgetItem *> selectedItemList = ui.highlightTable->selectedItems();
239     for (auto selectedItem : selectedItemList) {
240         selectedRows.append(selectedItem->row());
241     }
242     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
243     int lastRow = -1;
244     for (auto row : selectedRows) {
245         if (row != lastRow) {
246             ui.highlightTable->removeRow(row);
247             highlightList.removeAt(row);
248         }
249         lastRow = row;
250     }
251 }
252
253 void CoreHighlightSettingsPage::removeSelectedIgnoredRows()
254 {
255     QList<int> selectedRows;
256     QList<QTableWidgetItem *> selectedItemList = ui.ignoredTable->selectedItems();
257     for (auto selectedItem : selectedItemList) {
258         selectedRows.append(selectedItem->row());
259     }
260     qSort(selectedRows.begin(), selectedRows.end(), qGreater<int>());
261     int lastRow = -1;
262     for (auto row : selectedRows) {
263         if (row != lastRow) {
264             ui.ignoredTable->removeRow(row);
265             ignoredList.removeAt(row);
266         }
267         lastRow = row;
268     }
269 }
270
271 void CoreHighlightSettingsPage::selectHighlightRow(QTableWidgetItem *item)
272 {
273     int row = item->row();
274     bool selected = item->isSelected();
275     ui.highlightTable
276         ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
277                            selected);
278 }
279
280 void CoreHighlightSettingsPage::selectIgnoredRow(QTableWidgetItem *item)
281 {
282     int row = item->row();
283     bool selected = item->isSelected();
284     ui.ignoredTable
285         ->setRangeSelected(QTableWidgetSelectionRange(row, 0, row, CoreHighlightSettingsPage::ColumnCount - 1),
286                            selected);
287 }
288
289 void CoreHighlightSettingsPage::emptyHighlightTable()
290 {
291     // ui.highlight and highlightList should have the same size, but just to make sure.
292     if (ui.highlightTable->rowCount() != highlightList.size()) {
293         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
294     }
295     ui.highlightTable->clear();
296     highlightList.clear();
297 }
298
299 void CoreHighlightSettingsPage::emptyIgnoredTable()
300 {
301     // ui.highlight and highlightList should have the same size, but just to make sure.
302     if (ui.ignoredTable->rowCount() != ignoredList.size()) {
303         qDebug() << "something is wrong: ui.highlight and highlightList don't have the same size!";
304     }
305     ui.ignoredTable->clear();
306     ignoredList.clear();
307 }
308
309 void CoreHighlightSettingsPage::highlightTableChanged(QTableWidgetItem *item)
310 {
311     if (item->row() + 1 > highlightList.size())
312         return;
313
314     auto highlightRule = highlightList.value(item->row());
315
316
317     switch (item->column()) {
318         case CoreHighlightSettingsPage::EnableColumn:
319             highlightRule.isEnabled = (item->checkState() == Qt::Checked);
320         case CoreHighlightSettingsPage::NameColumn:
321             if (item->text() == "")
322                 item->setText(tr("this shouldn't be empty"));
323             highlightRule.name = item->text();
324             break;
325         case CoreHighlightSettingsPage::RegExColumn:
326             highlightRule.isRegEx = (item->checkState() == Qt::Checked);
327             break;
328         case CoreHighlightSettingsPage::CsColumn:
329             highlightRule.isCaseSensitive = (item->checkState() == Qt::Checked);
330             break;
331         case CoreHighlightSettingsPage::SenderColumn:
332             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
333                 item->setText("");
334             highlightRule.sender = item->text();
335             break;
336         case CoreHighlightSettingsPage::ChanColumn:
337             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
338                 item->setText("");
339             highlightRule.chanName = item->text();
340             break;
341     }
342     highlightList[item->row()] = highlightRule;
343     emit widgetHasChanged();
344 }
345
346 void CoreHighlightSettingsPage::ignoredTableChanged(QTableWidgetItem *item)
347 {
348     if (item->row() + 1 > ignoredList.size())
349         return;
350
351     auto ignoredRule = ignoredList.value(item->row());
352
353
354     switch (item->column()) {
355         case CoreHighlightSettingsPage::EnableColumn:
356             ignoredRule.isEnabled = (item->checkState() == Qt::Checked);
357         case CoreHighlightSettingsPage::NameColumn:
358             if (item->text() == "")
359                 item->setText(tr("this shouldn't be empty"));
360             ignoredRule.name = item->text();
361             break;
362         case CoreHighlightSettingsPage::RegExColumn:
363             ignoredRule.isRegEx = (item->checkState() == Qt::Checked);
364             break;
365         case CoreHighlightSettingsPage::CsColumn:
366             ignoredRule.isCaseSensitive = (item->checkState() == Qt::Checked);
367             break;
368         case CoreHighlightSettingsPage::SenderColumn:
369             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
370                 item->setText("");
371             ignoredRule.sender = item->text();
372             break;
373         case CoreHighlightSettingsPage::ChanColumn:
374             if (!item->text().isEmpty() && item->text().trimmed().isEmpty())
375                 item->setText("");
376             ignoredRule.chanName = item->text();
377             break;
378     }
379     ignoredList[item->row()] = ignoredRule;
380     emit widgetHasChanged();
381 }
382
383 void CoreHighlightSettingsPage::load()
384 {
385     emptyHighlightTable();
386     emptyIgnoredTable();
387
388     auto ruleManager = Client::highlightRuleManager();
389     if (ruleManager) {
390         for (auto &rule : ruleManager->highlightRuleList()) {
391             if (rule.isInverse) {
392                 addNewIgnoredRow(rule.isEnabled,
393                                  rule.name,
394                                  rule.isRegEx,
395                                  rule.isCaseSensitive,
396                                  rule.sender,
397                                  rule.chanName);
398             }
399             else {
400                 addNewHighlightRow(rule.isEnabled, rule.name, rule.isRegEx, rule.isCaseSensitive, rule.sender,
401                                    rule.chanName);
402             }
403         }
404
405         ui.highlightNicksComboBox
406             ->setCurrentIndex(ui.highlightNicksComboBox->findData(QVariant(ruleManager->highlightNick())));
407         ui.nicksCaseSensitive->setChecked(ruleManager->nicksCaseSensitive());
408
409         setChangedState(false);
410         _initialized = true;
411     }
412 }
413
414 void CoreHighlightSettingsPage::save()
415 {
416     if (!hasChanged())
417         return;
418
419     if (!_initialized)
420         return;
421
422     auto ruleManager = Client::highlightRuleManager();
423     if (ruleManager == nullptr)
424         return;
425
426     auto clonedManager = HighlightRuleManager();
427     clonedManager.fromVariantMap(ruleManager->toVariantMap());
428     clonedManager.clear();
429
430     for (auto &rule : highlightList) {
431         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, false,
432                                        rule.sender, rule.chanName);
433     }
434
435     for (auto &rule : ignoredList) {
436         clonedManager.addHighlightRule(rule.name, rule.isRegEx, rule.isCaseSensitive, rule.isEnabled, true,
437                                        rule.sender, rule.chanName);
438     }
439
440     auto highlightNickType = ui.highlightNicksComboBox->currentData().value<HighlightRuleManager::HighlightNickType>();
441
442     clonedManager.setHighlightNick(highlightNickType);
443     clonedManager.setNicksCaseSensitive(ui.nicksCaseSensitive->isChecked());
444
445     ruleManager->requestUpdate(clonedManager.toVariantMap());
446     setChangedState(false);
447     load();
448 }
449
450 void CoreHighlightSettingsPage::widgetHasChanged()
451 {
452     setChangedState(true);
453 }