diff --git a/libs/ui/kis_node_filter_proxy_model.cpp b/libs/ui/kis_node_filter_proxy_model.cpp index 4ce8118c79..e9218bd6bb 100644 --- a/libs/ui/kis_node_filter_proxy_model.cpp +++ b/libs/ui/kis_node_filter_proxy_model.cpp @@ -1,139 +1,164 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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) any later version. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_node_filter_proxy_model.h" #include #include "kis_node.h" #include "kis_node_model.h" #include "kis_node_manager.h" #include "kis_signal_compressor.h" #include "kis_image.h" struct KisNodeFilterProxyModel::Private { - Private() : nodeModel(0), activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) {} + Private() + : nodeModel(0), + activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) + {} KisNodeModel *nodeModel; KisNodeSP pendingActiveNode; KisNodeSP activeNode; QSet acceptedLabels; KisSignalCompressor activeNodeCompressor; + bool isUpdatingFilter = false; bool checkIndexAllowedRecursively(QModelIndex srcIndex); }; KisNodeFilterProxyModel::KisNodeFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_d(new Private) { connect(&m_d->activeNodeCompressor, SIGNAL(timeout()), SLOT(slotUpdateCurrentNodeFilter())); } KisNodeFilterProxyModel::~KisNodeFilterProxyModel() { } void KisNodeFilterProxyModel::setNodeModel(KisNodeModel *model) { m_d->nodeModel = model; setSourceModel(model); } +bool KisNodeFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (m_d->isUpdatingFilter && role == KisNodeModel::ActiveRole) { + return false; + } + + return QSortFilterProxyModel::setData(index, value, role); +} + bool KisNodeFilterProxyModel::Private::checkIndexAllowedRecursively(QModelIndex srcIndex) { KisNodeSP node = nodeModel->nodeFromIndex(srcIndex); if (node == activeNode || acceptedLabels.contains(node->colorLabelIndex())) { return true; } bool result = false; const int numChildren = srcIndex.model()->rowCount(srcIndex); for (int i = 0; i < numChildren; i++) { QModelIndex child = srcIndex.child(i, 0); if (checkIndexAllowedRecursively(child)) { result = true; break; } } return result; } bool KisNodeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return true; } const QModelIndex index = sourceModel()->index(source_row, 0, source_parent); KisNodeSP node = m_d->nodeModel->nodeFromIndex(index); return !node || m_d->acceptedLabels.isEmpty() || m_d->checkIndexAllowedRecursively(index); } KisNodeSP KisNodeFilterProxyModel::nodeFromIndex(const QModelIndex &index) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return 0; } QModelIndex srcIndex = mapToSource(index); return m_d->nodeModel->nodeFromIndex(srcIndex); } QModelIndex KisNodeFilterProxyModel::indexFromNode(KisNodeSP node) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return QModelIndex(); } QModelIndex srcIndex = m_d->nodeModel->indexFromNode(node); return mapFromSource(srcIndex); } void KisNodeFilterProxyModel::setAcceptedLabels(const QList &value) { m_d->acceptedLabels = QSet::fromList(value); invalidateFilter(); } void KisNodeFilterProxyModel::setActiveNode(KisNodeSP node) { + KIS_SAFE_ASSERT_RECOVER_RETURN(node); m_d->pendingActiveNode = node; if (node && indexFromNode(node).isValid()) { m_d->activeNodeCompressor.start(); } else { slotUpdateCurrentNodeFilter(); } } void KisNodeFilterProxyModel::slotUpdateCurrentNodeFilter() { m_d->activeNode = m_d->pendingActiveNode; + + /** + * During the filter update the model might emit "current changed" signals, + * which (in their turn) will issue setData(..., KisNodeModel::ActiveRole) + * call, leading to a double recursion. Which, obviously, crashes Krita. + * + * Right now, just blocking the KisNodeModel::ActiveRole call is the + * most obvious solution for the problem. + */ + m_d->isUpdatingFilter = true; invalidateFilter(); + m_d->isUpdatingFilter = false; } void KisNodeFilterProxyModel::unsetDummiesFacade() { m_d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0); m_d->pendingActiveNode = 0; m_d->activeNode = 0; } diff --git a/libs/ui/kis_node_filter_proxy_model.h b/libs/ui/kis_node_filter_proxy_model.h index efd325d70d..53e8516c91 100644 --- a/libs/ui/kis_node_filter_proxy_model.h +++ b/libs/ui/kis_node_filter_proxy_model.h @@ -1,60 +1,62 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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) any later version. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_NODE_FILTER_PROXY_MODEL_H #define __KIS_NODE_FILTER_PROXY_MODEL_H #include #include #include "kis_types.h" #include "kritaui_export.h" class KisNodeModel; class KisNodeDummy; class KisNodeManager; class KRITAUI_EXPORT KisNodeFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: KisNodeFilterProxyModel(QObject *parent); ~KisNodeFilterProxyModel(); void setNodeModel(KisNodeModel *model); + bool setData(const QModelIndex &index, const QVariant &value, int role); + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; void setAcceptedLabels(const QList &value); KisNodeSP nodeFromIndex(const QModelIndex &index) const; QModelIndex indexFromNode(KisNodeSP node) const; void unsetDummiesFacade(); public Q_SLOTS: void setActiveNode(KisNodeSP node); private Q_SLOTS: void slotUpdateCurrentNodeFilter(); private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_NODE_FILTER_PROXY_MODEL_H */