qa: Avoid deprecation warnings for QList/QSet conversions
[quassel.git] / src / client / messagefilter.cpp
index 3ef02ee..6ff6378 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-08 by the Quassel Project                          *
+ *   Copyright (C) 2005-2019 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   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.             *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
 #include "messagefilter.h"
+
+#include <algorithm>
+
+#include "buffermodel.h"
 #include "buffersettings.h"
+#include "client.h"
+#include "clientignorelistmanager.h"
+#include "messagemodel.h"
+#include "networkmodel.h"
+#include "util.h"
+
+MessageFilter::MessageFilter(QAbstractItemModel* source, QObject* parent)
+    : QSortFilterProxyModel(parent)
+    , _messageTypeFilter(0)
+{
+    init();
+    setSourceModel(source);
+}
 
-MessageFilter::MessageFilter(QAbstractItemModel *source, QObject *parent)
-  : QSortFilterProxyModel(parent),
-    _messageTypeFilter(0)
+MessageFilter::MessageFilter(MessageModel* source, const QList<BufferId>& buffers, QObject* parent)
+    : QSortFilterProxyModel(parent)
+    , _validBuffers(toQSet(buffers))
+    , _messageTypeFilter(0)
 {
-  init();
-  setSourceModel(source);
+    init();
+    setSourceModel(source);
 }
 
