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

KDEUI

kmodelindexproxymapper.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2010 Klarälvdalens Datakonsult AB,
00003         a KDAB Group company, info@kdab.net,
00004         author Stephen Kelly <stephen@kdab.com>
00005 
00006     This library is free software; you can redistribute it and/or modify it
00007     under the terms of the GNU Library General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or (at your
00009     option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful, but WITHOUT
00012     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00014     License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to the
00018     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00019     02110-1301, USA.
00020 */
00021 
00022 #include "kmodelindexproxymapper.h"
00023 
00024 #include <QtCore/QAbstractItemModel>
00025 #include <QtCore/QWeakPointer>
00026 #include <QtGui/QAbstractProxyModel>
00027 #include <QtGui/QItemSelectionModel>
00028 
00029 #include "kdebug.h"
00030 
00031 class KModelIndexProxyMapperPrivate
00032 {
00033   KModelIndexProxyMapperPrivate(const QAbstractItemModel *leftModel, const QAbstractItemModel *rightModel, KModelIndexProxyMapper *qq)
00034     : q_ptr(qq), m_leftModel(leftModel), m_rightModel(rightModel)
00035   {
00036     createProxyChain();
00037   }
00038 
00039   void createProxyChain();
00040   bool assertValid();
00041 
00042   bool assertSelectionValid(const QItemSelection &selection) const {
00043     foreach(const QItemSelectionRange &range, selection) {
00044       if (!range.isValid()) {
00045         kDebug() << selection << m_leftModel << m_rightModel << m_proxyChainDown << m_proxyChainUp;
00046       }
00047       Q_ASSERT(range.isValid());
00048     }
00049     return true;
00050   }
00051 
00052   Q_DECLARE_PUBLIC(KModelIndexProxyMapper)
00053   KModelIndexProxyMapper * const q_ptr;
00054 
00055   QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainUp;
00056   QList<QWeakPointer<const QAbstractProxyModel> > m_proxyChainDown;
00057 
00058   QWeakPointer<const QAbstractItemModel> m_leftModel;
00059   QWeakPointer<const QAbstractItemModel> m_rightModel;
00060 };
00061 
00062 
00063 /*
00064 
00065   The idea here is that <tt>this</tt> selection model and proxySelectionModel might be in different parts of the
00066   proxy chain. We need to build up to two chains of proxy models to create mappings between them.
00067 
00068   Example 1:
00069 
00070      Root model
00071           |
00072         /    \
00073     Proxy 1   Proxy 3
00074        |       |
00075     Proxy 2   Proxy 4
00076 
00077   Need Proxy 1 and Proxy 2 in one chain, and Proxy 3 and 4 in the other.
00078 
00079   Example 2:
00080 
00081      Root model
00082           |
00083         Proxy 1
00084           |
00085         Proxy 2
00086         /     \
00087     Proxy 3   Proxy 6
00088        |       |
00089     Proxy 4   Proxy 7
00090        |
00091     Proxy 5
00092 
00093   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
00094   already in the first chain.
00095 
00096   Stephen Kelly, 30 March 2010.
00097 */
00098 
00099 void KModelIndexProxyMapperPrivate::createProxyChain()
00100 {
00101   QWeakPointer<const QAbstractItemModel> targetModel = m_rightModel;
00102 
00103   if (!targetModel)
00104     return;
00105 
00106   if (m_leftModel == targetModel)
00107     return;
00108 
00109   QList<QWeakPointer<const QAbstractProxyModel> > proxyChainDown;
00110   QWeakPointer<const QAbstractProxyModel> selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(targetModel.data());
00111   while( selectionTargetProxyModel )
00112   {
00113     proxyChainDown.prepend( selectionTargetProxyModel );
00114 
00115     selectionTargetProxyModel = qobject_cast<const QAbstractProxyModel*>(selectionTargetProxyModel.data()->sourceModel());
00116 
00117     if (selectionTargetProxyModel.data() == m_leftModel.data())
00118     {
00119       m_proxyChainDown = proxyChainDown;
00120       return;
00121     }
00122   }
00123 
00124   QWeakPointer<const QAbstractItemModel> sourceModel = m_leftModel;
00125   QWeakPointer<const QAbstractProxyModel> sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceModel.data());
00126 
00127   while(sourceProxyModel)
00128   {
00129     m_proxyChainUp.append(sourceProxyModel);
00130 
00131     sourceProxyModel = qobject_cast<const QAbstractProxyModel*>(sourceProxyModel.data()->sourceModel());
00132 
00133     const int targetIndex = proxyChainDown.indexOf(sourceProxyModel);
00134 
00135     if (targetIndex != -1)
00136     {
00137       m_proxyChainDown = proxyChainDown.mid(targetIndex + 1, proxyChainDown.size());
00138       return;
00139     }
00140   }
00141   m_proxyChainDown = proxyChainDown;
00142   Q_ASSERT(assertValid());
00143 }
00144 
00145 bool KModelIndexProxyMapperPrivate::assertValid()
00146 {
00147   if ( m_proxyChainDown.isEmpty())
00148   {
00149     Q_ASSERT( !m_proxyChainUp.isEmpty() );
00150     Q_ASSERT( m_proxyChainUp.last().data()->sourceModel() == m_rightModel.data() );
00151   }
00152   else if ( m_proxyChainUp.isEmpty())
00153   {
00154     Q_ASSERT( !m_proxyChainDown.isEmpty() );
00155     Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_leftModel.data() );
00156   } else {
00157     Q_ASSERT( m_proxyChainDown.first().data()->sourceModel() == m_proxyChainUp.last().data()->sourceModel() );
00158   }
00159   return true;
00160 }
00161 
00162 KModelIndexProxyMapper::KModelIndexProxyMapper(const QAbstractItemModel* leftModel, const QAbstractItemModel* rightModel, QObject* parent)
00163   : QObject(parent), d_ptr( new KModelIndexProxyMapperPrivate(leftModel, rightModel, this) )
00164 {
00165 
00166 }
00167 
00168 KModelIndexProxyMapper::~KModelIndexProxyMapper()
00169 {
00170   delete d_ptr;
00171 }
00172 
00173 QModelIndex KModelIndexProxyMapper::mapLeftToRight(const QModelIndex& index) const
00174 {
00175   const QItemSelection selection = mapSelectionLeftToRight(QItemSelection(index, index));
00176   if (selection.isEmpty())
00177     return QModelIndex();
00178 
00179   return selection.indexes().first();
00180 }
00181 
00182 QModelIndex KModelIndexProxyMapper::mapRightToLeft(const QModelIndex& index) const
00183 {
00184   const QItemSelection selection = mapSelectionRightToLeft(QItemSelection(index, index));
00185   if (selection.isEmpty())
00186     return QModelIndex();
00187 
00188   return selection.indexes().first();
00189 }
00190 
00191 // QAbstractProxyModel::mapSelectionFromSource creates invalid ranges to we filter
00192 // those out manually in a loop. Hopefully fixed in Qt 4.7.2, so we ifdef it out.
00193 // http://qt.gitorious.org/qt/qt/merge_requests/2474
00194 // http://qt.gitorious.org/qt/qt/merge_requests/831
00195 #if QT_VERSION < 0x040702
00196 #define RANGE_FIX_HACK
00197 #endif
00198 
00199 #ifdef RANGE_FIX_HACK
00200 static QItemSelection removeInvalidRanges(const QItemSelection &selection)
00201 {
00202   QItemSelection result;
00203   Q_FOREACH(const QItemSelectionRange &range, selection)
00204   {
00205     if (!range.isValid())
00206       continue;
00207     result << range;
00208   }
00209   return result;
00210 }
00211 #endif
00212 
00213 QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelection& selection) const
00214 {
00215   Q_D(const KModelIndexProxyMapper);
00216 
00217   if (selection.isEmpty())
00218     return QItemSelection();
00219 
00220   if (selection.first().model() != d->m_leftModel.data())
00221     kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
00222   Q_ASSERT(selection.first().model() == d->m_leftModel.data());
00223 
00224   QItemSelection seekSelection = selection;
00225   Q_ASSERT(d->assertSelectionValid(seekSelection));
00226   QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
00227 
00228   while (iUp.hasNext())
00229   {
00230     const QWeakPointer<const QAbstractProxyModel> proxy = iUp.next();
00231     if (!proxy.data())
00232       return QItemSelection();
00233     seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
00234 
00235 #ifdef RANGE_FIX_HACK
00236     seekSelection = removeInvalidRanges(seekSelection);
00237 #endif
00238     Q_ASSERT(d->assertSelectionValid(seekSelection));
00239   }
00240 
00241   QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
00242 
00243   while (iDown.hasNext())
00244   {
00245     const QWeakPointer<const QAbstractProxyModel> proxy = iDown.next();
00246     if (!proxy.data())
00247       return QItemSelection();
00248     seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
00249 
00250 #ifdef RANGE_FIX_HACK
00251     seekSelection = removeInvalidRanges(seekSelection);
00252 #endif
00253     Q_ASSERT(d->assertSelectionValid(seekSelection));
00254   }
00255 
00256   Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_rightModel.data() ) || true );
00257   return seekSelection;
00258 }
00259 
00260 QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelection& selection) const
00261 {
00262   Q_D(const KModelIndexProxyMapper);
00263 
00264   if (selection.isEmpty())
00265     return QItemSelection();
00266 
00267   if (selection.first().model() != d->m_rightModel.data())
00268     kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
00269   Q_ASSERT(selection.first().model() == d->m_rightModel.data());
00270 
00271   QItemSelection seekSelection = selection;
00272   Q_ASSERT(d->assertSelectionValid(seekSelection));
00273   QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
00274 
00275   iDown.toBack();
00276   while (iDown.hasPrevious())
00277   {
00278     const QWeakPointer<const QAbstractProxyModel> proxy = iDown.previous();
00279     if (!proxy.data())
00280       return QItemSelection();
00281     seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
00282 
00283 #ifdef RANGE_FIX_HACK
00284     seekSelection = removeInvalidRanges(seekSelection);
00285 #endif
00286     Q_ASSERT(d->assertSelectionValid(seekSelection));
00287   }
00288 
00289   QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
00290 
00291   iUp.toBack();
00292   while (iUp.hasPrevious())
00293   {
00294     const QWeakPointer<const QAbstractProxyModel> proxy = iUp.previous();
00295     if (!proxy.data())
00296       return QItemSelection();
00297     seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
00298 
00299 #ifdef RANGE_FIX_HACK
00300     seekSelection = removeInvalidRanges(seekSelection);
00301 #endif
00302     Q_ASSERT(d->assertSelectionValid(seekSelection));
00303   }
00304 
00305   Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_leftModel.data() ) || true );
00306   return seekSelection;
00307 }
00308 
00309 #include "kmodelindexproxymapper.moc"

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal