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"
KDE 4.6 API Reference