From 7203282c4d87cbe21cc07b4c4a652110ae84c4a9 Mon Sep 17 00:00:00 2001 From: Sebastian Goth Date: Sun, 16 Aug 2009 17:10:35 +0200 Subject: [PATCH] Ignorelist settingspage --- src/qtui/mainwin.cpp | 2 + src/qtui/settingspages/ignorelisteditdlg.ui | 320 ++++++++++++++++++ src/qtui/settingspages/ignorelistmodel.cpp | 279 +++++++++++++++ src/qtui/settingspages/ignorelistmodel.h | 93 +++++ .../settingspages/ignorelistsettingspage.cpp | 274 +++++++++++++++ .../settingspages/ignorelistsettingspage.h | 93 +++++ .../settingspages/ignorelistsettingspage.ui | 83 +++++ src/qtui/settingspages/settingspages.inc | 8 +- 8 files changed, 1148 insertions(+), 4 deletions(-) create mode 100644 src/qtui/settingspages/ignorelisteditdlg.ui create mode 100644 src/qtui/settingspages/ignorelistmodel.cpp create mode 100644 src/qtui/settingspages/ignorelistmodel.h create mode 100644 src/qtui/settingspages/ignorelistsettingspage.cpp create mode 100644 src/qtui/settingspages/ignorelistsettingspage.h create mode 100644 src/qtui/settingspages/ignorelistsettingspage.ui diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index b56c7947..ee33952e 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -102,6 +102,7 @@ #include "settingspages/generalsettingspage.h" #include "settingspages/highlightsettingspage.h" #include "settingspages/identitiessettingspage.h" +#include "settingspages/ignorelistsettingspage.h" #include "settingspages/inputwidgetsettingspage.h" #include "settingspages/itemviewsettingspage.h" #include "settingspages/networkssettingspage.h" @@ -825,6 +826,7 @@ void MainWin::showSettingsDlg() { dlg->registerSettingsPage(new IdentitiesSettingsPage(dlg)); dlg->registerSettingsPage(new NetworksSettingsPage(dlg)); dlg->registerSettingsPage(new AliasesSettingsPage(dlg)); + dlg->registerSettingsPage(new IgnoreListSettingsPage(dlg)); dlg->show(); } diff --git a/src/qtui/settingspages/ignorelisteditdlg.ui b/src/qtui/settingspages/ignorelisteditdlg.ui new file mode 100644 index 00000000..98cae6e5 --- /dev/null +++ b/src/qtui/settingspages/ignorelisteditdlg.ui @@ -0,0 +1,320 @@ + + + Sebastian Goth <seezer@roath.org> + IgnoreListEditDlg + + + + 0 + 0 + 389 + 368 + + + + Configure ignore rule + + + + :/icons/oxygen/16x16/actions/configure.png:/icons/oxygen/16x16/actions/configure.png + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Strictness:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">Dynamic:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Messages are filtered "on the fly".</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Whenever you disable/delete the ignore rule,</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">the messages are shown again.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">Permanent:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Messages are filtered before they get stored in the database.</p></body></html> + + + Strictness + + + + + + Dynamic + + + strictnessButtonGroup + + + + + + + Permanent + + + strictnessButtonGroup + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Rule Type:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">By Sender:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The rule is matched against the sender string </p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">"nick!ident@host.name"</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">By Message:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The rule is matched against the actual message content</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; text-decoration: underline;"></p></body></html> + + + Rule Type + + + + + + Sender + + + typeButtonGroup + + + + + + + Message + + + typeButtonGroup + + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Ignore rule:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Depending on the type of the rule, the text is matched against either:</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- <span style=" text-decoration: underline;">the message content:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; text-decoration: underline;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Example:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> "*foobar*" matches any text containing the word "foobar"</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- <span style=" text-decoration: underline;">the sender string </span><span style=" font-style:italic; text-decoration: underline;">nick!ident@host.name</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic; text-decoration: underline;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Examples:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> "*@foobar.com" matches any sender from host "foobar.com"</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> "stupid!.+" (RegEx) matches any sender with nickname "stupid" from any host</p></body></html> + + + Ignore rule + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Use RegularExpressions:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If enabled, rules follow regular expression syntax.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Otherwise rules allow wildcard matching with </p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> *: represents "any amount of any character"</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> ?: represents "one or none character"</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> + + + Use Regular Expressions + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Enable / Disable:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:768;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Only enabled rules are filtered.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:768;">For dynamic rules, disabling actually shows the filtered messages again.</p></body></html> + + + Enabled + + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Scope:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">Global:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The rule is active for any channel on any network</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">Network:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The list below is interpreted as a list of networks for which the rule should match</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" text-decoration: underline;">Channel:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The list below is interpreted as a list of channels for which the rule should match</p></body></html> + + + Scope + + + + + + + + Global + + + scopeButtonGroup + + + + + + + Network + + + scopeButtonGroup + + + + + + + Channel + + + scopeButtonGroup + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Scope rule:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A scope rule is a semicolon separated list of either</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">network or channel names.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Example:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">"#quassel*; #foobar"</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">would match on #foobar and on any channel starting with "#quassel"</p></body></html> + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + IgnoreListEditDlg + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + IgnoreListEditDlg + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + + + + diff --git a/src/qtui/settingspages/ignorelistmodel.cpp b/src/qtui/settingspages/ignorelistmodel.cpp new file mode 100644 index 00000000..445201a7 --- /dev/null +++ b/src/qtui/settingspages/ignorelistmodel.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (C) 2005-09 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "ignorelistmodel.h" + +#include +#include +#include + +#include "client.h" +#include "signalproxy.h" + +IgnoreListModel::IgnoreListModel(QObject *parent) + : QAbstractItemModel(parent), + _configChanged(false), + _modelReady(false) +{ + // we need this signal for future connects to reset the data; + connect(Client::instance(), SIGNAL(connected()), this, SLOT(clientConnected())); + connect(Client::instance(), SIGNAL(disconnected()), this, SLOT(clientDisconnected())); + + if(Client::isConnected()) + clientConnected(); + else + emit modelReady(false); +} + +QVariant IgnoreListModel::data(const QModelIndex &index, int role) const { + if(!_modelReady) + return QVariant(); + + if(!index.isValid() || index.row() >= rowCount() || index.column() >= columnCount()) + return QVariant(); + + switch(role) { + case Qt::ToolTipRole: + switch(index.column()) { + /* + case 0: return "Type:
" + "BySender:
" + "The ignore rule is matched against the nick!ident@host.mask sender-string.
" + "ByMessage:
" + "The ignore rule is matched against the message content."; + case 1: + return "Strictness:
" + "Dynamic:
" + "Messages are hidden but still get stored in the database.
Deactivate or delete an ignore rule to show the messages again
" + "Permanent:
" + "Messages are never stored or shown anywhere."; + */ + case 0: + return tr("Enable / Disable:
" + "Only enabled rules are filtered.
" + "For dynamic rules, disabling actually shows the filtered messages again"); + case 2: + return tr("Ignore rule:
" + "Depending on the type of the rule, the text is matched against either:

