Add a property indicating whether the models form a connected chain.

Replace existing asserts with a query and notification API.  This makes
the class more suitable for use in QML.
wilder
Stephen Kelly 10 years ago
parent bb36968396
commit a0e309b403
  1. 103
      autotests/kmodelindexproxymappertest.cpp
  2. 49
      src/kmodelindexproxymapper.cpp
  3. 17
      src/kmodelindexproxymapper.h

@ -35,6 +35,11 @@ private Q_SLOTS:
void testIndexMapping(); void testIndexMapping();
void testSelectionMapping(); void testSelectionMapping();
void selfConnection();
void connectedChangedSimple();
void connectedChangedComplex();
void crossWires();
void isConnected();
private: private:
QStringListModel baseModel; QStringListModel baseModel;
@ -101,5 +106,103 @@ void ModelIndexProxyMapperTest::testSelectionMapping()
QCOMPARE(mapper.mapSelectionRightToLeft(rightSel), leftSel); QCOMPARE(mapper.mapSelectionRightToLeft(rightSel), leftSel);
} }
void ModelIndexProxyMapperTest::selfConnection()
{
KModelIndexProxyMapper mapper(&baseModel, &baseModel);
QVERIFY(mapper.isConnected());
auto idx = baseModel.index(0, 0);
QVERIFY(idx.isValid());
QCOMPARE(mapper.mapLeftToRight(idx), idx);
}
void ModelIndexProxyMapperTest::connectedChangedSimple()
{
QIdentityProxyModel proxy1;
Q_SET_OBJECT_NAME(proxy1);
KModelIndexProxyMapper mapper(&proxy1, &baseModel);
QSignalSpy spy(&mapper, SIGNAL(isConnectedChanged()));
QVERIFY(!mapper.isConnected());
proxy1.setSourceModel(&baseModel);
QVERIFY(mapper.isConnected());
QCOMPARE(spy.count(), 1);
}
void ModelIndexProxyMapperTest::connectedChangedComplex()
{
KModelIndexProxyMapper mapper(&proxy_left3, &proxy_right4);
QSignalSpy spy(&mapper, SIGNAL(isConnectedChanged()));
QVERIFY(mapper.isConnected());
proxy_right2.setSourceModel(Q_NULLPTR);
QVERIFY(!mapper.isConnected());
QCOMPARE(spy.count(), 1);
proxy_right2.setSourceModel(&proxy_right1);
QVERIFY(mapper.isConnected());
QCOMPARE(spy.count(), 2);
auto leftIdx = proxy_left3.index(0, 0);
QVERIFY(leftIdx.isValid());
auto rightIdx = mapper.mapLeftToRight(leftIdx);
QVERIFY(rightIdx.isValid());
QCOMPARE(mapper.mapRightToLeft(rightIdx), leftIdx);
QIdentityProxyModel replacement_right1;
replacement_right1.setSourceModel(&proxy_right1);
proxy_right2.setSourceModel(&replacement_right1);
QVERIFY(mapper.isConnected());
QCOMPARE(spy.count(), 2);
}
void ModelIndexProxyMapperTest::crossWires()
{
KModelIndexProxyMapper mapper(&proxy_left3, &proxy_right4);
QSignalSpy spy(&mapper, SIGNAL(isConnectedChanged()));
QVERIFY(mapper.isConnected());
proxy_left3.setSourceModel(&proxy_right3);
QVERIFY(mapper.isConnected());
QCOMPARE(spy.count(), 0);
{
auto leftIdx = proxy_left3.index(0, 0);
auto rightIdx = proxy_right4.index(0, 0);
QCOMPARE(mapper.mapLeftToRight(leftIdx), rightIdx);
}
proxy_right4.setSourceModel(&proxy_left2);
QVERIFY(mapper.isConnected());
QCOMPARE(spy.count(), 0);
{
auto leftIdx = proxy_left3.index(0, 0);
auto rightIdx = proxy_right4.index(0, 0);
QCOMPARE(mapper.mapLeftToRight(leftIdx), rightIdx);
}
}
void ModelIndexProxyMapperTest::isConnected()
{
KModelIndexProxyMapper mapper1(&proxy_left1, &baseModel);
QVERIFY(mapper1.isConnected());
KModelIndexProxyMapper mapper2(&baseModel, &proxy_left1);
QVERIFY(mapper2.isConnected());
}
QTEST_MAIN(ModelIndexProxyMapperTest) QTEST_MAIN(ModelIndexProxyMapperTest)
#include "kmodelindexproxymappertest.moc" #include "kmodelindexproxymappertest.moc"

