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

KDEUI

  • sources
  • kde-4.14
  • kdelibs
  • 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 
31 class 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 
99 void 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 
145 bool 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 
162 KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel* leftModel, const QAbstractItemModel* rightModel, QObject* parent)
163  : QObject(parent), d_ptr( new KModelIndexProxyMapperPrivate(leftModel, rightModel, this) )
164 {
165 
166 }
167 
168 KModelIndexProxyMapper::~KModelIndexProxyMapper()
169 {
170  delete d_ptr;
171 }
172 
173 QModelIndex 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 
182 QModelIndex 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
200 static 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 
213 QItemSelection 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 
260 QItemSelection 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"
QItemSelection::indexes
QModelIndexList indexes() const
QModelIndex
KModelIndexProxyMapper::~KModelIndexProxyMapper
~KModelIndexProxyMapper()
Definition: kmodelindexproxymapper.cpp:168
QItemSelectionRange
KModelIndexProxyMapper::mapLeftToRight
QModelIndex mapLeftToRight(const QModelIndex &index) const
Maps the index from the left model to the right model.
Definition: kmodelindexproxymapper.cpp:173
kdebug.h
QListIterator::next
const T & next()
QAbstractProxyModel
QListIterator::previous
const T & previous()
QWeakPointer::data
T * data() const
kmodelindexproxymapper.h
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QList::size
int size() const
QList::indexOf
int indexOf(const T &value, int from) const
QObject
QList::isEmpty
bool isEmpty() const
QListIterator::toBack
void toBack()
QList::first
T & first()
QList
QItemSelection
QListIterator::hasPrevious
bool hasPrevious() const
KModelIndexProxyMapper
This class facilitates easy mapping of indexes and selections through proxy models.
Definition: kmodelindexproxymapper.h:79
KModelIndexProxyMapper::mapRightToLeft
QModelIndex mapRightToLeft(const QModelIndex &index) const
Maps the index from the right model to the left model.
Definition: kmodelindexproxymapper.cpp:182
QList::mid
QList< T > mid(int pos, int length) const
QAbstractItemModel
QWeakPointer
KModelIndexProxyMapper::mapSelectionRightToLeft
QItemSelection mapSelectionRightToLeft(const QItemSelection &selection) const
Maps the selection from the right model to the left model.
Definition: kmodelindexproxymapper.cpp:260
removeInvalidRanges
static QItemSelection removeInvalidRanges(const QItemSelection &selection)
Definition: kmodelindexproxymapper.cpp:200
QList::prepend
void prepend(const T &value)
KModelIndexProxyMapper::mapSelectionLeftToRight
QItemSelection mapSelectionLeftToRight(const QItemSelection &selection) const
Maps the selection from the left model to the right model.
Definition: kmodelindexproxymapper.cpp:213
QListIterator
QItemSelectionRange::isValid
bool isValid() const
KModelIndexProxyMapper::KModelIndexProxyMapper
KModelIndexProxyMapper(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, QObject *parent=0)
Constructor.
Definition: kmodelindexproxymapper.cpp:162
QListIterator::hasNext
bool hasNext() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:23:59 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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

Search



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