" + "- the message content:
" + "Example:
" + " \"*foobar*\" matches any text containing the word \"foobar\"

" + "- the sender string nick!ident@host.name
" + "Example:
" + " \"*@foobar.com\" matches any sender from host foobar.com
" + " \"stupid!.+\" (RegEx) matches any sender with nickname \"stupid\" from any host
"); + default: + return QVariant(); + } + case Qt::DisplayRole: + switch(index.column()) { + case 1: + if(ignoreListManager()[index.row()].type == IgnoreListManager::SenderIgnore) + return tr("By Sender"); + else + return tr("By Message"); + } + case Qt::EditRole: + switch(index.column()) { + case 0: + return ignoreListManager()[index.row()].isActive; + case 1: + return ignoreListManager()[index.row()].type; + case 2: + return ignoreListManager()[index.row()].ignoreRule; + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +bool IgnoreListModel::setData(const QModelIndex &index, const QVariant &value, int role) { + if(!_modelReady) + return false; + + if(!index.isValid() || index.row() >= rowCount() || index.column() >= columnCount() || role != Qt::EditRole) + return false; + + QVariant newValue = value; + if(newValue.isNull()) + return false; + + switch(index.column()) { + case 0: + cloneIgnoreListManager()[index.row()].isActive = newValue.toBool(); + return true; + case 1: + cloneIgnoreListManager()[index.row()].type = (IgnoreListManager::IgnoreType)newValue.toInt(); + return true; + case 2: + if(ignoreListManager().contains(newValue.toString())) { + return false; + } else { + cloneIgnoreListManager()[index.row()].ignoreRule = newValue.toString(); + return true; + } + default: + return false; + } +} + +bool IgnoreListModel::newIgnoreRule(const IgnoreListManager::IgnoreListItem &item) { + IgnoreListManager &manager = cloneIgnoreListManager(); + if(manager.contains(item.ignoreRule)) + return false; + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + manager.addIgnoreListItem(item); + endInsertRows(); + return true; +} + +void IgnoreListModel::loadDefaults() { + /*if(!_modelReady) + return; + + IgnoreListManager &manager = cloneIgnoreListManager(); + + if(!manager.isEmpty()) { + beginRemoveRows(QModelIndex(), 0, rowCount() - 1); + for(int i = rowCount() - 1; i >= 0; i--) + manager.removeAt(i); + endRemoveRows(); + } + + IgnoreListManager::IgnoreList defaults = IgnoreListModel::defaults(); + beginInsertRows(QModelIndex(), 0, defaults.count() - 1); + foreach(IgnoreListManager::IgnoreListItem item, defaults) { + manager.addIgnoreListItem(item.ignoreRule, item.isRegEx, item.strictness, item.scope, item.scopeRule); + } + endInsertRows();*/ +} + +void IgnoreListModel::removeIgnoreRule(int index) { + if(index < 0 || index >= rowCount()) + return; + + IgnoreListManager &manager = cloneIgnoreListManager(); + beginRemoveRows(QModelIndex(), index, index); + manager.removeAt(index); + endRemoveRows(); +} + +Qt::ItemFlags IgnoreListModel::flags(const QModelIndex &index) const { + if(!index.isValid()) { + return Qt::ItemIsDropEnabled; + } else { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; + } +} + + +QVariant IgnoreListModel::headerData(int section, Qt::Orientation orientation, int role) const { + QStringList header; + header << tr("Enabled") + << tr("Type") + << tr("Ignore Rule"); + + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) + return header[section]; + + return QVariant(); +} + +QModelIndex IgnoreListModel::index(int row, int column, const QModelIndex &parent) const { + Q_UNUSED(parent); + if(row >= rowCount() || column >= columnCount()) + return QModelIndex(); + + return createIndex(row, column); +} + + +const IgnoreListManager &IgnoreListModel::ignoreListManager() const { + if(_configChanged) + return _clonedIgnoreListManager; + else + return *Client::ignoreListManager(); +} + +IgnoreListManager &IgnoreListModel::ignoreListManager() { + if(_configChanged) + return _clonedIgnoreListManager; + else + return *Client::ignoreListManager(); +} + +IgnoreListManager &IgnoreListModel::cloneIgnoreListManager() { + if(!_configChanged) { + _clonedIgnoreListManager = *Client::ignoreListManager(); + _configChanged = true; + emit configChanged(true); + } + return _clonedIgnoreListManager; +} + +void IgnoreListModel::revert() { + if(!_configChanged) + return; + + _configChanged = false; + emit configChanged(false); + reset(); +} + +void IgnoreListModel::commit() { + if(!_configChanged) + return; + + Client::ignoreListManager()->requestUpdate(_clonedIgnoreListManager.toVariantMap()); + revert(); +} + +void IgnoreListModel::initDone() { + _modelReady = true; + reset(); + emit modelReady(true); +} + +void IgnoreListModel::clientConnected() { + connect(Client::ignoreListManager(), SIGNAL(updated(QVariantMap)), SLOT(revert())); + if(Client::ignoreListManager()->isInitialized()) + initDone(); + else + connect(Client::ignoreListManager(), SIGNAL(initDone()), SLOT(initDone())); +} + +void IgnoreListModel::clientDisconnected() { + // clear + _clonedIgnoreListManager = ClientIgnoreListManager(); + _modelReady = false; + reset(); + emit modelReady(false); +} + +const IgnoreListManager::IgnoreListItem &IgnoreListModel::ignoreListItemAt(int row) const { + return ignoreListManager()[row]; +} +// FIXME use QModelIndex? +void IgnoreListModel::setIgnoreListItemAt(int row, const IgnoreListManager::IgnoreListItem &item) { + cloneIgnoreListManager()[row] = item; + emit dataChanged(createIndex(row, 0), createIndex(row, 2)); +} diff --git a/src/qtui/settingspages/ignorelistmodel.h b/src/qtui/settingspages/ignorelistmodel.h new file mode 100644 index 00000000..87b242a3 --- /dev/null +++ b/src/qtui/settingspages/ignorelistmodel.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2005-09 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef IGNORELISTMODEL_H +#define IGNORELISTMODEL_H + +#include +#include + +#include "clientignorelistmanager.h" + +class IgnoreListModel : public QAbstractItemModel { + Q_OBJECT + +public: + IgnoreListModel(QObject *parent = 0); + + virtual QVariant data(const QModelIndex &index, int role) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + + inline QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } + + inline int rowCount(const QModelIndex &parent = QModelIndex()) const; + inline int columnCount(const QModelIndex &parent = QModelIndex()) const; + + inline bool configChanged() const { return _configChanged; } + inline bool isReady() const { return _modelReady; } + + const IgnoreListManager::IgnoreListItem &ignoreListItemAt(int row) const; + void setIgnoreListItemAt(int row, const IgnoreListManager::IgnoreListItem &item); + bool newIgnoreRule(const IgnoreListManager::IgnoreListItem &item); + +public slots: + void loadDefaults(); + void removeIgnoreRule(int index); + void revert(); + void commit(); + +signals: + void configChanged(bool); + void modelReady(bool); + +private: + ClientIgnoreListManager _clonedIgnoreListManager; + bool _configChanged; + bool _modelReady; + + const IgnoreListManager &ignoreListManager() const; + IgnoreListManager &ignoreListManager(); + IgnoreListManager &cloneIgnoreListManager(); + +private slots: + void clientConnected(); + void clientDisconnected(); + void initDone(); +}; + +// Inlines +int IgnoreListModel::rowCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + return isReady() ? ignoreListManager().count() : 0; +} + +int IgnoreListModel::columnCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + return isReady() ? 3 : 0; +} + + +#endif //IGNORELISTMODEL_H diff --git a/src/qtui/settingspages/ignorelistsettingspage.cpp b/src/qtui/settingspages/ignorelistsettingspage.cpp new file mode 100644 index 00000000..62dfc889 --- /dev/null +++ b/src/qtui/settingspages/ignorelistsettingspage.cpp @@ -0,0 +1,274 @@ +/*************************************************************************** + * Copyright (C) 2005-09 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "ignorelistsettingspage.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "iconloader.h" + +IgnoreListSettingsPage::IgnoreListSettingsPage(QWidget *parent) + : SettingsPage(tr("Misc"), tr("Ignorelist"), parent) +{ + ui.setupUi(this); + _delegate = new IgnoreListDelegate(ui.ignoreListView); + ui.newIgnoreRuleButton->setIcon(SmallIcon("list-add")); + ui.deleteIgnoreRuleButton->setIcon(SmallIcon("edit-delete")); + ui.editIgnoreRuleButton->setIcon(SmallIcon("configure")); + + ui.ignoreListView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui.ignoreListView->setSelectionMode(QAbstractItemView::SingleSelection); + ui.ignoreListView->setAlternatingRowColors(true); + ui.ignoreListView->setTabKeyNavigation(false); + ui.ignoreListView->setModel(&_ignoreListModel); + // ui.ignoreListView->setEditTriggers(QAbstractItemView::NoEditTriggers); + + // ui.ignoreListView->setSortingEnabled(true); + ui.ignoreListView->verticalHeader()->hide(); + ui.ignoreListView->hideColumn(1); + ui.ignoreListView->resizeColumnToContents(0); + ui.ignoreListView->horizontalHeader()->setStretchLastSection(true); + ui.ignoreListView->setItemDelegateForColumn(0, _delegate); + ui.ignoreListView->viewport()->setAttribute(Qt::WA_Hover); + ui.ignoreListView->viewport()->setMouseTracking(true); + + connect(ui.ignoreListView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(selectionChanged(const QItemSelection &, const QItemSelection &))); + connect(ui.newIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(newIgnoreRule())); + connect(ui.deleteIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(deleteSelectedIgnoreRule())); + connect(ui.editIgnoreRuleButton, SIGNAL(clicked()), this, SLOT(editSelectedIgnoreRule())); + connect(&_ignoreListModel, SIGNAL(configChanged(bool)), this, SLOT(setChangedState(bool))); + connect(&_ignoreListModel, SIGNAL(modelReady(bool)), this, SLOT(enableDialog(bool))); + + enableDialog(_ignoreListModel.isReady()); +} + +IgnoreListSettingsPage::~IgnoreListSettingsPage() { + delete _delegate; +} + +void IgnoreListSettingsPage::load() { + if(_ignoreListModel.configChanged()) + _ignoreListModel.revert(); +} + +void IgnoreListSettingsPage::defaults() { + _ignoreListModel.loadDefaults(); +} + +void IgnoreListSettingsPage::save() { + if(_ignoreListModel.configChanged()) { + _ignoreListModel.commit(); + } +} + +void IgnoreListSettingsPage::enableDialog(bool enabled) { + ui.newIgnoreRuleButton->setEnabled(enabled); + setEnabled(enabled); +} + +void IgnoreListSettingsPage::selectionChanged(const QItemSelection &selection, const QItemSelection &) { + bool state = !selection.isEmpty(); + ui.deleteIgnoreRuleButton->setEnabled(state); + ui.editIgnoreRuleButton->setEnabled(state); +} + +void IgnoreListSettingsPage::deleteSelectedIgnoreRule() { + if(!ui.ignoreListView->selectionModel()->hasSelection()) + return; + + _ignoreListModel.removeIgnoreRule(ui.ignoreListView->selectionModel()->selectedIndexes()[0].row()); +} + +void IgnoreListSettingsPage::newIgnoreRule() { + IgnoreListEditDlg *dlg = new IgnoreListEditDlg(-1, IgnoreListManager::IgnoreListItem(), this); + + while(dlg->exec() == QDialog::Accepted) { + if(!_ignoreListModel.newIgnoreRule(dlg->ignoreListItem())) { + if(QMessageBox::warning(this, + tr("Rule already exists"), + tr("There is already a rule\n\"%1\"\nPlease choose another rule.") + .arg(dlg->ignoreListItem().ignoreRule), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) + == QMessageBox::Cancel) + break; + + IgnoreListManager::IgnoreListItem item = dlg->ignoreListItem(); + delete dlg; + dlg = new IgnoreListEditDlg(-1, item, this); + } + else { + break; + } + } + dlg->deleteLater(); +} + +void IgnoreListSettingsPage::editSelectedIgnoreRule() { + if(!ui.ignoreListView->selectionModel()->hasSelection()) + return; + int row = ui.ignoreListView->selectionModel()->selectedIndexes()[0].row(); + IgnoreListEditDlg dlg(row, _ignoreListModel.ignoreListItemAt(row), this); + dlg.setAttribute(Qt::WA_DeleteOnClose, false); + if(dlg.exec() == QDialog::Accepted) { + _ignoreListModel.setIgnoreListItemAt(row, dlg.ignoreListItem()); + } +} + +void IgnoreListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + if(index.column() == 0) { + QStyle *style = QApplication::style(); + if(option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + + QStyleOptionButton opts; + opts.direction = option.direction; + opts.rect = option.rect; + opts.rect.moveLeft(option.rect.center().rx()-10); + opts.state = option.state; + opts.state |= index.data().toBool() ? QStyle::State_On : QStyle::State_Off; + style->drawControl(QStyle::CE_CheckBox, &opts, painter); + } + else + QStyledItemDelegate::paint(painter, option, index); +} + +IgnoreListEditDlg::IgnoreListEditDlg(int row, const IgnoreListManager::IgnoreListItem &item, QWidget *parent) + : QDialog(parent), _selectedRow(row), _ignoreListItem(item), _hasChanged(false) { + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, false); + setModal(true); + + ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + ui.ignoreRuleLineEdit->setText(item.ignoreRule); + + if(item.type == IgnoreListManager::MessageIgnore) + ui.messageTypeButton->setChecked(true); + else + ui.senderTypeButton->setChecked(true); + + ui.isRegExCheckBox->setChecked(item.isRegEx); + ui.isActiveCheckBox->setChecked(item.isActive); + + if(item.strictness == IgnoreListManager::HardStrictness) + ui.permanentStrictnessButton->setChecked(true); + else + ui.dynamicStrictnessButton->setChecked(true); + + switch(item.scope) { + case IgnoreListManager::NetworkScope: + ui.networkScopeButton->setChecked(true); + ui.scopeRuleTextEdit->setEnabled(true); + break; + case IgnoreListManager::ChannelScope: + ui.channelScopeButton->setChecked(true); + ui.scopeRuleTextEdit->setEnabled(true); + break; + default: + ui.globalScopeButton->setChecked(true); + ui.scopeRuleTextEdit->setEnabled(false); + } + + if(item.scope == IgnoreListManager::GlobalScope) + ui.scopeRuleTextEdit->clear(); + else + ui.scopeRuleTextEdit->setPlainText(item.scopeRule); + + connect(ui.ignoreRuleLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(widgetHasChanged())); + connect(ui.scopeRuleTextEdit, SIGNAL(textChanged()), this, SLOT(widgetHasChanged())); + connect(ui.typeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged())); + connect(ui.strictnessButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged())); + connect(ui.scopeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged())); + connect(ui.typeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(widgetHasChanged())); + connect(ui.isRegExCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged())); + connect(ui.isRegExCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged())); + connect(ui.isActiveCheckBox, SIGNAL(stateChanged(int)), this, SLOT(widgetHasChanged())); + + connect(ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(aboutToAccept())); +} + +void IgnoreListEditDlg::widgetHasChanged() { + if(ui.messageTypeButton->isChecked()) + _clonedIgnoreListItem.type = IgnoreListManager::MessageIgnore; + else + _clonedIgnoreListItem.type = IgnoreListManager::SenderIgnore; + + if(ui.permanentStrictnessButton->isChecked()) + _clonedIgnoreListItem.strictness = IgnoreListManager::HardStrictness; + else + _clonedIgnoreListItem.strictness = IgnoreListManager::SoftStrictness; + + if(ui.networkScopeButton->isChecked()) { + _clonedIgnoreListItem.scope = IgnoreListManager::NetworkScope; + ui.scopeRuleTextEdit->setEnabled(true); + } + else if(ui.channelScopeButton->isChecked()) { + _clonedIgnoreListItem.scope = IgnoreListManager::ChannelScope; + ui.scopeRuleTextEdit->setEnabled(true); + } + else { + _clonedIgnoreListItem.scope = IgnoreListManager::GlobalScope; + ui.scopeRuleTextEdit->setEnabled(false); + } + + if(_clonedIgnoreListItem.scope == IgnoreListManager::GlobalScope) { + _clonedIgnoreListItem.scopeRule = QString(); + } + else { + QStringList text = ui.scopeRuleTextEdit->toPlainText().split(";", QString::SkipEmptyParts); + QStringList::iterator it = text.begin(); + while(it != text.end()) + (*it++).trimmed(); + + _clonedIgnoreListItem.scopeRule = text.join("; "); + } + + _clonedIgnoreListItem.ignoreRule = ui.ignoreRuleLineEdit->text(); + _clonedIgnoreListItem.isRegEx = ui.isRegExCheckBox->isChecked(); + _clonedIgnoreListItem.isActive = ui.isActiveCheckBox->isChecked(); + + if(!_clonedIgnoreListItem.ignoreRule.isEmpty() && _clonedIgnoreListItem != _ignoreListItem) + _hasChanged = true; + else + _hasChanged = false; + ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(_hasChanged); +} + +// provide interactivity for the checkboxes +bool IgnoreListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) { + Q_UNUSED(option) + switch(event->type()) { + case QEvent::MouseButtonRelease: + model->setData(index, !index.data().toBool()); + return true; + // don't show the default editor for the column + case QEvent::MouseButtonDblClick: + return true; + default: + return false; + } +} diff --git a/src/qtui/settingspages/ignorelistsettingspage.h b/src/qtui/settingspages/ignorelistsettingspage.h new file mode 100644 index 00000000..646b95ba --- /dev/null +++ b/src/qtui/settingspages/ignorelistsettingspage.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2005-09 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef IGNORELISTSETTINGSPAGE_H +#define IGNORELISTSETTINGSPAGE_H + +#include + +#include "settingspage.h" +#include "ui_ignorelistsettingspage.h" +#include "ui_ignorelisteditdlg.h" +#include "ignorelistmodel.h" +#include "clientignorelistmanager.h" + +class QEvent; +class QPainter; +class QAbstractItemModel; + +// the delegate is used to draw the checkbox in column 0 +class IgnoreListDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + IgnoreListDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {} + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, + const QModelIndex &index); +}; + +class IgnoreListEditDlg : public QDialog { + Q_OBJECT + +public: + IgnoreListEditDlg(int row, const IgnoreListManager::IgnoreListItem &item, QWidget *parent = 0); + inline IgnoreListManager::IgnoreListItem ignoreListItem() { return _ignoreListItem; } + +private slots: + void widgetHasChanged(); + void aboutToAccept() { _ignoreListItem = _clonedIgnoreListItem; } +private: + int _selectedRow; + IgnoreListManager::IgnoreListItem _ignoreListItem; + IgnoreListManager::IgnoreListItem _clonedIgnoreListItem; + bool _hasChanged; + Ui::IgnoreListEditDlg ui; +}; + +class IgnoreListSettingsPage : public SettingsPage { + Q_OBJECT + +public: + IgnoreListSettingsPage(QWidget *parent = 0); + ~IgnoreListSettingsPage(); + virtual inline bool hasDefaults() const { return true; } + +public slots: + void save(); + void load(); + void defaults(); + +private slots: + void enableDialog(bool); + void deleteSelectedIgnoreRule(); + void editSelectedIgnoreRule(); + void newIgnoreRule(); + void selectionChanged(const QItemSelection &selection, const QItemSelection &); + +private: + IgnoreListDelegate *_delegate; + Ui::IgnoreListSettingsPage ui; + IgnoreListModel _ignoreListModel; +}; + + +#endif //IGNORELISTSETTINGSPAGE_H diff --git a/src/qtui/settingspages/ignorelistsettingspage.ui b/src/qtui/settingspages/ignorelistsettingspage.ui new file mode 100644 index 00000000..22c28b8f --- /dev/null +++ b/src/qtui/settingspages/ignorelistsettingspage.ui @@ -0,0 +1,83 @@ + + + IgnoreListSettingsPage + + + + 0 + 0 + 291 + 229 + + + + Form + + + + + + + + + + + false + + + New + + + + :/16x16/actions/oxygen/16x16/actions/list-add.png:/16x16/actions/oxygen/16x16/actions/list-add.png + + + + + + + false + + + Delete + + + + :/16x16/actions/oxygen/16x16/actions/edit-delete.png:/16x16/actions/oxygen/16x16/actions/edit-delete.png + + + + + + + false + + + &Edit + + + + :/16x16/actions/oxygen/16x16/actions/edit-delete.png:/16x16/actions/oxygen/16x16/actions/edit-delete.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/src/qtui/settingspages/settingspages.inc b/src/qtui/settingspages/settingspages.inc index 66ca1f21..2f1df1c0 100644 --- a/src/qtui/settingspages/settingspages.inc +++ b/src/qtui/settingspages/settingspages.inc @@ -1,9 +1,9 @@ # Putting $FOO in SETTINGSPAGES automatically includes # $FOOsettingspage.cpp, $FOOsettingspage.h and $FOOsettingspage.ui -set(SETTINGSPAGES aliases appearance backlog bufferview chatview connection chatmonitor general highlight identities inputwidget itemview networks) +set(SETTINGSPAGES aliases appearance backlog bufferview chatview connection chatmonitor general highlight identities ignorelist inputwidget itemview networks) # Specify additional files (e.g. for subdialogs) here! -set(SP_SOURCES aliasesmodel.cpp identityeditwidget.cpp notificationssettingspage.cpp) -set(SP_HEADERS aliasesmodel.h identityeditwidget.h notificationssettingspage.h previewbufferview.h) -set(SP_FORMS buffervieweditdlg.ui createidentitydlg.ui identityeditwidget.ui saveidentitiesdlg.ui networkadddlg.ui networkeditdlg.ui nickeditdlg.ui servereditdlg.ui) +set(SP_SOURCES aliasesmodel.cpp identityeditwidget.cpp ignorelistmodel.cpp notificationssettingspage.cpp) +set(SP_HEADERS aliasesmodel.h identityeditwidget.h ignorelistmodel.h notificationssettingspage.h previewbufferview.h) +set(SP_FORMS buffervieweditdlg.ui createidentitydlg.ui identityeditwidget.ui ignorelisteditdlg.ui saveidentitiesdlg.ui networkadddlg.ui networkeditdlg.ui nickeditdlg.ui servereditdlg.ui) -- 2.20.1