• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • itemviews
kmodelindexproxymapper.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2010 Klarälvdalens Datakonsult AB,
3 a KDAB Group company, info@kdab.net,
4 author Stephen Kelly <stephen@kdab.com>
5
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.
10
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.
15
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
19 02110-1301, USA.
20*/
21
22#include "kmodelindexproxymapper.h"
23
24#include <QtCore/QAbstractItemModel>
25#include <QtCore/QWeakPointer>
26#include <QtGui/QAbstractProxyModel>
27#include <QtGui/QItemSelectionModel>
28
29#include "kdebug.h"
30
31class KModelIndexProxyMapperPrivate
32{
33 KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq)
34 : q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel)
35 {
36 createProxyChain();
37 }
38
39 void createProxyChain();
40 bool assertValid();
41
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;
46 }
47 Q_ASSERT(range.isValid());
48 }
49 return true;
50 }
51
52 Q_DECLARE_PUBLIC(KModelIndexProxyMapper)
53 KModelIndexProxyMapper * const q_ptr;
54
55 QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainUp;
56 QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainDown;
57
58 QWeakPointer<const QAbstractItemModel> m_leftModel;
59 QWeakPointer<const QAbstractItemModel> m_rightModel;
60};
61
62
63/*
64
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.
67
68 Example 1:
69
70 Root model
71 |
72 / \
73 Proxy 1 Proxy 3
74 | |
75 Proxy 2 Proxy 4
76
77 Need Proxy 1 and Proxy 2 in one chain, and Proxy 3 and 4 in the other.
78
79 Example 2:
80
81 Root model
82 |
83 Proxy 1
84 |
85 Proxy 2
86 / \
87 Proxy 3 Proxy 6
88 | |
89 Proxy 4 Proxy 7
90 |
91 Proxy 5
92
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.
95
96 Stephen Kelly, 30 March 2010.
97*/
98
99void KModelIndexProxyMapperPrivate::createProxyChain()
100{
101 QWeakPointer<const QAbstractItemModel> targetModel = m_rightModel;
102
103 if (!targetModel)
104 return;
105
106 if (m_leftModel == targetModel)
107 return;
108
109 QList<QWeakPointer<const QAbstractProxyModel> > proxyChainDown;
110 QWeakPointer<const QAbstractProxyModel> selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(targetModel.data());
111 while( selectionTargetProxyModel )
112 {
113 proxyChainDown.prepend( selectionTargetProxyModel );
114
115 selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(selectionTargetProxyModel.data()->sourceModel());
116
117 if (selectionTargetProxyModel.data() == m_leftModel.data())
118 {
119 m_proxyChainDown = proxyChainDown;
120 return;
121 }
122 }
123
124 QWeakPointer<const QAbstractItemModel> sourceModel = m_leftModel;
125 QWeakPointer<const QAbstractProxyModel> sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceModel.data());
126
127 while(sourceProxyModel)
128 {
129 m_proxyChainUp.append(sourceProxyModel);
130
131 sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceProxyModel.data()->sourceModel());
132
133 const int targetIndex = proxyChainDown.indexOf(sourceProxyModel);
134
135 if (targetIndex != -1)
136 {
137 m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size());
138 return;
139 }
140 }
141 m_proxyChainDown = proxyChainDown;
142 Q_ASSERT(assertValid());
143}
144
145bool KModelIndexProxyMapperPrivate::assertValid()
146{
147 if ( m_proxyChainDown.isEmpty())
148 {
149 Q_ASSERT( !m_proxyChainUp.isEmpty() );
150 Q_ASSERT( m_proxyChainUp.last().data()->sourceModel() == m_rightModel.data() );
151 }
152 else if ( m_proxyChainUp.isEmpty())
153 {
154 Q_ASSERT( !m_proxyChainDown.isEmpty() );
155 Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_leftModel.data() );
156 } else {
157 Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_proxyChainUp.last().data()->sourceModel() );
158 }
159 return true;
160}
161
162KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel* leftModel, const QAbstractItemModel* rightModel, QObject* parent)
163 : QObject(parent), d_ptr( new KModelIndexProxyMapperPrivate(leftModel, rightModel, this) )
164{
165
166}
167
168KModelIndexProxyMapper::~KModelIndexProxyMapper()
169{
170 delete d_ptr;
171}
172
173QModelIndex KModelIndexProxyMapper::mapLeftToRight(const QModelIndex& index) const
174{
175 const QItemSelection selection = mapSelectionLeftToRight(QItemSelection(index, index));
176 if (selection.isEmpty())
177 return QModelIndex();
178
179 return selection.indexes().first();
180}
181
182QModelIndex KModelIndexProxyMapper::mapRightToLeft(const QModelIndex& index) const
183{
184 const QItemSelection selection = mapSelectionRightToLeft(QItemSelection(index, index));
185 if (selection.isEmpty())
186 return QModelIndex();
187
188 return selection.indexes().first();
189}
190
191// QAbstractProxyModel::mapSelectionFromSource creates invalid ranges to we filter
192// those out manually in a loop. Hopefully fixed in Qt 4.7.2, so we ifdef it out.
193// http://qt.gitorious.org/qt/qt/merge_requests/2474
194// http://qt.gitorious.org/qt/qt/merge_requests/831
195#if QT_VERSION < 0x040702
196#define RANGE_FIX_HACK
197#endif
198
199#ifdef RANGE_FIX_HACK
200static QItemSelection removeInvalidRanges(const QItemSelection &selection)
201{
202 QItemSelection result;
203 Q_FOREACH(const QItemSelectionRange &range, selection)
204 {
205 if (!range.isValid())
206 continue;
207 result << range;
208 }
209 return result;
210}
211#endif
212
213QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelection& selection) const
214{
215 Q_D(const KModelIndexProxyMapper);
216
217 if (selection.isEmpty())
218 return QItemSelection();
219
220 if (selection.first().model() != d->m_leftModel.data())
221 kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
222 Q_ASSERT(selection.first().model() == d->m_leftModel.data());
223
224 QItemSelection seekSelection = selection;
225 Q_ASSERT(d->assertSelectionValid(seekSelection));
226 QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
227
228 while (iUp.hasNext())
229 {
230 const QWeakPointer<const QAbstractProxyModel> proxy = iUp.next();
231 if (!proxy.data())
232 return QItemSelection();
233 seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
234
235#ifdef RANGE_FIX_HACK
236 seekSelection = removeInvalidRanges(seekSelection);
237#endif
238 Q_ASSERT(d->assertSelectionValid(seekSelection));
239 }
240
241 QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
242
243 while (iDown.hasNext())
244 {
245 const QWeakPointer<const QAbstractProxyModel> proxy = iDown.next();
246 if (!proxy.data())
247 return QItemSelection();
248 seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
249
250#ifdef RANGE_FIX_HACK
251 seekSelection = removeInvalidRanges(seekSelection);
252#endif
253 Q_ASSERT(d->assertSelectionValid(seekSelection));
254 }
255
256 Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_rightModel.data() ) || true );
257 return seekSelection;
258}
259
260QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelection& selection) const
261{
262 Q_D(const KModelIndexProxyMapper);
263
264 if (selection.isEmpty())
265 return QItemSelection();
266
267 if (selection.first().model() != d->m_rightModel.data())
268 kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
269 Q_ASSERT(selection.first().model() == d->m_rightModel.data());
270
271 QItemSelection seekSelection = selection;
272 Q_ASSERT(d->assertSelectionValid(seekSelection));
273 QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
274
275 iDown.toBack();
276 while (iDown.hasPrevious())
277 {
278 const QWeakPointer<const QAbstractProxyModel> proxy = iDown.previous();
279 if (!proxy.data())
280 return QItemSelection();
281 seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
282
283#ifdef RANGE_FIX_HACK
284 seekSelection = removeInvalidRanges(seekSelection);
285#endif
286 Q_ASSERT(d->assertSelectionValid(seekSelection));
287 }
288
289 QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
290
291 iUp.toBack();
292 while (iUp.hasPrevious())
293 {
294 const QWeakPointer<const QAbstractProxyModel> proxy = iUp.previous();
295 if (!proxy.data())
296 return QItemSelection();
297 seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
298
299#ifdef RANGE_FIX_HACK
300 seekSelection = removeInvalidRanges(seekSelection);
301#endif
302 Q_ASSERT(d->assertSelectionValid(seekSelection));
303 }
304
305 Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_leftModel.data() ) || true );
306 return seekSelection;
307}
308
309#include "kmodelindexproxymapper.moc"
KModelIndexProxyMapper
This class facilitates easy mapping of indexes and selections through proxy models.
Definition: kmodelindexproxymapper.h:80
KModelIndexProxyMapper::mapSelectionLeftToRight
QItemSelection mapSelectionLeftToRight(const QItemSelection &selection) const
Maps the selection from the left model to the right model.
Definition: kmodelindexproxymapper.cpp:213
KModelIndexProxyMapper::KModelIndexProxyMapper
KModelIndexProxyMapper(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, QObject *parent=0)
Constructor.
Definition: kmodelindexproxymapper.cpp:162
KModelIndexProxyMapper::mapLeftToRight
QModelIndex mapLeftToRight(const QModelIndex &index) const
Maps the index from the left model to the right model.
Definition: kmodelindexproxymapper.cpp:173
KModelIndexProxyMapper::mapSelectionRightToLeft
QItemSelection mapSelectionRightToLeft(const QItemSelection &selection) const
Maps the selection from the right model to the left model.
Definition: kmodelindexproxymapper.cpp:260
KModelIndexProxyMapper::~KModelIndexProxyMapper
~KModelIndexProxyMapper()
Definition: kmodelindexproxymapper.cpp:168
KModelIndexProxyMapper::mapRightToLeft
QModelIndex mapRightToLeft(const QModelIndex &index) const
Maps the index from the right model to the left model.
Definition: kmodelindexproxymapper.cpp:182
QAbstractItemModel
QList
QObject
kDebug
#define kDebug
kdebug.h
removeInvalidRanges
static QItemSelection removeInvalidRanges(const QItemSelection &selection)
Definition: kmodelindexproxymapper.cpp:200
kmodelindexproxymapper.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal