|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbWidgets module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include "hbtreemodeliterator_p.h" |
|
27 #include "hbtreemodeliterator_p_p.h" |
|
28 |
|
29 HbTreeModelIterator::HbTreeModelIterator(QAbstractItemModel *model, |
|
30 QModelIndex rootIndex, bool useCache) |
|
31 : HbModelIterator(*new HbTreeModelIteratorPrivate()) |
|
32 { |
|
33 Q_D(HbTreeModelIterator); |
|
34 d->mUseCache = useCache; |
|
35 setModel(model, rootIndex); |
|
36 } |
|
37 |
|
38 HbTreeModelIterator::~HbTreeModelIterator() |
|
39 { |
|
40 } |
|
41 |
|
42 /*! |
|
43 \reimp |
|
44 |
|
45 Children of collapsed parents are not taken into account. |
|
46 */ |
|
47 int HbTreeModelIterator::indexCount(const QModelIndex &parent) const |
|
48 { |
|
49 Q_D(const HbTreeModelIterator); |
|
50 int itemsCount = 0; |
|
51 if (d->mModel) { |
|
52 QModelIndex parentIndex = parent; |
|
53 if (!parentIndex.isValid()) { |
|
54 parentIndex = d->mRootIndex; |
|
55 } |
|
56 if (d->isInCountCache(parentIndex)) { |
|
57 return d->mCachedCount.count; |
|
58 } |
|
59 if (parentIndex != d->mRootIndex) { |
|
60 itemsCount = d->childIndexCount(parentIndex); |
|
61 } else { |
|
62 int rowsCount = d->mModel->rowCount(parentIndex); |
|
63 itemsCount += rowsCount; |
|
64 for (int i = 0; i < rowsCount; i++) { |
|
65 itemsCount += d->childIndexCount(d->mModel->index(i, 0, parentIndex)); |
|
66 } |
|
67 } |
|
68 d->mCachedCount.count = itemsCount; |
|
69 d->mCachedCount.index = parentIndex; |
|
70 } |
|
71 return itemsCount; |
|
72 } |
|
73 |
|
74 /*! |
|
75 \reimp |
|
76 Position depends on item state - expanded/colapsed |
|
77 */ |
|
78 int HbTreeModelIterator::indexPosition(const QModelIndex &index) const |
|
79 { |
|
80 Q_D(const HbTreeModelIterator); |
|
81 int result = -1; |
|
82 if (d->mModel) { |
|
83 if (d->isInPositionCache(index)) { |
|
84 return d->mCachedPosition.count; |
|
85 } else if (d->mCachedPosition.index == d->mRootIndex |
|
86 || lessThan(d->mCachedPosition.index, index)) { //Cache has been reset, start from the beginning |
|
87 result = d->searchForward(index); |
|
88 } else { |
|
89 result = d->searchBackward(index); |
|
90 } |
|
91 } |
|
92 return result; |
|
93 } |
|
94 |
|
95 /*! |
|
96 \reimp |
|
97 Index is calculated from position and it depends on item state - expanded/colapsed |
|
98 Very slow - need to interate throught whole model in worst case! |
|
99 */ |
|
100 QModelIndex HbTreeModelIterator::index(int pos, const QModelIndex &parent) const |
|
101 { |
|
102 Q_D(const HbTreeModelIterator); |
|
103 QModelIndex index; |
|
104 QModelIndex parentIndex = parent; |
|
105 if (!parentIndex.isValid()) { |
|
106 parentIndex = d->mRootIndex; |
|
107 } |
|
108 if (pos < 0) { |
|
109 int count = indexCount(parentIndex); |
|
110 pos = count - pos; |
|
111 if (pos > count) { |
|
112 return index; |
|
113 } |
|
114 } |
|
115 if (d->isInCountCache(parentIndex) |
|
116 && d->mCachedCount.count >= 0 |
|
117 && (d->mCachedCount.count/2) < pos) { |
|
118 // is this reasonable? |
|
119 // last go through whole branch to find last |
|
120 // faster than nextIndex but... |
|
121 int i = d->mCachedCount.count - pos; |
|
122 if (i > 0) { |
|
123 index = d->last(parentIndex); |
|
124 --i; |
|
125 } |
|
126 while (i > 0) { |
|
127 index = previousIndex(index); |
|
128 if (!index.isValid()) break; |
|
129 --i; |
|
130 } |
|
131 } else { |
|
132 int i = 0; |
|
133 index = d->first(parentIndex); |
|
134 while (i < pos) { |
|
135 index = nextIndex(index); |
|
136 if (index.parent() != parentIndex) { |
|
137 // next index must be in the same branch as parent! |
|
138 QModelIndex temp = index.parent(); |
|
139 while (temp.isValid() |
|
140 && temp.parent() != parentIndex) { |
|
141 temp = temp.parent(); |
|
142 } |
|
143 if (!temp.isValid()) { |
|
144 index = temp; |
|
145 break; |
|
146 } |
|
147 } |
|
148 if (!index.isValid()) { |
|
149 break; |
|
150 } |
|
151 ++i; |
|
152 } |
|
153 } |
|
154 return index; |
|
155 } |
|
156 |
|
157 QModelIndex HbTreeModelIterator::child(int pos, const QModelIndex &parent) const |
|
158 { |
|
159 Q_D(const HbTreeModelIterator); |
|
160 if (d->mModel) { |
|
161 return d->mModel->index(pos, 0, parent); |
|
162 } |
|
163 return QModelIndex(); |
|
164 } |
|
165 |
|
166 int HbTreeModelIterator::childCount(const QModelIndex &parent) const |
|
167 { |
|
168 Q_D(const HbTreeModelIterator); |
|
169 if (d->mModel) { |
|
170 return d->mModel->rowCount(parent); |
|
171 } |
|
172 return 0; |
|
173 } |
|
174 |
|
175 /*! |
|
176 \reimp |
|
177 |
|
178 Next index for valid index is determined in following way: |
|
179 - If index is in collapsed branch QModelIndex is returned |
|
180 - If index has children and it is expanded then first child is returned |
|
181 - Otherwise if index has next sibling then that is returned |
|
182 - Otherwise next valid sibling for parent is returned |
|
183 - Otherwise QModelIndex is returned |
|
184 |
|
185 \a index must belong to mRootIndex branch, otherwise result is not determined. |
|
186 |
|
187 To get first index use nextIndex(QModelIndex()). |
|
188 */ |
|
189 QModelIndex HbTreeModelIterator::nextIndex(const QModelIndex &index) const |
|
190 { |
|
191 Q_D(const HbTreeModelIterator); |
|
192 if (d->mModel) { |
|
193 QModelIndex result; |
|
194 if (!index.isValid()) { |
|
195 result = d->first(d->mRootIndex); |
|
196 } else { |
|
197 // nextIndex(d->mRootIndex) == first(d->mRootIndex) |
|
198 if (!d->isExpandedBranch(index.parent())) { |
|
199 return QModelIndex(); |
|
200 } |
|
201 QModelIndex tmpIndex = index; |
|
202 |
|
203 do { |
|
204 if (d->isExpanded(tmpIndex)) { |
|
205 // if expanded, take first child - going deeper |
|
206 result = tmpIndex.child(0, 0); |
|
207 } |
|
208 if (!result.isValid()) { |
|
209 // if not expanded, or expanded but without child |
|
210 // take next on the same level |
|
211 result = tmpIndex.sibling(tmpIndex.row()+1, 0); |
|
212 } |
|
213 while (!result.isValid()) { |
|
214 // if not possible to take next on the same level, go level up |
|
215 tmpIndex = tmpIndex.parent(); |
|
216 if (tmpIndex == d->mRootIndex |
|
217 || !tmpIndex.isValid()) { |
|
218 tmpIndex = QModelIndex(); |
|
219 break; |
|
220 } |
|
221 result = tmpIndex.sibling(tmpIndex.row()+1, 0); |
|
222 } |
|
223 } |
|
224 while (!result.isValid() && tmpIndex.isValid()); |
|
225 } |
|
226 return result; |
|
227 } // if (mModel) |
|
228 return QModelIndex(); |
|
229 } |
|
230 |
|
231 /*! |
|
232 \reimp |
|
233 |
|
234 Previous index for valid index is determined in following way: |
|
235 - If index is in collapsed branch QModelIndex is returned |
|
236 - If index has previous sibling last child from it is returned |
|
237 - Otherwise previous sibling is returned |
|
238 - Otherwise parent index is returned |
|
239 - Otherwise QModelIndex is returned |
|
240 If index was invalid then last valid is returned. |
|
241 |
|
242 \a index must belong to mRootIndex branch, otherwise result is not determined. |
|
243 |
|
244 To get last index use previousIndex(QModelIndex()). |
|
245 */ |
|
246 QModelIndex HbTreeModelIterator::previousIndex(const QModelIndex &index) const |
|
247 { |
|
248 Q_D(const HbTreeModelIterator); |
|
249 if (d->mModel) { |
|
250 QModelIndex result; |
|
251 if (!index.isValid()) { |
|
252 result = d->last(d->mRootIndex); |
|
253 } else if (index != d->mRootIndex) { // can't go outside mRootIndex |
|
254 if (!d->isExpandedBranch(index.parent())) { |
|
255 return QModelIndex(); |
|
256 } |
|
257 QModelIndex tmpIndex = index; |
|
258 |
|
259 // try to take previous on the same level |
|
260 result = tmpIndex.sibling(tmpIndex.row()-1, 0); |
|
261 if (result.isValid()) { |
|
262 // take last |
|
263 tmpIndex = d->last(result); |
|
264 if (tmpIndex.isValid()) { |
|
265 result = tmpIndex; |
|
266 } |
|
267 } else { |
|
268 result = tmpIndex.parent(); |
|
269 if (result == d->mRootIndex) { |
|
270 result = QModelIndex(); |
|
271 } |
|
272 } |
|
273 } |
|
274 return result; |
|
275 } |
|
276 return QModelIndex(); |
|
277 } |
|
278 |
|
279 void HbTreeModelIterator::setModel(QAbstractItemModel *model, |
|
280 QModelIndex rootIndex) |
|
281 { |
|
282 Q_D(HbTreeModelIterator); |
|
283 if (model != d->mModel) { |
|
284 d->resetCache(); |
|
285 d->setModel(model, rootIndex); |
|
286 if (d->mModel) { |
|
287 connect(d->mModel, SIGNAL(rowsInserted(QModelIndex,int,int)), |
|
288 this, SLOT(rowsInserted(QModelIndex,int,int))); |
|
289 connect(d->mModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), |
|
290 this, SLOT(rowsRemoved(QModelIndex,int,int))); |
|
291 connect(d->mModel, SIGNAL(columnsInserted(QModelIndex,int,int)), |
|
292 this, SLOT(columnsInserted(QModelIndex,int,int))); |
|
293 connect(d->mModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), |
|
294 this, SLOT(columnsRemoved(QModelIndex,int,int))); |
|
295 } |
|
296 } else { |
|
297 setRootIndex(rootIndex); |
|
298 } |
|
299 } |
|
300 |
|
301 void HbTreeModelIterator::setRootIndex(QModelIndex rootIndex) |
|
302 { |
|
303 if (d->mRootIndex != rootIndex) { |
|
304 Q_D(HbTreeModelIterator); |
|
305 d->setRootIndex(rootIndex); |
|
306 d->resetCache(); |
|
307 } |
|
308 } |
|
309 |
|
310 void HbTreeModelIterator::setItemContainer(HbAbstractItemContainer *itemContainer, |
|
311 int expansionKey) |
|
312 { |
|
313 Q_D(HbTreeModelIterator); |
|
314 d->mItemContainer = itemContainer; |
|
315 d->mExpansionKey = expansionKey; |
|
316 } |
|
317 |
|
318 bool HbTreeModelIterator::lessThan(const QModelIndex &index1, |
|
319 const QModelIndex &index2) const |
|
320 { |
|
321 Q_D(const HbTreeModelIterator); |
|
322 if (index1 == index2) { |
|
323 return false; |
|
324 } |
|
325 |
|
326 //- Create lists from item to root so that the root will be the first item in the list (prepend...) |
|
327 QModelIndexList indexList1 = d->createParentChainList(index1); |
|
328 QModelIndexList indexList2 = d->createParentChainList(index2); |
|
329 |
|
330 int list1Count = indexList1.count(); |
|
331 int list2Count = indexList2.count(); |
|
332 int minCount = qMin(list1Count, list2Count); |
|
333 |
|
334 //- Loop the lists starting from the root until different item is found or if other list ends. |
|
335 //- If difference is found the comparison can be made compring the rows |
|
336 //- If no difference has been found and other list ends step out of the loop |
|
337 for (int i = 0; i < minCount; i++) { |
|
338 if (indexList1.at(i)!= indexList2.at(i)) { |
|
339 if (indexList1.at(i).row() < indexList2.at(i).row()) { |
|
340 return true; |
|
341 } else { |
|
342 return false; |
|
343 } |
|
344 } |
|
345 } |
|
346 |
|
347 //- Now we can be sure that the items to be compared are from the same branch. |
|
348 //- The longer list indicates lower position |
|
349 return (list1Count < list2Count); |
|
350 } |
|
351 |
|
352 void HbTreeModelIterator::itemStateChanged(const QModelIndex &index, int stateKey) |
|
353 { |
|
354 Q_UNUSED(index); |
|
355 Q_D(HbTreeModelIterator); |
|
356 if (stateKey & d->mExpansionKey) { |
|
357 d->resetCache(); |
|
358 } |
|
359 } |
|
360 |
|
361 void HbTreeModelIterator::rowsInserted(const QModelIndex &parent, int start, int end) |
|
362 { |
|
363 Q_D(HbTreeModelIterator); |
|
364 Q_UNUSED(parent); |
|
365 Q_UNUSED(start); |
|
366 Q_UNUSED(end); |
|
367 d->resetCache(); |
|
368 } |
|
369 |
|
370 void HbTreeModelIterator::rowsRemoved(const QModelIndex &parent, int start, int end) |
|
371 { |
|
372 Q_D(HbTreeModelIterator); |
|
373 Q_UNUSED(parent); |
|
374 Q_UNUSED(start); |
|
375 Q_UNUSED(end); |
|
376 d->resetCache(); |
|
377 } |
|
378 |
|
379 void HbTreeModelIterator::columnsInserted(const QModelIndex &parent, int start, int end) |
|
380 { |
|
381 Q_D(HbTreeModelIterator); |
|
382 Q_UNUSED(parent); |
|
383 Q_UNUSED(start); |
|
384 Q_UNUSED(end); |
|
385 d->resetCache(); |
|
386 } |
|
387 |
|
388 void HbTreeModelIterator::columnsRemoved(const QModelIndex &parent, int start, int end) |
|
389 { |
|
390 Q_D(HbTreeModelIterator); |
|
391 Q_UNUSED(parent); |
|
392 Q_UNUSED(start); |
|
393 Q_UNUSED(end); |
|
394 d->resetCache(); |
|
395 } |
|
396 |
|
397 #include "moc_hbtreemodeliterator_p.cpp" |
|
398 |