qa: Avoid deprecation warnings for QList/QSet conversions
[quassel.git] / src / uisupport / bufferviewfilter.cpp
index c829267..780d0bf 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2015 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  *
@@ -21,8 +21,8 @@
 #include "bufferviewfilter.h"
 
 #include <QApplication>
-#include <QPalette>
 #include <QBrush>
+#include <QPalette>
 
 #include "bufferinfo.h"
 #include "buffermodel.h"
 #include "graphicalui.h"
 #include "networkmodel.h"
 #include "uistyle.h"
-
-class CheckRemovalEvent : public QEvent
-{
-public:
-    CheckRemovalEvent(const QModelIndex &source_index) : QEvent(QEvent::User), index(source_index) {};
-    QPersistentModelIndex index;
-};
-
+#include "util.h"
 
 /*****************************************
-* The Filter for the Tree View
-*****************************************/
-BufferViewFilter::BufferViewFilter(QAbstractItemModel *model, BufferViewConfig *config)
-    : QSortFilterProxyModel(model),
-    _config(0),
-    _sortOrder(Qt::AscendingOrder),
-    _showServerQueries(false),
-    _editMode(false),
-    _enableEditMode(tr("Show / Hide Chats"), this)
+ * The Filter for the Tree View
+ *****************************************/
+BufferViewFilter::BufferViewFilter(QAbstractItemModel* model, BufferViewConfig* config)
+    : QSortFilterProxyModel(model)
+    , _config(nullptr)
+    , _sortOrder(Qt::AscendingOrder)
+    , _showServerQueries(false)
+    , _editMode(false)
+    _enableEditMode(tr("Show / Hide Chats"), this)
 {
     setConfig(config);
     setSourceModel(model);
 
     setDynamicSortFilter(true);
-
-    connect(this, SIGNAL(_dataChanged(const QModelIndex &, const QModelIndex &)),
-        this, SLOT(_q_sourceDataChanged(QModelIndex, QModelIndex)));
+    // Sort case-insensitively (primarily for network names; channels/nicks handled elsewhere)
+    setSortCaseSensitivity(Qt::CaseInsensitive);
 
     _enableEditMode.setCheckable(true);
     _enableEditMode.setChecked(_editMode);
-    connect(&_enableEditMode, SIGNAL(toggled(bool)), this, SLOT(enableEditMode(bool)));
+    connect(&_enableEditMode, &QAction::toggled, this, &BufferViewFilter::enableEditMode);
 
     BufferSettings defaultSettings;
-    defaultSettings.notify("ServerNoticesTarget", this, SLOT(showServerQueriesChanged()));
+    defaultSettings.notify("ServerNoticesTarget", this, &BufferViewFilter::showServerQueriesChanged);
     showServerQueriesChanged();
 }
 
-
-void BufferViewFilter::setConfig(BufferViewConfig *config)
+void BufferViewFilter::setConfig(BufferViewConfig* config)
 {
     if (_config == config)
         return;
 
     if (_config) {
-        disconnect(_config, 0, this, 0);
+        disconnect(_config, nullptr, this, nullptr);
     }
 
     _config = config;
@@ -93,32 +84,19 @@ void BufferViewFilter::setConfig(BufferViewConfig *config)
     else {
         // we use a queued connection here since manipulating the connection list of a sending object
         // doesn't seem to be such a good idea while executing a connected slots.
-        connect(config, SIGNAL(initDone()), this, SLOT(configInitialized()), Qt::QueuedConnection);
+        connect(config, &SyncableObject::initDone, this, &BufferViewFilter::configInitialized, Qt::QueuedConnection);
         invalidate();
     }
 }
 