@ -2,6 +2,8 @@
Copyright (C) 2010 Klarälvdalens Datakonsult AB, Copyright (C) 2010 Klarälvdalens Datakonsult AB,
a KDAB Group company, info@kdab.net, a KDAB Group company, info@kdab.net,
author Stephen Kelly <stephen@kdab.com> author Stephen Kelly <stephen@kdab.com>
Copyright (c) 2016 Ableton AG <info@ableton.com>
Author Stephen Kelly <stephen.kelly@ableton.com>
This library is free software; you can redistribute it and/or modify it This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by under the terms of the GNU Library General Public License as published by
@ -30,13 +32,14 @@
class KModelIndexProxyMapperPrivate class KModelIndexProxyMapperPrivate
{ {
KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq) KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq)
: q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel) : q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel), mConnected(false)
{ {
createProxyChain(); createProxyChain();
} }
void createProxyChain(); void createProxyChain();
bool assertValid(); void checkConnected();
void setConnected(bool connected);
bool assertSelectionValid(const QItemSelection &selection) const bool assertSelectionValid(const QItemSelection &selection) const
{ {
@ -57,6 +60,8 @@ class KModelIndexProxyMapperPrivate
QPointer<const QAbstractItemModel> m_leftModel; QPointer<const QAbstractItemModel> m_leftModel;
QPointer<const QAbstractItemModel> m_rightModel; QPointer<const QAbstractItemModel> m_rightModel;
bool mConnected;
}; };
/* /*
@ -97,18 +102,28 @@ class KModelIndexProxyMapperPrivate
void KModelIndexProxyMapperPrivate::createProxyChain() void KModelIndexProxyMapperPrivate::createProxyChain()
{ {
Q_FOREACH (auto p, m_proxyChainUp) {
p->disconnect(q_ptr);
}
Q_FOREACH (auto p, m_proxyChainDown) {
p->disconnect(q_ptr);
}
m_proxyChainUp.clear();
m_proxyChainDown.clear();
QPointer<const QAbstractItemModel> targetModel = m_rightModel; QPointer<const QAbstractItemModel> targetModel = m_rightModel;
QList<QPointer<const QAbstractProxyModel> > proxyChainDown; QList<QPointer<const QAbstractProxyModel> > proxyChainDown;
QPointer<const QAbstractProxyModel> selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel *>(targetModel); QPointer<const QAbstractProxyModel> selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel *>(targetModel);
while (selectionTargetProxyModel) { while (selectionTargetProxyModel) {
proxyChainDown.prepend(selectionTargetProxyModel); proxyChainDown.prepend(selectionTargetProxyModel);
QObject::connect(selectionTargetProxyModel, &QAbstractProxyModel::sourceModelChanged, q_ptr,
[this]{ createProxyChain(); });
selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel *>(selectionTargetProxyModel->sourceModel()); selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel *>(selectionTargetProxyModel->sourceModel());
if (selectionTargetProxyModel == m_leftModel) { if (selectionTargetProxyModel == m_leftModel) {
m_proxyChainDown = proxyChainDown; m_proxyChainDown = proxyChainDown;
Q_ASSERT(assertValid()); checkConnected();
return; return;
} }
} }
@ -118,6 +133,8 @@ void KModelIndexProxyMapperPrivate::createProxyChain()
while (sourceProxyModel) { while (sourceProxyModel) {
m_proxyChainUp.append(sourceProxyModel); m_proxyChainUp.append(sourceProxyModel);
QObject::connect(sourceProxyModel, &QAbstractProxyModel::sourceModelChanged, q_ptr,
[this]{ createProxyChain(); });
sourceProxyModel = qobject_cast<const QAbstractProxyModel *>(sourceProxyModel->sourceModel()); sourceProxyModel = qobject_cast<const QAbstractProxyModel *>(sourceProxyModel->sourceModel());
@ -125,19 +142,28 @@ void KModelIndexProxyMapperPrivate::createProxyChain()
if (targetIndex != -1) { if (targetIndex != -1) {
m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size()); m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size());
Q_ASSERT(assertValid()); checkConnected();
return; return;
} }
} }
m_proxyChainDown = proxyChainDown; m_proxyChainDown = proxyChainDown;
Q_ASSERT(assertValid()); checkConnected();
} }
bool KModelIndexProxyMapperPrivate::assertValid() void KModelIndexProxyMapperPrivate::checkConnected()
{ {
auto konamiRight = m_proxyChainUp.isEmpty() ? m_leftModel : m_proxyChainUp.last()->sourceModel(); auto konamiRight = m_proxyChainUp.isEmpty() ? m_leftModel : m_proxyChainUp.last()->sourceModel();
auto konamiLeft = m_proxyChainDown.isEmpty() ? m_rightModel : m_proxyChainDown.first()->sourceModel(); auto konamiLeft = m_proxyChainDown.isEmpty() ? m_rightModel : m_proxyChainDown.first()->sourceModel();
return konamiLeft && (konamiLeft == konamiRight); setConnected(konamiLeft && (konamiLeft == konamiRight));
}
void KModelIndexProxyMapperPrivate::setConnected(bool connected)
{
if (mConnected != connected) {
Q_Q(KModelIndexProxyMapper);
mConnected = connected;
Q_EMIT q->isConnectedChanged();
}
} }
KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, QObject *parent) KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, QObject *parent)
@ -197,7 +223,7 @@ QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelect
{ {
Q_D(const KModelIndexProxyMapper); Q_D(const KModelIndexProxyMapper);
if (selection.isEmpty()) { if (selection.isEmpty() || !d->mConnected) {
return QItemSelection(); return QItemSelection();
} }
@ -251,7 +277,7 @@ QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelect
{ {
Q_D(const KModelIndexProxyMapper); Q_D(const KModelIndexProxyMapper);
if (selection.isEmpty()) { if (selection.isEmpty() || !d->mConnected) {
return QItemSelection(); return QItemSelection();
} }
@ -298,3 +324,8 @@ QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelect
return seekSelection; return seekSelection;
} }
bool KModelIndexProxyMapper::isConnected() const
{
Q_D(const KModelIndexProxyMapper);
return d->mConnected;
}

@ -2,6 +2,8 @@
Copyright (C) 2010 Klarälvdalens Datakonsult AB, Copyright (C) 2010 Klarälvdalens Datakonsult AB,
a KDAB Group company, info@kdab.net, a KDAB Group company, info@kdab.net,
author Stephen Kelly <stephen@kdab.com> author Stephen Kelly <stephen@kdab.com>
Copyright (c) 2016 Ableton AG <info@ableton.com>
Author Stephen Kelly <stephen.kelly@ableton.com>
This library is free software; you can redistribute it and/or modify it This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by under the terms of the GNU Library General Public License as published by
@ -73,12 +75,22 @@ class KModelIndexProxyMapperPrivate;
* Proxy 2 Proxy 4 * Proxy 2 Proxy 4
* @endverbatim * @endverbatim
* *
* The isConnected property indicates whether there is a
* path from the left side to the right side.
*
* @author Stephen Kelly <steveire@gmail.com> * @author Stephen Kelly <steveire@gmail.com>
* *
*/ */
class KITEMMODELS_EXPORT KModelIndexProxyMapper : public QObject class KITEMMODELS_EXPORT KModelIndexProxyMapper : public QObject
{ {
Q_OBJECT Q_OBJECT
/**
* Indicates whether there is a chain that can be followed from leftModel to rightModel.
*
* This value can change if the sourceModel of an intermediate proxy is changed.
*/
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged)
public: public:
/** /**
* Constructor * Constructor
@ -107,6 +119,11 @@ public:
*/ */
QItemSelection mapSelectionRightToLeft(const QItemSelection &selection) const; QItemSelection mapSelectionRightToLeft(const QItemSelection &selection) const;
bool isConnected() const;
Q_SIGNALS:
void isConnectedChanged();
private: private:
//@cond PRIVATE //@cond PRIVATE
Q_DECLARE_PRIVATE(KModelIndexProxyMapper) Q_DECLARE_PRIVATE(KModelIndexProxyMapper)

Loading…
Cancel
Save