Salome HOME
Modif Hexablock.py
[modules/hexablock.git] / src / HEXABLOCKGUI / kmodelindexproxymapper.cxx
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.hxx"
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.cxx"
310 // #include "kmodelindexproxymapper.moc"