-MessageFilter::MessageFilter(MessageModel *source, const QList<BufferId> &buffers, QObject *parent)
-  : QSortFilterProxyModel(parent),
-    _validBuffers(buffers.toSet()),
-    _messageTypeFilter(0)
+void MessageFilter::init()
 {
-  init();
-  setSourceModel(source);
+    setDynamicSortFilter(true);
+
+    _userNoticesTarget = _serverNoticesTarget = _errorMsgsTarget = -1;
+
+    BufferSettings defaultSettings;
+    defaultSettings.notify("UserNoticesTarget", this, &MessageFilter::messageRedirectionChanged);
+    defaultSettings.notify("ServerNoticesTarget", this, &MessageFilter::messageRedirectionChanged);
+    defaultSettings.notify("ErrorMsgsTarget", this, &MessageFilter::messageRedirectionChanged);
+    messageRedirectionChanged();
+
+    _messageTypeFilter = defaultSettings.messageFilter();
+    defaultSettings.notify("MessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
+
+    BufferSettings mySettings(MessageFilter::idString());
+    if (mySettings.hasFilter())
+        _messageTypeFilter = mySettings.messageFilter();
+    mySettings.notify("MessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
+    mySettings.notify("hasMessageTypeFilter", this, &MessageFilter::messageTypeFilterChanged);
 }
 
-void MessageFilter::init() {
-  BufferSettings defaultSettings;
-  _messageTypeFilter = defaultSettings.messageFilter();
-  defaultSettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
+void MessageFilter::messageTypeFilterChanged()
+{
+    int newFilter;
+    BufferSettings defaultSettings;
+    newFilter = BufferSettings().messageFilter();
+
+    BufferSettings mySettings(idString());
+    if (mySettings.hasFilter())
+        newFilter = mySettings.messageFilter();
 
-  BufferSettings mySettings(idString());
-  if(mySettings.hasFilter())
-    _messageTypeFilter = mySettings.messageFilter();
-  mySettings.notify("MessageTypeFilter", this, SLOT(messageTypeFilterChanged()));
+    if (_messageTypeFilter != newFilter) {
+        _messageTypeFilter = newFilter;
+        _filteredQuitMsgTime.clear();
+        invalidateFilter();
+    }
 }
 
-void MessageFilter::messageTypeFilterChanged() {
-  int newFilter;
-  BufferSettings defaultSettings();
-  newFilter = BufferSettings().messageFilter();
+void MessageFilter::messageRedirectionChanged()
+{
+    BufferSettings bufferSettings;
+    bool changed = false;
+
+    if (_userNoticesTarget != bufferSettings.userNoticesTarget()) {
+        _userNoticesTarget = bufferSettings.userNoticesTarget();
+        changed = true;
+    }
+
+    if (_serverNoticesTarget != bufferSettings.serverNoticesTarget()) {
+        _serverNoticesTarget = bufferSettings.serverNoticesTarget();
+        changed = true;
+    }
 
-  BufferSettings mySettings(idString());
-  if(mySettings.hasFilter())
-    newFilter = mySettings.messageFilter();
+    if (_errorMsgsTarget != bufferSettings.errorMsgsTarget()) {
+        _errorMsgsTarget = bufferSettings.errorMsgsTarget();
+        changed = true;
+    }
 
-  if(_messageTypeFilter != newFilter) {
-    _messageTypeFilter = newFilter;
-    invalidateFilter();
-  }
+    if (changed)
+        invalidateFilter();
 }
 
-QString MessageFilter::idString() const {
-  if(_validBuffers.isEmpty())
-    return "*";
+QString MessageFilter::idString() const
+{
+    if (_validBuffers.isEmpty())
+        return "*";
+
+    QList<BufferId> bufferIds = _validBuffers.values();
+    std::sort(bufferIds.begin(), bufferIds.end());
 
-  QList<BufferId> bufferIds = _validBuffers.toList();;
-  qSort(bufferIds);
-  
-  QStringList bufferIdStrings;
-  foreach(BufferId id, bufferIds)
-    bufferIdStrings << QString::number(id.toInt());
+    QStringList bufferIdStrings;
+    foreach (BufferId id, bufferIds)
+        bufferIdStrings << QString::number(id.toInt());
 
-  return bufferIdStrings.join("|");
+    return bufferIdStrings.join("|");
 }
 
-bool MessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
-  Q_UNUSED(sourceParent);
-  QModelIndex sourceIdx = sourceModel()->index(sourceRow, 0);
-  if(_messageTypeFilter & sourceModel()->data(sourceIdx, MessageModel::TypeRole).toInt())
-    return false;
+bool MessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
+{
+    Q_UNUSED(sourceParent);
+    QModelIndex sourceIdx = sourceModel()->index(sourceRow, 2);
+    Message::Type messageType = (Message::Type)sourceIdx.data(MessageModel::TypeRole).toInt();
+
+    // apply message type filter
+    if (_messageTypeFilter & messageType)
+        return false;
 
-  if(_validBuffers.isEmpty())
-    return true;
+    if (_validBuffers.isEmpty())
+        return true;
 
-  BufferId id = sourceModel()->data(sourceIdx, MessageModel::BufferIdRole).value<BufferId>();
-  if(!id.isValid()) {
-    return true;
-  }
-  return _validBuffers.contains(id);
+    BufferId bufferId = sourceIdx.data(MessageModel::BufferIdRole).value<BufferId>();
+    if (!bufferId.isValid()) {
+        return true;
+    }
+
+    // MsgId msgId = sourceIdx.data(MessageModel::MsgIdRole).value<MsgId>();
+    Message::Flags flags = (Message::Flags)sourceIdx.data(MessageModel::FlagsRole).toInt();
+
+    NetworkId myNetworkId = networkId();
+    NetworkId msgNetworkId = Client::networkModel()->networkId(bufferId);
+    if (myNetworkId != msgNetworkId)
+        return false;
+
+    // ignorelist handling
+    // only match if message is not flagged as server msg
+    if (!(flags & Message::ServerMsg) && Client::ignoreListManager()
+        && Client::ignoreListManager()->match(sourceIdx.data(MessageModel::MessageRole).value<Message>(),
+                                              Client::networkModel()->networkName(bufferId)))
+        return false;
+
+    if (flags & Message::Redirected) {
+        int redirectionTarget = 0;
+        switch (messageType) {
+        case Message::Notice:
+            if (Client::networkModel()->bufferType(bufferId) != BufferInfo::ChannelBuffer) {
+                if (flags & Message::ServerMsg) {
+                    // server notice
+                    redirectionTarget = _serverNoticesTarget;
+                }
+                else {
+                    redirectionTarget = _userNoticesTarget;
+                }
+            }
+            break;
+        case Message::Error:
+            redirectionTarget = _errorMsgsTarget;
+            break;
+        default:
+            break;
+        }
+
+        if (redirectionTarget & BufferSettings::DefaultBuffer && _validBuffers.contains(bufferId))
+            return true;
+
+        if (redirectionTarget & BufferSettings::CurrentBuffer && !(flags & Message::Backlog)) {
+            BufferId redirectedTo = sourceModel()->data(sourceIdx, MessageModel::RedirectedToRole).value<BufferId>();
+            if (!redirectedTo.isValid()) {
+                BufferId redirectedTo = Client::bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
+                if (redirectedTo.isValid())
+                    sourceModel()->setData(sourceIdx, QVariant::fromValue(redirectedTo), MessageModel::RedirectedToRole);
+            }
+
+            if (_validBuffers.contains(redirectedTo))
+                return true;
+        }
+
+        if (redirectionTarget & BufferSettings::StatusBuffer) {
+            QSet<BufferId>::const_iterator idIter = _validBuffers.constBegin();
+            while (idIter != _validBuffers.constEnd()) {
+                if (Client::networkModel()->bufferType(*idIter) == BufferInfo::StatusBuffer)
+                    return true;
+                ++idIter;
+            }
+        }
+
+        return false;
+    }
+
+    if (_validBuffers.contains(bufferId)) {
+        return true;
+    }
+    else {
+        // show Quit messages in Query buffers:
+        if (bufferType() != BufferInfo::QueryBuffer)
+            return false;
+        if (!(messageType & Message::Quit))
+            return false;
+
+        if (myNetworkId != msgNetworkId)
+            return false;
+
+        // Extract timestamp and nickname from the new quit message
+        qint64 messageTimestamp = sourceModel()->data(sourceIdx, MessageModel::TimestampRole).value<QDateTime>().toMSecsSinceEpoch();
+        QString quiter = nickFromMask(sourceModel()->data(sourceIdx, MessageModel::MessageRole).value<Message>().sender()).toLower();
+
+        // Check that nickname matches query name
+        if (quiter != bufferName().toLower())
+            return false;
+
+        // Check if a quit message was already forwarded within +/- 1000 ms
+        static constexpr qint64 MAX_QUIT_DELTA_MS = 1 * 1000;
+        // No need to check if it's the appropriate buffer, each query has a unique message filter
+        if (std::binary_search(_filteredQuitMsgTime.begin(), _filteredQuitMsgTime.end(), messageTimestamp, [](qint64 a, qint64 b) {
+                return ((a + MAX_QUIT_DELTA_MS) < b);
+            })) {
+            // New element is less than if at least 1000 ms older/newer
+            // Match found, no need to forward another quit message
+            return false;
+        }
+
+        // Mark query as having a quit message inserted
+        auto* that = const_cast<MessageFilter*>(this);
+        that->_filteredQuitMsgTime.insert(messageTimestamp);
+        return true;
+    }
+}
+
+void MessageFilter::requestBacklog()
+{
+    QSet<BufferId>::const_iterator bufferIdIter = _validBuffers.constBegin();
+    while (bufferIdIter != _validBuffers.constEnd()) {
+        Client::messageModel()->requestBacklog(*bufferIdIter);
+        ++bufferIdIter;
+    }
 }