Salome HOME
Add license
[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 <QAbstractItemModel>
25 #include <QWeakPointer>
26 #include <QAbstractProxyModel>
27 #include <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 QItemSelection KModelIndexProxyMapper::mapSelectionLeftToRight(const QItemSelection& selection) const
192 {
193   Q_D(const KModelIndexProxyMapper);
194
195   if (selection.isEmpty())
196     return QItemSelection();
197
198 //   if (selection.first().model() != d->m_leftModel.data())
199 //     kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
200   Q_ASSERT(selection.first().model() == d->m_leftModel.data());
201
202   QItemSelection seekSelection = selection;
203   Q_ASSERT(d->assertSelectionValid(seekSelection));
204   QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
205
206   while (iUp.hasNext())
207   {
208     const QWeakPointer<const QAbstractProxyModel> proxy = iUp.next();
209     if (!proxy.data())
210       return QItemSelection();
211     seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
212
213     Q_ASSERT(d->assertSelectionValid(seekSelection));
214   }
215
216   QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
217
218   while (iDown.hasNext())
219   {
220     const QWeakPointer<const QAbstractProxyModel> proxy = iDown.next();
221     if (!proxy.data())
222       return QItemSelection();
223     seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
224
225     Q_ASSERT(d->assertSelectionValid(seekSelection));
226   }
227
228   Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_rightModel.data() ) || true );
229   return seekSelection;
230 }
231
232 QItemSelection KModelIndexProxyMapper::mapSelectionRightToLeft(const QItemSelection& selection) const
233 {
234   Q_D(const KModelIndexProxyMapper);
235
236   if (selection.isEmpty())
237     return QItemSelection();
238
239 //   if (selection.first().model() != d->m_rightModel.data())
240 //     kDebug() << "FAIL" << selection.first().model() << d->m_leftModel.data() << d->m_rightModel.data();
241   Q_ASSERT(selection.first().model() == d->m_rightModel.data());
242
243   QItemSelection seekSelection = selection;
244   Q_ASSERT(d->assertSelectionValid(seekSelection));
245   QListIterator<QWeakPointer<const QAbstractProxyModel> > iDown(d->m_proxyChainDown);
246
247   iDown.toBack();
248   while (iDown.hasPrevious())
249   {
250     const QWeakPointer<const QAbstractProxyModel> proxy = iDown.previous();
251     if (!proxy.data())
252       return QItemSelection();
253     seekSelection = proxy.data()->mapSelectionToSource(seekSelection);
254
255     Q_ASSERT(d->assertSelectionValid(seekSelection));
256   }
257
258   QListIterator<QWeakPointer<const QAbstractProxyModel> > iUp(d->m_proxyChainUp);
259
260   iUp.toBack();
261   while (iUp.hasPrevious())
262   {
263     const QWeakPointer<const QAbstractProxyModel> proxy = iUp.previous();
264     if (!proxy.data())
265       return QItemSelection();
266     seekSelection = proxy.data()->mapSelectionFromSource(seekSelection);
267
268     Q_ASSERT(d->assertSelectionValid(seekSelection));
269   }
270
271   Q_ASSERT( ( !seekSelection.isEmpty() && seekSelection.first().model() == d->m_leftModel.data() ) || true );
272   return seekSelection;
273 }
274
275 // #include "kmodelindexproxymapper_moc.cxx"
276 // #include "kmodelindexproxymapper.moc"