2 Copyright (C) 2010 Klarälvdalens Datakonsult AB,
3 a KDAB Group company, info@kdab.net,
4 author Stephen Kelly <stephen@kdab.com>
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Library General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
11 This library is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 #include "kmodelindexproxymapper.hxx"
24 #include <QAbstractItemModel>
25 #include <QWeakPointer>
26 #include <QAbstractProxyModel>
27 #include <QItemSelectionModel>
29 // #include "kdebug.h"
31 class KModelIndexProxyMapperPrivate
33 KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq)
34 : q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel)
39 void createProxyChain();
42 bool assertSelectionValid(const QItemSelection &selection) const {
43 foreach(const QItemSelectionRange &range, selection) {
44 if (!range.isValid()) {
45 // kDebug() << selection << m_leftModel << m_rightModel << m_proxyChainDown << m_proxyChainUp;
47 Q_ASSERT(range.isValid());
52 Q_DECLARE_PUBLIC(KModelIndexProxyMapper)
53 KModelIndexProxyMapper * const q_ptr;
55 QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainUp;
56 QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainDown;
58 QWeakPointer<const QAbstractItemModel> m_leftModel;
59 QWeakPointer<const QAbstractItemModel> m_rightModel;
65 The idea here is that <tt>this</tt> selection model and proxySelectionModel might be in different parts of the
66 proxy chain. We need to build up to two chains of proxy models to create mappings between them.
77 Need Proxy 1 and Proxy 2 in one chain, and Proxy 3 and 4 in the other.
93 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
94 already in the first chain.
96 Stephen Kelly, 30 March 2010.
99 void KModelIndexProxyMapperPrivate::createProxyChain()
101 QWeakPointer<const QAbstractItemModel> targetModel = m_rightModel;
106 if (m_leftModel == targetModel)
109 QList<QWeakPointer<const QAbstractProxyModel> > proxyChainDown;
110 QWeakPointer<const QAbstractProxyModel> selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(targetModel.data());
111 while( selectionTargetProxyModel )
113 proxyChainDown.prepend( selectionTargetProxyModel );
115 selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(selectionTargetProxyModel.data()->sourceModel());
117 if (selectionTargetProxyModel.data() == m_leftModel.data())
119 m_proxyChainDown = proxyChainDown;
124 QWeakPointer<const QAbstractItemModel> sourceModel = m_leftModel;
125 QWeakPointer<const QAbstractProxyModel> sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceModel.data());
127 while(sourceProxyModel)
129 m_proxyChainUp.append(sourceProxyModel);
131 sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceProxyModel.data()->sourceModel());
133 const int targetIndex = proxyChainDown.indexOf(sourceProxyModel);
135 if (targetIndex != -1)
137 m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size());
141 m_proxyChainDown = proxyChainDown;
142 Q_ASSERT(assertValid());
145 bool KModelIndexProxyMapperPrivate::assertValid()
147 if ( m_proxyChainDown.isEmpty())
149 Q_ASSERT( !m_proxyChainUp.isEmpty() );
150 Q_ASSERT( m_proxyChainUp.last().data()->sourceModel() == m_rightModel.data() );
152 else if ( m_proxyChainUp.isEmpty())
154 Q_ASSERT( !m_proxyChainDown.isEmpty() );
155 Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_leftModel.data() );
157 Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_proxyChainUp.last().data()->sourceModel() );
162 KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel* leftModel, const QAbstractItemModel* rightModel, QObject* parent)
163 : QObject(parent), d_ptr( new KModelIndexProxyMapperPrivate(leftModel, rightModel, this) )
168 KModelIndexProxyMapper::~KModelIndexProxyMapper()
173 QModelIndex KModelIndexProxyMapper::mapLeftToRight(const QModelIndex& index) const
175 const QItemSelection selection = mapSelectionLeftToRight(QItemSelection(index, index));
176 if (selection.isEmpty())
177 return QModelIndex();
179 return selection.indexes().first();
182 QModelIndex KModelIndexProxyMapper::mapRightToLeft(const QModelIndex& index) const
184 const QItemSelection selection = mapSelectionRightToLeft(QItemSelection(index, index));
185 if (selection.isEmpty())
186 return QModelIndex();
188 return selection.indexes().first();
191 QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelection& selection) const
193 Q_D(const KModelIndexProxyMapper);
195 if (selection.isEmpty())
196 return QItemSelection();
198 // if (selection.first().model() != d->m_leftModel.data())
199 // kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
200 Q_ASSERT(selection.first().model() == d->m_leftModel.data());
202 QItemSelection seekSelection = selection;
203 Q_ASSERT(d->assertSelectionValid(seekSelection));
204 QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
206 while (iUp.hasNext())
208 const QWeakPointer<const QAbstractProxyModel> proxy = iUp.next();
210 return QItemSelection();
211 seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
213 Q_ASSERT(d->assertSelectionValid(seekSelection));
216 QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
218 while (iDown.hasNext())
220 const QWeakPointer<const QAbstractProxyModel> proxy = iDown.next();
222 return QItemSelection();
223 seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
225 Q_ASSERT(d->assertSelectionValid(seekSelection));
228 Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_rightModel.data() ) || true );
229 return seekSelection;
232 QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelection& selection) const
234 Q_D(const KModelIndexProxyMapper);
236 if (selection.isEmpty())
237 return QItemSelection();
239 // if (selection.first().model() != d->m_rightModel.data())
240 // kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
241 Q_ASSERT(selection.first().model() == d->m_rightModel.data());
243 QItemSelection seekSelection = selection;
244 Q_ASSERT(d->assertSelectionValid(seekSelection));
245 QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
248 while (iDown.hasPrevious())
250 const QWeakPointer<const QAbstractProxyModel> proxy = iDown.previous();
252 return QItemSelection();
253 seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
255 Q_ASSERT(d->assertSelectionValid(seekSelection));
258 QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
261 while (iUp.hasPrevious())
263 const QWeakPointer<const QAbstractProxyModel> proxy = iUp.previous();
265 return QItemSelection();
266 seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
268 Q_ASSERT(d->assertSelectionValid(seekSelection));
271 Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_leftModel.data() ) || true );
272 return seekSelection;
275 // #include "kmodelindexproxymapper_moc.cxx"
276 // #include "kmodelindexproxymapper.moc"