-
 void BufferViewFilter::configInitialized()
 {
     if (!config())
         return;
 
-//   connect(config(), SIGNAL(bufferViewNameSet(const QString &)), this, SLOT(invalidate()));
-    connect(config(), SIGNAL(configChanged()), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(networkIdSet(const NetworkId &)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(addNewBuffersAutomaticallySet(bool)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(sortAlphabeticallySet(bool)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(hideInactiveBuffersSet(bool)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(allowedBufferTypesSet(int)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(minimumActivitySet(int)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(bufferListSet()), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(bufferAdded(const BufferId &, int)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(bufferMoved(const BufferId &, int)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(bufferRemoved(const BufferId &)), this, SLOT(invalidate()));
-//   connect(config(), SIGNAL(bufferPermanentlyRemoved(const BufferId &)), this, SLOT(invalidate()));
-
-    disconnect(config(), SIGNAL(initDone()), this, SLOT(configInitialized()));
+    connect(config(), &BufferViewConfig::configChanged, this, &QSortFilterProxyModel::invalidate);
+
+    disconnect(config(), &SyncableObject::initDone, this, &BufferViewFilter::configInitialized);
 
     setObjectName(config()->bufferViewName());
 
@@ -126,7 +104,6 @@ void BufferViewFilter::configInitialized()
     emit configChanged();
 }
 
-
 void BufferViewFilter::showServerQueriesChanged()
 {
     BufferSettings bufferSettings;
@@ -138,15 +115,21 @@ void BufferViewFilter::showServerQueriesChanged()
     }
 }
 
-
-QList<QAction *> BufferViewFilter::actions(const QModelIndex &index)
+QList<QAction*> BufferViewFilter::actions(const QModelIndex& index)
 {
     Q_UNUSED(index)
-    QList<QAction *> actionList;
+    QList<QAction*> actionList;
     actionList << &_enableEditMode;
     return actionList;
 }
 
+void BufferViewFilter::setFilterString(const QString string)
+{
+    beginResetModel();
+    _filterString = string;
+    endResetModel();
+    enableEditMode(!string.isEmpty());
+}
 
 void BufferViewFilter::enableEditMode(bool enable)
 {
@@ -159,7 +142,7 @@ void BufferViewFilter::enableEditMode(bool enable)
         return;
 
     if (enable == false) {
-        addBuffers(QList<BufferId>::fromSet(_toAdd));
+        addBuffers(_toAdd.values());
         QSet<BufferId>::const_iterator iter;
         for (iter = _toTempRemove.constBegin(); iter != _toTempRemove.constEnd(); ++iter) {
             if (config()->temporarilyRemovedBuffers().contains(*iter))
@@ -179,35 +162,33 @@ void BufferViewFilter::enableEditMode(bool enable)
     invalidate();
 }
 
-
-Qt::ItemFlags BufferViewFilter::flags(const QModelIndex &index) const
+Qt::ItemFlags BufferViewFilter::flags(const QModelIndex& index) const
 {
     QModelIndex source_index = mapToSource(index);
     Qt::ItemFlags flags = sourceModel()->flags(source_index);
     if (config()) {
-        NetworkModel::ItemType itemType = (NetworkModel::ItemType)sourceModel()->data(source_index, NetworkModel::ItemTypeRole).toInt();
         BufferInfo::Type bufferType = (BufferInfo::Type)sourceModel()->data(source_index, NetworkModel::BufferTypeRole).toInt();
-        if (source_index == QModelIndex() || itemType == NetworkModel::NetworkItemType) {
-            flags |= Qt::ItemIsDropEnabled;
-        }
-        else if (_editMode) {
-            flags |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
-        }
 
-        // prohibit dragging of most items. and most drop places
-        // only query to query is allowed for merging
-        if (bufferType != BufferInfo::QueryBuffer) {
-            ClientBufferViewConfig *clientConf = qobject_cast<ClientBufferViewConfig *>(config());
-            if (clientConf && clientConf->isLocked()) {
-                flags &= ~(Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled);
+        // We need Status Buffers to be a drop target, to allow for rearranging buffers.
+        // The Status Buffer "owns" the space between Channel/Query buffers in the tree.
+        // This DOES mean that it looks like you can merge a buffer into the Status buffer, but that is restricted in BufferView::dropEvent().
+        if (bufferType == BufferInfo::StatusBuffer) {
+            // But only if the layout isn't locked!
+            auto* clientConf = qobject_cast<ClientBufferViewConfig*>(config());
+            if (clientConf && !clientConf->isLocked()) {
+                flags |= Qt::ItemIsDropEnabled;
             }
         }
+
+        // If we're in Edit Mode, everything except Status Buffers should be hideable.
+        if (_editMode && bufferType != BufferInfo::StatusBuffer) {
+            flags |= Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
+        }
     }
     return flags;
 }
 
-
-bool BufferViewFilter::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
+bool BufferViewFilter::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
 {
     if (!config() || !NetworkModel::mimeContainsBufferList(data))
         return QSortFilterProxyModel::dropMimeData(data, action, row, column, parent);
@@ -217,7 +198,7 @@ bool BufferViewFilter::dropMimeData(const QMimeData *data, Qt::DropAction action
     if (sourceModel()->data(source_parent, NetworkModel::ItemTypeRole) == NetworkModel::NetworkItemType)
         droppedNetworkId = sourceModel()->data(source_parent, NetworkModel::NetworkIdRole).value<NetworkId>();
 
-    QList<QPair<NetworkId, BufferId> > bufferList = NetworkModel::mimeDataToBufferList(data);
+    QList<QPair<NetworkId, BufferId>> bufferList = NetworkModel::mimeDataToBufferList(data);
     BufferId bufferId;
     NetworkId networkId;
     int pos;
@@ -245,7 +226,7 @@ bool BufferViewFilter::dropMimeData(const QMimeData *data, Qt::DropAction action
             if (config()->bufferList().contains(bufferId) && !config()->sortAlphabetically()) {
                 if (config()->bufferList().indexOf(bufferId) < pos)
                     pos--;
-                ClientBufferViewConfig *clientConf = qobject_cast<ClientBufferViewConfig *>(config());
+                auto* clientConf = qobject_cast<ClientBufferViewConfig*>(config());
                 if (!clientConf || !clientConf->isLocked())
                     config()->requestMoveBuffer(bufferId, pos);
             }
@@ -260,15 +241,13 @@ bool BufferViewFilter::dropMimeData(const QMimeData *data, Qt::DropAction action
     return true;
 }
 
-
 void BufferViewFilter::sort(int column, Qt::SortOrder order)
 {
     _sortOrder = order;
     QSortFilterProxyModel::sort(column, order);
 }
 
-
-void BufferViewFilter::addBuffer(const BufferId &bufferId) const
+void BufferViewFilter::addBuffer(const BufferId& bufferId) const
 {
     if (!config() || config()->bufferList().contains(bufferId))
         return;
@@ -289,14 +268,13 @@ void BufferViewFilter::addBuffer(const BufferId &bufferId) const
     config()->requestAddBuffer(bufferId, pos);
 }
 
-
-void BufferViewFilter::addBuffers(const QList<BufferId> &bufferIds) const
+void BufferViewFilter::addBuffers(const QList<BufferId>& bufferIds) const
 {
     if (!config())
         return;
 
     QList<BufferId> bufferList = config()->bufferList();
-    foreach(BufferId bufferId, bufferIds) {
+    foreach (BufferId bufferId, bufferIds) {
         if (bufferList.contains(bufferId))
             continue;
 
@@ -318,8 +296,7 @@ void BufferViewFilter::addBuffers(const QList<BufferId> &bufferIds) const
     }
 }
 
-
-bool BufferViewFilter::filterAcceptBuffer(const QModelIndex &source_bufferIndex) const
+bool BufferViewFilter::filterAcceptBuffer(const QModelIndex& source_bufferIndex) const
 {
     // no config -> "all buffers" -> accept everything
     if (!config())
@@ -332,17 +309,19 @@ bool BufferViewFilter::filterAcceptBuffer(const QModelIndex &source_bufferIndex)
 
     if (!config()->bufferList().contains(bufferId) && !_editMode) {
         // add the buffer if...
-        if (config()->isInitialized()
-            && !config()->removedBuffers().contains(bufferId) // it hasn't been manually removed and either
-            && ((config()->addNewBuffersAutomatically() && !config()->temporarilyRemovedBuffers().contains(bufferId)) // is totally unknown to us (a new buffer)...
-                || (config()->temporarilyRemovedBuffers().contains(bufferId) && activityLevel > BufferInfo::OtherActivity))) { // or was just temporarily hidden and has a new message waiting for us.
+        if (config()->isInitialized() && !config()->removedBuffers().contains(bufferId)  // it hasn't been manually removed and either
+            && ((config()->addNewBuffersAutomatically()
+                 && !config()->temporarilyRemovedBuffers().contains(bufferId))  // is totally unknown to us (a new buffer)...
+                || (config()->temporarilyRemovedBuffers().contains(bufferId)
+                    && activityLevel > BufferInfo::OtherActivity))) {  // or was just temporarily hidden and has a new message waiting for us.
             addBuffer(bufferId);
         }
         // note: adding the buffer to the valid list does not temper with the following filters ("show only channels" and stuff)
         return false;
     }
 
-    if (config()->networkId().isValid() && config()->networkId() != sourceModel()->data(source_bufferIndex, NetworkModel::NetworkIdRole).value<NetworkId>())
+    if (config()->networkId().isValid()
+        && config()->networkId() != sourceModel()->data(source_bufferIndex, NetworkModel::NetworkIdRole).value<NetworkId>())
         return false;
 
     int allowedBufferTypes = config()->allowedBufferTypes();
@@ -352,16 +331,29 @@ bool BufferViewFilter::filterAcceptBuffer(const QModelIndex &source_bufferIndex)
     if (!(allowedBufferTypes & bufferType))
         return false;
 
-    if (bufferType & BufferInfo::QueryBuffer && !_showServerQueries && sourceModel()->data(source_bufferIndex, Qt::DisplayRole).toString().contains('.')) {
+    if (bufferType & BufferInfo::QueryBuffer && !_showServerQueries
+        && sourceModel()->data(source_bufferIndex, Qt::DisplayRole).toString().contains('.')) {
         return false;
     }
 
+    if (!_filterString.isEmpty()) {
+        const BufferInfo info = qvariant_cast<BufferInfo>(Client::bufferModel()->data(source_bufferIndex, NetworkModel::BufferInfoRole));
+        QString name = info.bufferName();
+        if (name.contains(_filterString, Qt::CaseInsensitive)) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
     // the following dynamic filters may not trigger if the buffer is currently selected.
     QModelIndex currentIndex = Client::bufferModel()->standardSelectionModel()->currentIndex();
     if (bufferId == Client::bufferModel()->data(currentIndex, NetworkModel::BufferIdRole).value<BufferId>())
         return true;
 
-    if (config()->hideInactiveBuffers() && !sourceModel()->data(source_bufferIndex, NetworkModel::ItemActiveRole).toBool() && activityLevel <= BufferInfo::OtherActivity)
+    if (config()->hideInactiveBuffers() && !sourceModel()->data(source_bufferIndex, NetworkModel::ItemActiveRole).toBool()
+        && activityLevel <= BufferInfo::OtherActivity)
         return false;
 
     if (config()->minimumActivity() > activityLevel)
@@ -370,8 +362,7 @@ bool BufferViewFilter::filterAcceptBuffer(const QModelIndex &source_bufferIndex)
     return true;
 }
 
-
-bool BufferViewFilter::filterAcceptNetwork(const QModelIndex &source_index) const
+bool BufferViewFilter::filterAcceptNetwork(const QModelIndex& source_index) const
 {
     if (!config())
         return true;
@@ -388,8 +379,7 @@ bool BufferViewFilter::filterAcceptNetwork(const QModelIndex &source_index) cons
     }
 }
 
-
-bool BufferViewFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+bool BufferViewFilter::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
 {
     QModelIndex child = sourceModel()->index(source_row, 0, source_parent);
 
@@ -409,8 +399,7 @@ bool BufferViewFilter::filterAcceptsRow(int source_row, const QModelIndex &sourc
     }
 }
 
-
-bool BufferViewFilter::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+bool BufferViewFilter::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
 {
     int leftItemType = sourceModel()->data(source_left, NetworkModel::ItemTypeRole).toInt();
     int rightItemType = sourceModel()->data(source_right, NetworkModel::ItemTypeRole).toInt();
@@ -425,11 +414,32 @@ bool BufferViewFilter::lessThan(const QModelIndex &source_left, const QModelInde
     }
 }
 
-
-bool BufferViewFilter::bufferLessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+bool BufferViewFilter::bufferLessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
 {
     BufferId leftBufferId = sourceModel()->data(source_left, NetworkModel::BufferIdRole).value<BufferId>();
     BufferId rightBufferId = sourceModel()->data(source_right, NetworkModel::BufferIdRole).value<BufferId>();
+    // If filtering, prioritize relevant items first
+    if (!_filterString.isEmpty()) {
+        // Get names of the buffers
+        QString leftBufferName = sourceModel()->data(source_left, NetworkModel::BufferInfoRole).value<BufferInfo>().bufferName();
+        QString rightBufferName = sourceModel()->data(source_right, NetworkModel::BufferInfoRole).value<BufferInfo>().bufferName();
+        // Check if there's any differences across types, most important first
+        if ((QString::compare(leftBufferName, _filterString, Qt::CaseInsensitive) == 0)
+            != (QString::compare(rightBufferName, _filterString, Qt::CaseInsensitive) == 0)) {
+            // One of these buffers is an exact match with the filter string, while the other isn't
+            // Prioritize whichever one is the exact match
+            // (If left buffer is exact, return true to set it as less than right)
+            return (QString::compare(leftBufferName, _filterString, Qt::CaseInsensitive) == 0);
+        }
+        else if (leftBufferName.startsWith(_filterString, Qt::CaseInsensitive)
+                 != rightBufferName.startsWith(_filterString, Qt::CaseInsensitive)) {
+            // One of these buffers starts with the filter string, while the other doesn't
+            // Prioritize whichever one starts with the filter string
+            // (If left buffer starts with, return true to set it as less than right)
+            return leftBufferName.startsWith(_filterString, Qt::CaseInsensitive);
+        }
+        // Otherwise, do the normal sorting (sorting happens within each priority bracket)
+    }
     if (config()) {
         int leftPos = config()->bufferList().indexOf(leftBufferId);
         int rightPos = config()->bufferList().indexOf(rightBufferId);
@@ -443,8 +453,7 @@ bool BufferViewFilter::bufferLessThan(const QModelIndex &source_left, const QMod
         return bufferIdLessThan(leftBufferId, rightBufferId);
 }
 
-
-bool BufferViewFilter::networkLessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+bool BufferViewFilter::networkLessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
 {
     // NetworkId leftNetworkId = sourceModel()->data(source_left, NetworkModel::NetworkIdRole).value<NetworkId>();
     // NetworkId rightNetworkId = sourceModel()->data(source_right, NetworkModel::NetworkIdRole).value<NetworkId>();
@@ -452,8 +461,7 @@ bool BufferViewFilter::networkLessThan(const QModelIndex &source_left, const QMo
     return QSortFilterProxyModel::lessThan(source_left, source_right);
 }
 
-
-QVariant BufferViewFilter::data(const QModelIndex &index, int role) const
+QVariant BufferViewFilter::data(const QModelIndex& index, int role) const
 {
     switch (role) {
     case Qt::FontRole:
@@ -470,8 +478,7 @@ QVariant BufferViewFilter::data(const QModelIndex &index, int role) const
     }
 }
 
-
-QVariant BufferViewFilter::checkedState(const QModelIndex &index) const
+QVariant BufferViewFilter::checkedState(const QModelIndex& index) const
 {
     if (!_editMode || !config())
         return QVariant();
@@ -499,8 +506,7 @@ QVariant BufferViewFilter::checkedState(const QModelIndex &index) const
     return Qt::Unchecked;
 }
 
-
-bool BufferViewFilter::setData(const QModelIndex &index, const QVariant &value, int role)
+bool BufferViewFilter::setData(const QModelIndex& index, const QVariant& value, int role)
 {
     switch (role) {
     case Qt::CheckStateRole:
@@ -510,8 +516,7 @@ bool BufferViewFilter::setData(const QModelIndex &index, const QVariant &value,
     }
 }
 
-
-bool BufferViewFilter::setCheckedState(const QModelIndex &index, Qt::CheckState state)
+bool BufferViewFilter::setCheckedState(const QModelIndex& index, Qt::CheckState state)
 {
     QModelIndex source_index = mapToSource(index);
     BufferId bufferId = sourceModel()->data(source_index, NetworkModel::BufferIdRole).value<BufferId>();
@@ -541,36 +546,7 @@ bool BufferViewFilter::setCheckedState(const QModelIndex &index, Qt::CheckState
     return true;
 }
 
-
-void BufferViewFilter::checkPreviousCurrentForRemoval(const QModelIndex &current, const QModelIndex &previous)
-{
-    Q_UNUSED(current);
-    if (previous.isValid())
-        QCoreApplication::postEvent(this, new CheckRemovalEvent(previous));
-}
-
-
-void BufferViewFilter::customEvent(QEvent *event)
-{
-    if (event->type() != QEvent::User)
-        return;
-
-    CheckRemovalEvent *removalEvent = static_cast<CheckRemovalEvent *>(event);
-    checkItemForRemoval(removalEvent->index);
-
-    event->accept();
-}
-
-
-void BufferViewFilter::checkItemsForRemoval(const QModelIndex &topLeft, const QModelIndex &bottomRight)
-{
-    QModelIndex source_topLeft = mapToSource(topLeft);
-    QModelIndex source_bottomRight = mapToSource(bottomRight);
-    emit _dataChanged(source_topLeft, source_bottomRight);
-}
-
-
-bool BufferViewFilter::bufferIdLessThan(const BufferId &left, const BufferId &right)
+bool BufferViewFilter::bufferIdLessThan(const BufferId& left, const BufferId& right)
 {
     Q_CHECK_PTR(Client::networkModel());
     if (!Client::networkModel())
@@ -585,5 +561,8 @@ bool BufferViewFilter::bufferIdLessThan(const BufferId &left, const BufferId &ri
     if (leftType != rightType)
         return leftType < rightType;
     else
-        return QString::compare(Client::networkModel()->data(leftIndex, Qt::DisplayRole).toString(), Client::networkModel()->data(rightIndex, Qt::DisplayRole).toString(), Qt::CaseInsensitive) < 0;
+        return QString::compare(Client::networkModel()->data(leftIndex, Qt::DisplayRole).toString(),
+                                Client::networkModel()->data(rightIndex, Qt::DisplayRole).toString(),
+                                Qt::CaseInsensitive)
+               < 0;
 }