mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
275 lines
8.4 KiB
C++
275 lines
8.4 KiB
C++
/*
|
|
Copyright (C) 2010 Klarälvdalens Datakonsult AB,
|
|
a KDAB Group company, info@kdab.net,
|
|
author Stephen Kelly <stephen@kdab.com>
|
|
|
|
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
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This library 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 Library General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301, USA.
|
|
*/
|
|
|
|
#include "kmodelindexproxymapper.h"
|
|
|
|
#include <QtCore/QAbstractItemModel>
|
|
#include <QtCore/qsharedpointer.h>
|
|
#include <QtGui/QAbstractProxyModel>
|
|
#include <QtGui/QItemSelectionModel>
|
|
|
|
#include "kdebug.h"
|
|
|
|
class KModelIndexProxyMapperPrivate
|
|
{
|
|
KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq)
|
|
: q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel)
|
|
{
|
|
createProxyChain();
|
|
}
|
|
|
|
void createProxyChain();
|
|
bool assertValid();
|
|
|
|
bool assertSelectionValid(const QItemSelection &selection) const {
|
|
foreach(const QItemSelectionRange &range, selection) {
|
|
if (!range.isValid()) {
|
|
kDebug() << selection << m_leftModel << m_rightModel << m_proxyChainDown << m_proxyChainUp;
|
|
}
|
|
Q_ASSERT(range.isValid());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Q_DECLARE_PUBLIC(KModelIndexProxyMapper)
|
|
KModelIndexProxyMapper * const q_ptr;
|
|
|
|
QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainUp;
|
|
QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainDown;
|
|
|
|
QWeakPointer<const QAbstractItemModel> m_leftModel;
|
|
QWeakPointer<const QAbstractItemModel> m_rightModel;
|
|
};
|
|
|
|
|
|
/*
|
|
|
|
The idea here is that <tt>this</tt> selection model and proxySelectionModel might be in different parts of the
|
|
proxy chain. We need to build up to two chains of proxy models to create mappings between them.
|
|
|
|
Example 1:
|
|
|
|
Root model
|
|
|
|
|
/ \
|
|
Proxy 1 Proxy 3
|
|
| |
|
|
Proxy 2 Proxy 4
|
|
|
|
Need Proxy 1 and Proxy 2 in one chain, and Proxy 3 and 4 in the other.
|
|
|
|
Example 2:
|
|
|
|
Root model
|
|
|
|
|
Proxy 1
|
|
|
|
|
Proxy 2
|
|
/ \
|
|
Proxy 3 Proxy 6
|
|
| |
|
|
Proxy 4 Proxy 7
|
|
|
|
|
Proxy 5
|
|
|
|
We first build the chain from 1 to 5, then start building the chain from 7 to 1. We stop when we find that proxy 2 is
|
|
already in the first chain.
|
|
|
|
Stephen Kelly, 30 March 2010.
|
|
*/
|
|
|
|
void KModelIndexProxyMapperPrivate::createProxyChain()
|
|
{
|
|
QWeakPointer<const QAbstractItemModel> targetModel = m_rightModel;
|
|
|
|
if (!targetModel)
|
|
return;
|
|
|
|
if (m_leftModel == targetModel)
|
|
return;
|
|
|
|
QList<QWeakPointer<const QAbstractProxyModel> > proxyChainDown;
|
|
QWeakPointer<const QAbstractProxyModel> selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(targetModel.data());
|
|
while( selectionTargetProxyModel )
|
|
{
|
|
proxyChainDown.prepend( selectionTargetProxyModel );
|
|
|
|
selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(selectionTargetProxyModel.data()->sourceModel());
|
|
|
|
if (selectionTargetProxyModel.data() == m_leftModel.data())
|
|
{
|
|
m_proxyChainDown = proxyChainDown;
|
|
return;
|
|
}
|
|
}
|
|
|
|
QWeakPointer<const QAbstractItemModel> sourceModel = m_leftModel;
|
|
QWeakPointer<const QAbstractProxyModel> sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceModel.data());
|
|
|
|
while(sourceProxyModel)
|
|
{
|
|
m_proxyChainUp.append(sourceProxyModel);
|
|
|
|
sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceProxyModel.data()->sourceModel());
|
|
|
|
const int targetIndex = proxyChainDown.indexOf(sourceProxyModel);
|
|
|
|
if (targetIndex != -1)
|
|
{
|
|
m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size());
|
|
return;
|
|
}
|
|
}
|
|
m_proxyChainDown = proxyChainDown;
|
|
Q_ASSERT(assertValid());
|
|
}
|
|
|
|
bool KModelIndexProxyMapperPrivate::assertValid()
|
|
{
|
|
if ( m_proxyChainDown.isEmpty())
|
|
{
|
|
Q_ASSERT( !m_proxyChainUp.isEmpty() );
|
|
Q_ASSERT( m_proxyChainUp.last().data()->sourceModel() == m_rightModel.data() );
|
|
}
|
|
else if ( m_proxyChainUp.isEmpty())
|
|
{
|
|
Q_ASSERT( !m_proxyChainDown.isEmpty() );
|
|
Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_leftModel.data() );
|
|
} else {
|
|
Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_proxyChainUp.last().data()->sourceModel() );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel* leftModel, const QAbstractItemModel* rightModel, QObject* parent)
|
|
: QObject(parent), d_ptr( new KModelIndexProxyMapperPrivate(leftModel, rightModel, this) )
|
|
{
|
|
|
|
}
|
|
|
|
KModelIndexProxyMapper::~KModelIndexProxyMapper()
|
|
{
|
|
delete d_ptr;
|
|
}
|
|
|
|
QModelIndex KModelIndexProxyMapper::mapLeftToRight(const QModelIndex& index) const
|
|
{
|
|
const QItemSelection selection = mapSelectionLeftToRight(QItemSelection(index, index));
|
|
if (selection.isEmpty())
|
|
return QModelIndex();
|
|
|
|
return selection.indexes().first();
|
|
}
|
|
|
|
QModelIndex KModelIndexProxyMapper::mapRightToLeft(const QModelIndex& index) const
|
|
{
|
|
const QItemSelection selection = mapSelectionRightToLeft(QItemSelection(index, index));
|
|
if (selection.isEmpty())
|
|
return QModelIndex();
|
|
|
|
return selection.indexes().first();
|
|
}
|
|
|
|
QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelection& selection) const
|
|
{
|
|
Q_D(const KModelIndexProxyMapper);
|
|
|
|
if (selection.isEmpty())
|
|
return QItemSelection();
|
|
|
|
if (selection.first().model() != d->m_leftModel.data())
|
|
kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
|
|
Q_ASSERT(selection.first().model() == d->m_leftModel.data());
|
|
|
|
QItemSelection seekSelection = selection;
|
|
Q_ASSERT(d->assertSelectionValid(seekSelection));
|
|
QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
|
|
|
|
while (iUp.hasNext())
|
|
{
|
|
const QWeakPointer<const QAbstractProxyModel> proxy = iUp.next();
|
|
if (!proxy.data())
|
|
return QItemSelection();
|
|
seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
|
|
|
|
Q_ASSERT(d->assertSelectionValid(seekSelection));
|
|
}
|
|
|
|
QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
|
|
|
|
while (iDown.hasNext())
|
|
{
|
|
const QWeakPointer<const QAbstractProxyModel> proxy = iDown.next();
|
|
if (!proxy.data())
|
|
return QItemSelection();
|
|
seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
|
|
|
|
Q_ASSERT(d->assertSelectionValid(seekSelection));
|
|
}
|
|
|
|
Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_rightModel.data() ) || true );
|
|
return seekSelection;
|
|
}
|
|
|
|
QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelection& selection) const
|
|
{
|
|
Q_D(const KModelIndexProxyMapper);
|
|
|
|
if (selection.isEmpty())
|
|
return QItemSelection();
|
|
|
|
if (selection.first().model() != d->m_rightModel.data())
|
|
kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
|
|
Q_ASSERT(selection.first().model() == d->m_rightModel.data());
|
|
|
|
QItemSelection seekSelection = selection;
|
|
Q_ASSERT(d->assertSelectionValid(seekSelection));
|
|
QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
|
|
|
|
iDown.toBack();
|
|
while (iDown.hasPrevious())
|
|
{
|
|
const QWeakPointer<const QAbstractProxyModel> proxy = iDown.previous();
|
|
if (!proxy.data())
|
|
return QItemSelection();
|
|
seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
|
|
|
|
Q_ASSERT(d->assertSelectionValid(seekSelection));
|
|
}
|
|
|
|
QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
|
|
|
|
iUp.toBack();
|
|
while (iUp.hasPrevious())
|
|
{
|
|
const QWeakPointer<const QAbstractProxyModel> proxy = iUp.previous();
|
|
if (!proxy.data())
|
|
return QItemSelection();
|
|
seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
|
|
|
|
Q_ASSERT(d->assertSelectionValid(seekSelection));
|
|
}
|
|
|
|
Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_leftModel.data() ) || true );
|
|
return seekSelection;
|
|
}
|
|
|
|
#include "moc_kmodelindexproxymapper.cpp"
|