Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmllistcompositor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <QtCore/qvarlengtharray.h>
8
9//#define QT_QML_VERIFY_MINIMAL
10//#define QT_QML_VERIFY_INTEGRITY
11
13
14/*!
15 \class QQmlListCompositor
16 \brief The QQmlListCompositor class provides a lookup table for filtered, or re-ordered list
17 indexes.
18 \internal
19
20 QQmlListCompositor is intended as an aid for developing proxy models. It doesn't however
21 directly proxy a list or model, instead a range of indexes from one or many lists can be
22 inserted into the compositor and then categorized and shuffled around and it will manage the
23 task of translating from an index in the combined space into an index in a particular list.
24
25 Within a compositor indexes are categorized into groups where a group is a sub-set of the
26 total indexes referenced by the compositor, each with an address space ranging from 0 to
27 the number of indexes in the group - 1. Group memberships are independent of each other with
28 the one exception that items always retain the same order so if an index is moved within a
29 group, its position in other groups will change as well.
30
31 The iterator classes encapsulate information about a specific position in a compositor group.
32 This includes a source list, the index of an item within that list and the groups that item
33 is a member of. The iterator for a specific position in a group can be retrieved with the
34 find() function and the addition and subtraction operators of the iterators can be used to
35 navigate to adjacent items in the same group.
36
37 Items can be added to the compositor with the append() and insert() functions, group
38 membership can be changed with the setFlags() and clearFlags() functions, and the position
39 of items in the compositor can be changed with the move() function. Each of these functions
40 optionally returns a list of the changes made to indexes within each group which can then
41 be propagated to view so that it can correctly refresh its contents; e.g. 3 items
42 removed at index 6, and 5 items inserted at index 1. The notification changes are always
43 ordered from the start of the list to the end and accumulate, so if 5 items are removed at
44 index 4, one is skipped and then 3 move are removed, the changes returned are 5 items removed
45 at index 4, followed by 3 items removed at index 4.
46
47 When the contents of a source list change, the mappings within the compositor can be updated
48 with the listItemsInserted(), listItemsRemoved(), listItemsMoved(), and listItemsChanged()
49 functions. Like the direct manipulation functions these too return a list of group indexes
50 affected by the change. If items are removed from a source list they are also removed from
51 any groups they belong to with the one exception being items belonging to the \l Cache group.
52 When items belonging to this group are removed the list, index, and other group membership
53 information are discarded but Cache membership is retained until explicitly removed. This
54 allows the cache index to be retained until cached resources for that item are actually
55 released.
56
57 Internally the index mapping is stored as a list of Range objects, each has a list identifier,
58 a start index, a count, and a set of flags which represent group membership and some other
59 properties. The group index of a range is the sum of all preceding ranges that are members of
60 that group. To avoid the inefficiency of iterating over potentially all ranges when looking
61 for a specific index, each time a lookup is done the range and its indexes are cached and the
62 next lookup is done relative to this. This works out to near constant time in most relevant
63 use cases because successive index lookups are most frequently adjacent. The total number of
64 ranges is often quite small, which helps as well. If there is a need for faster random access
65 then a skip list like index may be an appropriate addition.
66
67 \sa DelegateModel
68*/
69
70#ifdef QT_QML_VERIFY_MINIMAL
71#define QT_QML_VERIFY_INTEGRITY
72/*
73 Diagnostic to verify there are no consecutive ranges, or that the compositor contains the
74 most compact representation possible.
75
76 Returns false and prints a warning if any range has a starting index equal to the end
77 (index + count) index of the previous range, and both ranges also have the same flags and list
78 property.
79
80 If there are no consecutive ranges this will return true.
81*/
82
83static bool qt_verifyMinimal(
84 const QQmlListCompositor::iterator &begin,
85 const QQmlListCompositor::iterator &end)
86{
87 bool minimal = true;
88 int index = 0;
89
90 for (const QQmlListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) {
91 if (range->previous->list == range->list
92 && range->previous->flags == (range->flags & ~QQmlListCompositor::AppendFlag)
93 && range->previous->end() == range->index) {
94 qWarning() << index << "Consecutive ranges";
95 qWarning() << *range->previous;
96 qWarning() << *range;
97 minimal = false;
98 }
99 }
100
101 return minimal;
102}
103
104#endif
105
106#ifdef QT_QML_VERIFY_INTEGRITY
107static bool qt_printInfo(const QQmlListCompositor &compositor)
108{
109 qWarning() << compositor;
110 return true;
111}
112
113/*
114 Diagnostic to verify the integrity of a compositor.
115
116 Per range this verifies there are no invalid range combinations, that non-append ranges have
117 positive non-zero counts, and that list ranges have non-negative indexes.
118
119 Accumulatively this verifies that the cached total group counts match the sum of counts
120 of member ranges.
121*/
122
123static bool qt_verifyIntegrity(
124 const QQmlListCompositor::iterator &begin,
125 const QQmlListCompositor::iterator &end,
126 const QQmlListCompositor::iterator &cachedIt)
127{
128 bool valid = true;
129
130 int index = 0;
131 QQmlListCompositor::iterator it;
132 for (it = begin; *it != *end; *it = it->next) {
133 if (it->count == 0 && !it->append()) {
134 qWarning() << index << "Empty non-append range";
135 valid = false;
136 }
137 if (it->count < 0) {
138 qWarning() << index << "Negative count";
139 valid = false;
140 }
141 if (it->list && it->flags != QQmlListCompositor::CacheFlag && it->index < 0) {
142 qWarning() << index <<"Negative index";
143 valid = false;
144 }
145 if (it->previous->next != it.range) {
146 qWarning() << index << "broken list: it->previous->next != it.range";
147 valid = false;
148 }
149 if (it->next->previous != it.range) {
150 qWarning() << index << "broken list: it->next->previous != it.range";
151 valid = false;
152 }
153 if (*it == *cachedIt) {
154 for (int i = 0; i < end.groupCount; ++i) {
155 int groupIndex = it.index[i];
156 if (cachedIt->flags & (1 << i))
157 groupIndex += cachedIt.offset;
158 if (groupIndex != cachedIt.index[i]) {
159 qWarning() << index
160 << "invalid cached index"
161 << QQmlListCompositor::Group(i)
162 << "Expected:"
163 << groupIndex
164 << "Actual"
165 << cachedIt.index[i]
166 << cachedIt;
167 valid = false;
168 }
169 }
170 }
171 it.incrementIndexes(it->count);
172 ++index;
173 }
174
175 for (int i = 0; i < end.groupCount; ++i) {
176 if (end.index[i] != it.index[i]) {
177 qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i];
178 valid = false;
179 }
180 }
181 return valid;
182}
183#endif
184
185#if defined(QT_QML_VERIFY_MINIMAL)
186# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt)
187 && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end))
188 && qt_printInfo(*this)));
189#elif defined(QT_QML_VERIFY_INTEGRITY)
190# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt)
191 && qt_printInfo(*this)));
192#else
193# define QT_QML_VERIFY_LISTCOMPOSITOR
194#endif
195
196//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args;
197#define QT_QML_TRACE_LISTCOMPOSITOR(args)
198
199QQmlListCompositor::iterator &QQmlListCompositor::iterator::operator +=(int difference)
200{
201 // Update all indexes to the start of the range.
202 decrementIndexes(offset);
203
204 // If the iterator group isn't a member of the current range ignore the current offset.
205 if (!(range->flags & groupFlag))
206 offset = 0;
207
208 offset += difference;
209
210 // Iterate backwards looking for a range with a positive offset.
211 while (offset <= 0 && range->previous->flags) {
212 range = range->previous;
213 if (range->flags & groupFlag)
214 offset += range->count;
215 decrementIndexes(range->count);
216 }
217
218 // Iterate forwards looking for the first range which contains both the offset and the
219 // iterator group.
220 while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) {
221 if (range->flags & groupFlag)
222 offset -= range->count;
223 incrementIndexes(range->count);
224 range = range->next;
225 }
226
227 // Update all the indexes to inclue the remaining offset.
228 incrementIndexes(offset);
229
230 return *this;
231}
232
233QQmlListCompositor::insert_iterator &QQmlListCompositor::insert_iterator::operator +=(int difference)
234{
235 iterator::operator +=(difference);
236
237 // If the previous range contains the append flag move the iterator to the tail of the previous
238 // range so that appended appear after the insert position.
239 if (offset == 0 && range->previous->append()) {
240 range = range->previous;
241 offset = range->inGroup() ? range->count : 0;
242 }
243
244 return *this;
245}
246
247
248/*!
249 Constructs an empty list compositor.
250*/
251
252QQmlListCompositor::QQmlListCompositor()
253 : m_end(m_ranges.next, 0, Default, 2)
254 , m_cacheIt(m_end)
255 , m_groupCount(2)
256 , m_defaultFlags(PrependFlag | DefaultFlag)
257 , m_removeFlags(AppendFlag | PrependFlag | GroupMask)
258 , m_moveId(0)
259{
260}
261
262/*!
263 Destroys a list compositor.
264*/
265
266QQmlListCompositor::~QQmlListCompositor()
267{
268 for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) {
269 next = range->next;
270 delete range;
271 }
273
274/*!
275 Inserts a range with the given source \a list, start \a index, \a count and \a flags, in front
276 of the existing range \a before.
277*/
278
279inline QQmlListCompositor::Range *QQmlListCompositor::insert(
280 Range *before, void *list, int index, int count, uint flags)
281{
282 return new Range(before, list, index, count, flags);
283}
284
285/*!
286 Erases a \a range from the compositor.
287
288 Returns a pointer to the next range in the compositor.
289*/
290
291inline QQmlListCompositor::Range *QQmlListCompositor::erase(
292 Range *range)
293{
294 Range *next = range->next;
295 next->previous = range->previous;
296 next->previous->next = range->next;
297 delete range;
298 return next;
299}
300
301/*!
302 Sets the number (\a count) of possible groups that items may belong to in a compositor.
303*/
304
305void QQmlListCompositor::setGroupCount(int count)
306{
307 m_groupCount = count;
308 m_end = iterator(&m_ranges, 0, Default, m_groupCount);
309 m_cacheIt = m_end;
310}
311
312/*!
313 Returns the number of items that belong to a \a group.
314*/
315
316int QQmlListCompositor::count(Group group) const
317{
318 return m_end.index[group];
319}
320
321/*!
322 Returns an iterator representing the item at \a index in a \a group.
323
324 The index must be between 0 and count(group) - 1.
325*/
326
327QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index)
328{
329 QT_QML_TRACE_LISTCOMPOSITOR(<< group << index)
330 Q_ASSERT(index >=0 && index < count(group));
331 if (m_cacheIt == m_end) {
332 m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount);
333 m_cacheIt += index;
334 } else {
335 const int offset = index - m_cacheIt.index[group];
336 m_cacheIt.setGroup(group);
337 m_cacheIt += offset;
338 }
339 Q_ASSERT(m_cacheIt.index[group] == index);
340 Q_ASSERT(m_cacheIt->inGroup(group));
342 return m_cacheIt;
343}
344
345/*!
346 Returns an iterator representing the item at \a index in a \a group.
347
348 The index must be between 0 and count(group) - 1.
349*/
350
351QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) const
352{
353 return const_cast<QQmlListCompositor *>(this)->find(group, index);
354}
355
356/*!
357 Returns an iterator representing an insert position in front of the item at \a index in a
358 \a group.
359
360 The iterator for an insert position can sometimes resolve to a different Range than a regular
361 iterator. This is because when items are inserted on a boundary between Ranges, if the first
362 range has the Append flag set then the items should be inserted into that range to ensure
363 that the append position for the existing range remains after the insert position.
364
365 The index must be between 0 and count(group) - 1.
366*/
367
368QQmlListCompositor::insert_iterator QQmlListCompositor::findInsertPosition(Group group, int index)
369{
370 QT_QML_TRACE_LISTCOMPOSITOR(<< group << index)
371 Q_ASSERT(index >=0 && index <= count(group));
372 insert_iterator it;
373 if (m_cacheIt == m_end) {
374 it = iterator(m_ranges.next, 0, group, m_groupCount);
375 it += index;
376 } else {
377 const int offset = index - m_cacheIt.index[group];
378 it = m_cacheIt;
379 it.setGroup(group);
380 it += offset;
381 }
382 Q_ASSERT(it.index[group] == index);
383 return it;
384}
385
386/*!
387 Appends a range of \a count indexes starting at \a index from a \a list into a compositor
388 with the given \a flags.
389
390 If supplied the \a inserts list will be populated with the positions of the inserted items
391 in each group.
392*/
393
394void QQmlListCompositor::append(
395 void *list, int index, int count, uint flags, QVector<Insert> *inserts)
396{
397 QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count << flags)
398 insert(m_end, list, index, count, flags, inserts);
399}
400
401/*!
402 Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags
403 into a \a group at index \a before.
404
405 If supplied the \a inserts list will be populated with the positions of items inserted into
406 each group.
407*/
408
409void QQmlListCompositor::insert(
410 Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts)
411{
412 QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags)
413 insert(findInsertPosition(group, before), list, index, count, flags, inserts);
414}
415
416/*!
417 Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags
418 into a compositor at position \a before.
419
420 If supplied the \a inserts list will be populated with the positions of items inserted into
421 each group.
422*/
423
424QQmlListCompositor::iterator QQmlListCompositor::insert(
425 iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts)
426{
427 QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags)
428 if (inserts) {
429 inserts->append(Insert(before, count, flags & GroupMask));
430 }
431 if (before.offset > 0) {
432 // Inserting into the middle of a range. Split it two and update the iterator so it's
433 // positioned at the start of the second half.
434 *before = insert(
435 *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next;
436 before->index += before.offset;
437 before->count -= before.offset;
438 before.offset = 0;
439 }
440
441
442 if (!(flags & AppendFlag) && *before != m_ranges.next
443 && before->previous->list == list
444 && before->previous->flags == flags
445 && (!list || before->previous->end() == index)) {
446 // The insert arguments represent a continuation of the previous range so increment
447 // its count instead of inserting a new range.
448 before->previous->count += count;
449 before.incrementIndexes(count, flags);
450 } else {
451 *before = insert(*before, list, index, count, flags);
452 before.offset = 0;
453 }
454
455 if (!(flags & AppendFlag) && before->next != &m_ranges
456 && before->list == before->next->list
457 && before->flags == before->next->flags
458 && (!list || before->end() == before->next->index)) {
459 // The current range and the next are continuous so add their counts and delete one.
460 before->next->index = before->index;
461 before->next->count += before->count;
462 *before = erase(*before);
463 }
464
465 m_end.incrementIndexes(count, flags);
466 m_cacheIt = before;
468 return before;
469}
470
471/*!
472 Sets the given flags \a flags on \a count items belonging to \a group starting at the position
473 identified by \a fromGroup and the index \a from.
474
475 If supplied the \a inserts list will be populated with insert notifications for affected groups.
476*/
477
478void QQmlListCompositor::setFlags(
479 Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts)
480{
481 QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags)
482 setFlags(find(fromGroup, from), count, group, flags, inserts);
483}
484
485/*!
486 Sets the given flags \a flags on \a count items belonging to \a group starting at the position
487 \a from.
488
489 If supplied the \a inserts list will be populated with insert notifications for affected groups.
490*/
491
492void QQmlListCompositor::setFlags(
493 iterator from, int count, Group group, uint flags, QVector<Insert> *inserts)
494{
495 QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags)
496 if (!flags || !count)
497 return;
498
499 if (from != group) {
500 // Skip to the next full range if the start one is not a member of the target group.
501 from.incrementIndexes(from->count - from.offset);
502 from.offset = 0;
503 *from = from->next;
504 } else if (from.offset > 0) {
505 // If the start position is mid range split off the portion unaffected.
506 *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next;
507 from->index += from.offset;
508 from->count -= from.offset;
509 from.offset = 0;
510 }
511
512 for (; count > 0; *from = from->next) {
513 if (from != from.group) {
514 // Skip ranges that are not members of the target group.
515 from.incrementIndexes(from->count);
516 continue;
517 }
518 // Find the number of items affected in the current range.
519 const int difference = qMin(count, from->count);
520 count -= difference;
521
522 // Determine the actual changes made to the range and increment counts accordingly.
523 const uint insertFlags = ~from->flags & flags;
524 const uint setFlags = (from->flags | flags) & ~AppendFlag;
525 if (insertFlags && inserts)
526 inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag)));
527 m_end.incrementIndexes(difference, insertFlags);
528 from.incrementIndexes(difference, setFlags);
529
530 if (from->previous != &m_ranges
531 && from->previous->list == from->list
532 && (!from->list || from->previous->end() == from->index)
533 && from->previous->flags == setFlags) {
534 // If the additional flags make the current range a continuation of the previous
535 // then move the affected items over to the previous range.
536 from->previous->count += difference;
537 from->index += difference;
538 from->count -= difference;
539 if (from->count == 0) {
540 // Delete the current range if it is now empty, preserving the append flag
541 // in the previous range.
542 if (from->append())
543 from->previous->flags |= AppendFlag;
544 *from = erase(*from)->previous;
545 continue;
546 } else {
547 break;
548 }
549 } else if (!insertFlags) {
550 // No new flags, so roll onto the next range.
551 from.incrementIndexes(from->count - difference);
552 continue;
553 } else if (difference < from->count) {
554 // Create a new range with the updated flags, and remove the affected items
555 // from the current range.
556 *from = insert(*from, from->list, from->index, difference, setFlags)->next;
557 from->index += difference;
558 from->count -= difference;
559 } else {
560 // The whole range is affected so simply update the flags.
561 from->flags |= flags;
562 continue;
563 }
564 from.incrementIndexes(from->count);
565 }
566
567 if (from->previous != &m_ranges
568 && from->previous->list == from->list
569 && (!from->list || from->previous->end() == from->index)
570 && from->previous->flags == (from->flags & ~AppendFlag)) {
571 // If the following range is now a continuation, merge it with its previous range.
572 from.offset = from->previous->count;
573 from->previous->count += from->count;
574 from->previous->flags = from->flags;
575 *from = erase(*from)->previous;
576 }
577 m_cacheIt = from;
579}
580
581/*!
582 Clears the given flags \a flags on \a count items belonging to \a group starting at the position
583 \a from.
584
585 If supplied the \a removes list will be populated with remove notifications for affected groups.
586*/
587
588void QQmlListCompositor::clearFlags(
589 Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removes)
590{
591 QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags)
592 clearFlags(find(fromGroup, from), count, group, flags, removes);
593}
594
595/*!
596 Clears the given flags \a flags on \a count items belonging to \a group starting at the position
597 identified by \a fromGroup and the index \a from.
598
599 If supplied the \a removes list will be populated with remove notifications for affected groups.
600*/
601
602void QQmlListCompositor::clearFlags(
603 iterator from, int count, Group group, uint flags, QVector<Remove> *removes)
604{
605 QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags)
606 if (!flags || !count)
607 return;
608
609 const bool clearCache = flags & CacheFlag;
610
611 if (from != group) {
612 // Skip to the next full range if the start one is not a member of the target group.
613 from.incrementIndexes(from->count - from.offset);
614 from.offset = 0;
615 *from = from->next;
616 } else if (from.offset > 0) {
617 // If the start position is mid range split off the portion unaffected.
618 *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next;
619 from->index += from.offset;
620 from->count -= from.offset;
621 from.offset = 0;
622 }
623
624 for (; count > 0; *from = from->next) {
625 if (from != group) {
626 // Skip ranges that are not members of the target group.
627 from.incrementIndexes(from->count);
628 continue;
629 }
630 // Find the number of items affected in the current range.
631 const int difference = qMin(count, from->count);
632 count -= difference;
633
634
635 // Determine the actual changes made to the range and decrement counts accordingly.
636 const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag);
637 const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag);
638 if (removeFlags && removes) {
639 const int maskedFlags = clearCache
640 ? (removeFlags & ~CacheFlag)
641 : (removeFlags | (from->flags & CacheFlag));
642 if (maskedFlags)
643 removes->append(Remove(from, difference, maskedFlags));
644 }
645 m_end.decrementIndexes(difference, removeFlags);
646 from.incrementIndexes(difference, clearedFlags);
647
648 if (from->previous != &m_ranges
649 && from->previous->list == from->list
650 && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index)
651 && from->previous->flags == clearedFlags) {
652 // If the removed flags make the current range a continuation of the previous
653 // then move the affected items over to the previous range.
654 from->previous->count += difference;
655 from->index += difference;
656 from->count -= difference;
657 if (from->count == 0) {
658 // Delete the current range if it is now empty, preserving the append flag
659 if (from->append())
660 from->previous->flags |= AppendFlag;
661 *from = erase(*from)->previous;
662 } else {
663 from.incrementIndexes(from->count);
664 }
665 } else if (difference < from->count) {
666 // Create a new range with the reduced flags, and remove the affected items from
667 // the current range.
668 if (clearedFlags)
669 *from = insert(*from, from->list, from->index, difference, clearedFlags)->next;
670 from->index += difference;
671 from->count -= difference;
672 from.incrementIndexes(from->count);
673 } else if (clearedFlags) {
674 // The whole range is affected so simply update the flags.
675 from->flags &= ~flags;
676 } else {
677 // All flags have been removed from the range so remove it.
678 *from = erase(*from)->previous;
679 }
680 }
681
682 if (*from != &m_ranges && from->previous != &m_ranges
683 && from->previous->list == from->list
684 && (!from->list || from->previous->end() == from->index)
685 && from->previous->flags == (from->flags & ~AppendFlag)) {
686 // If the following range is now a continuation, merge it with its previous range.
687 from.offset = from->previous->count;
688 from->previous->count += from->count;
689 from->previous->flags = from->flags;
690 *from = erase(*from)->previous;
691 }
692 m_cacheIt = from;
694}
695
696bool QQmlListCompositor::verifyMoveTo(
697 Group fromGroup, int from, Group toGroup, int to, int count, Group group) const
698{
699 if (group != toGroup) {
700 // determine how many items from the destination group intersect with the source group.
701 iterator fromIt = find(fromGroup, from);
702
703 int intersectingCount = 0;
704
705 for (; count > 0; *fromIt = fromIt->next) {
706 if (*fromIt == &m_ranges)
707 return false;
708 if (!fromIt->inGroup(group))
709 continue;
710 if (fromIt->inGroup(toGroup))
711 intersectingCount += qMin(count, fromIt->count - fromIt.offset);
712 count -= fromIt->count - fromIt.offset;
713 fromIt.offset = 0;
714 }
715 count = intersectingCount;
716 }
717
718 return to >= 0 && to + count <= m_end.index[toGroup];
719}
720
721/*!
722 \internal
723
724 Moves \a count items belonging to \a moveGroup from the index \a from in \a fromGroup
725 to the index \a to in \a toGroup.
726
727 If \a removes and \a inserts are not null they will be populated with per group notifications
728 of the items moved.
729 */
730
731void QQmlListCompositor::move(
732 Group fromGroup,
733 int from,
734 Group toGroup,
735 int to,
736 int count,
737 Group moveGroup,
738 QVector<Remove> *removes,
739 QVector<Insert> *inserts)
740{
741 QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count)
742 Q_ASSERT(count > 0);
743 Q_ASSERT(from >=0);
744 Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup));
745
746 // Find the position of the first item to move.
747 iterator fromIt = find(fromGroup, from);
748
749 if (fromIt != moveGroup) {
750 // If the range at the from index doesn't contain items from the move group; skip
751 // to the next range.
752 fromIt.incrementIndexes(fromIt->count - fromIt.offset);
753 fromIt.offset = 0;
754 *fromIt = fromIt->next;
755 } else if (fromIt.offset > 0) {
756 // If the range at the from index contains items from the move group and the index isn't
757 // at the start of the range; split the range at the index and move the iterator to start
758 // of the second range.
759 *fromIt = insert(
760 *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next;
761 fromIt->index += fromIt.offset;
762 fromIt->count -= fromIt.offset;
763 fromIt.offset = 0;
764 }
765
766 // Remove count items belonging to the move group from the list.
767 Range movedFlags;
768 for (int moveId = m_moveId; count > 0;) {
769 if (fromIt != moveGroup) {
770 // Skip ranges not containing items from the move group.
771 fromIt.incrementIndexes(fromIt->count);
772 *fromIt = fromIt->next;
773 continue;
774 }
775 int difference = qMin(count, fromIt->count);
776
777 // Create a new static range containing the moved items from an existing range.
778 new Range(
779 &movedFlags,
780 fromIt->list,
781 fromIt->index,
782 difference,
783 fromIt->flags & ~(PrependFlag | AppendFlag));
784 // Remove moved items from the count, the existing range, and a remove notification.
785 if (removes)
786 removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId));
787 count -= difference;
788 fromIt->count -= difference;
789
790 // If the existing range contains the prepend flag replace the removed items with
791 // a placeholder range for new items inserted into the source model.
792 int removeIndex = fromIt->index;
793 if (fromIt->prepend()
794 && fromIt->previous != &m_ranges
795 && fromIt->previous->flags == PrependFlag
796 && fromIt->previous->list == fromIt->list
797 && fromIt->previous->end() == fromIt->index) {
798 // Grow the previous range instead of creating a new one if possible.
799 fromIt->previous->count += difference;
800 } else if (fromIt->prepend()) {
801 *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next;
802 }
803 fromIt->index += difference;
804
805 if (fromIt->count == 0) {
806 // If the existing range has no items remaining; remove it from the list.
807 if (fromIt->append())
808 fromIt->previous->flags |= AppendFlag;
809 *fromIt = erase(*fromIt);
810
811 // If the ranges before and after the removed range can be joined, do so.
812 if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag
813 && fromIt->previous != &m_ranges
814 && fromIt->previous->flags == PrependFlag
815 && fromIt->previous->list == fromIt->list
816 && fromIt->previous->end() == fromIt->index) {
817 fromIt.incrementIndexes(fromIt->count);
818 fromIt->previous->count += fromIt->count;
819 *fromIt = erase(*fromIt);
820 }
821 } else if (count > 0) {
822 *fromIt = fromIt->next;
823 }
824 }
825
826 // Try and join the range following the removed items to the range preceding it.
827 if (*fromIt != m_ranges.next
828 && *fromIt != &m_ranges
829 && fromIt->previous->list == fromIt->list
830 && (!fromIt->list || fromIt->previous->end() == fromIt->index)
831 && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) {
832 if (fromIt == fromIt.group)
833 fromIt.offset = fromIt->previous->count;
834 fromIt.offset = fromIt->previous->count;
835 fromIt->previous->count += fromIt->count;
836 fromIt->previous->flags = fromIt->flags;
837 *fromIt = erase(*fromIt)->previous;
838 }
839
840 // Find the destination position of the move.
841 insert_iterator toIt = fromIt;
842 toIt.setGroup(toGroup);
843
844 const int difference = to - toIt.index[toGroup];
845 toIt += difference;
846
847 // If the insert position is part way through a range; split it and move the iterator to the
848 // start of the second range.
849 if (toIt.offset > 0) {
850 *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next;
851 toIt->index += toIt.offset;
852 toIt->count -= toIt.offset;
853 toIt.offset = 0;
854 }
855
856 // Insert the moved ranges before the insert iterator, growing the previous range if that
857 // is an option.
858 for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) {
859 if (*toIt != &m_ranges
860 && range->list == toIt->list
861 && (!range->list || range->end() == toIt->index)
862 && range->flags == (toIt->flags & ~AppendFlag)) {
863 toIt->index -= range->count;
864 toIt->count += range->count;
865 } else {
866 *toIt = insert(*toIt, range->list, range->index, range->count, range->flags);
867 }
868 }
869
870 // Try and join the range after the inserted ranges to the last range inserted.
871 if (*toIt != m_ranges.next
872 && toIt->previous->list == toIt->list
873 && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) {
874 toIt.offset = toIt->previous->count;
875 toIt->previous->count += toIt->count;
876 toIt->previous->flags = toIt->flags;
877 *toIt = erase(*toIt)->previous;
878 }
879 // Create insert notification for the ranges moved.
880 Insert insert(toIt, 0, 0, 0);
881 for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) {
882 insert.count = range->count;
883 insert.flags = range->flags;
884 if (inserts) {
885 insert.moveId = ++m_moveId;
886 inserts->append(insert);
887 }
888 for (int i = 0; i < m_groupCount; ++i) {
889 if (insert.inGroup(i))
890 insert.index[i] += range->count;
891 }
892
893 next = range->next;
894 delete range;
895 }
896
897 m_cacheIt = toIt;
898
900}
901
902/*!
903 Clears the contents of a compositor.
904*/
905
906void QQmlListCompositor::clear()
907{
909 for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {}
910 m_end = iterator(m_ranges.next, 0, Default, m_groupCount);
911 m_cacheIt = m_end;
912}
913
914void QQmlListCompositor::listItemsInserted(
915 QVector<Insert> *translatedInsertions,
916 void *list,
917 const QVector<QQmlChangeSet::Change> &insertions,
918 const QVector<MovedFlags> *movedFlags)
919{
920 QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions)
921 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
922 if (it->list != list || it->flags == CacheFlag) {
923 // Skip ranges that don't reference list.
924 it.incrementIndexes(it->count);
925 continue;
926 } else if (it->flags & MovedFlag) {
927 // Skip ranges that were already moved in listItemsRemoved.
928 it->flags &= ~MovedFlag;
929 it.incrementIndexes(it->count);
930 continue;
931 }
932 for (const QQmlChangeSet::Change &insertion : insertions) {
933 int offset = insertion.index - it->index;
934 if ((offset > 0 && offset < it->count)
935 || (offset == 0 && it->prepend())
936 || (offset == it->count && it->append())) {
937 // The insert index is within the current range.
938 if (it->prepend()) {
939 // The range has the prepend flag set so we insert new items into the range.
940 uint flags = m_defaultFlags;
941 if (insertion.isMove()) {
942 // If the insert was part of a move replace the default flags with
943 // the flags from the source range.
944 for (QVector<MovedFlags>::const_iterator move = movedFlags->begin();
945 move != movedFlags->end();
946 ++move) {
947 if (move->moveId == insertion.moveId) {
948 flags = move->flags;
949 break;
950 }
951 }
952 }
953 if (flags & ~(AppendFlag | PrependFlag)) {
954 // If any items are added to groups append an insert notification.
955 Insert translatedInsert(it, insertion.count, flags, insertion.moveId);
956 for (int i = 0; i < m_groupCount; ++i) {
957 if (it->inGroup(i))
958 translatedInsert.index[i] += offset;
959 }
960 translatedInsertions->append(translatedInsert);
961 }
962 if ((it->flags & ~AppendFlag) == flags) {
963 // Accumulate items on the current range it its flags are the same as
964 // the insert flags.
965 it->count += insertion.count;
966 } else if (offset == 0
967 && it->previous != &m_ranges
968 && it->previous->list == list
969 && it->previous->end() == insertion.index
970 && it->previous->flags == flags) {
971 // Attempt to append to the previous range if the insert position is at
972 // the start of the current range.
973 it->previous->count += insertion.count;
974 it->index += insertion.count;
975 it.incrementIndexes(insertion.count);
976 } else {
977 if (offset > 0) {
978 // Divide the current range at the insert position.
979 it.incrementIndexes(offset);
980 *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
981 }
982 // Insert a new range, and increment the start index of the current range.
983 *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next;
984 it.incrementIndexes(insertion.count, flags);
985 it->index += offset + insertion.count;
986 it->count -= offset;
987 }
988 m_end.incrementIndexes(insertion.count, flags);
989 } else {
990 // The range doesn't have the prepend flag set so split the range into parts;
991 // one before the insert position and one after the last inserted item.
992 if (offset > 0) {
993 *it = insert(*it, it->list, it->index, offset, it->flags)->next;
994 it->index += offset;
995 it->count -= offset;
996 }
997 it->index += insertion.count;
998 }
999 } else if (offset <= 0) {
1000 // The insert position was before the current range so increment the start index.
1001 it->index += insertion.count;
1002 }
1003 }
1004 it.incrementIndexes(it->count);
1005 }
1006 m_cacheIt = m_end;
1008}
1009
1010/*!
1011 Updates the contents of a compositor when \a count items are inserted into a \a list at
1012 \a index.
1013
1014 This corrects the indexes of each range for that list in the compositor, splitting the range
1015 in two if the insert index is in the middle of that range. If a range at the insert position
1016 has the Prepend flag set then a new range will be inserted at that position with the groups
1017 specified in defaultGroups(). If the insert index corresponds to the end of a range with
1018 the Append flag set a new range will be inserted before the end of the append range.
1019
1020 The \a translatedInsertions list is populated with insert notifications for affected
1021 groups.
1022*/
1023
1024void QQmlListCompositor::listItemsInserted(
1025 void *list, int index, int count, QVector<Insert> *translatedInsertions)
1026{
1027 QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count)
1028 Q_ASSERT(count > 0);
1029
1030 QVector<QQmlChangeSet::Change> insertions;
1031 insertions.append(QQmlChangeSet::Change(index, count));
1032
1033 listItemsInserted(translatedInsertions, list, insertions);
1034}
1035
1036void QQmlListCompositor::listItemsRemoved(
1037 QVector<Remove> *translatedRemovals,
1038 void *list,
1039 QVector<QQmlChangeSet::Change> *removals,
1040 QVector<QQmlChangeSet::Change> *insertions,
1041 QVector<MovedFlags> *movedFlags)
1042{
1043 QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals)
1044
1045 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
1046 if (it->list != list || it->flags == CacheFlag) {
1047 // Skip ranges that don't reference list.
1048 it.incrementIndexes(it->count);
1049 continue;
1050 }
1051 bool removed = false;
1052 for (QVector<QQmlChangeSet::Change>::iterator removal = removals->begin();
1053 !removed && removal != removals->end();
1054 ++removal) {
1055 int relativeIndex = removal->index - it->index;
1056 int itemsRemoved = removal->count;
1057 if (relativeIndex + removal->count > 0 && relativeIndex < it->count) {
1058 // If the current range intersects the remove; remove the intersecting items.
1059 const int offset = qMax(0, relativeIndex);
1060 int removeCount = qMin(it->count, relativeIndex + removal->count) - offset;
1061 it->count -= removeCount;
1062 int removeFlags = it->flags & m_removeFlags;
1063 Remove translatedRemoval(it, removeCount, it->flags);
1064 for (int i = 0; i < m_groupCount; ++i) {
1065 if (it->inGroup(i))
1066 translatedRemoval.index[i] += offset;
1067 }
1068 if (removal->isMove()) {
1069 // If the removal was part of a move find the corresponding insert.
1070 QVector<QQmlChangeSet::Change>::iterator insertion = insertions->begin();
1071 for (; insertion != insertions->end() && insertion->moveId != removal->moveId;
1072 ++insertion) {}
1073 Q_ASSERT(insertion != insertions->end());
1074 Q_ASSERT(insertion->count == removal->count);
1075
1076 if (relativeIndex < 0) {
1077 // If the remove started before the current range, split it and the
1078 // corresponding insert so we're only working with intersecting part.
1079 int splitMoveId = ++m_moveId;
1080 removal = removals->insert(removal, QQmlChangeSet::Change(
1081 removal->index, -relativeIndex, splitMoveId));
1082 ++removal;
1083 removal->count -= -relativeIndex;
1084 insertion = insertions->insert(insertion, QQmlChangeSet::Change(
1085 insertion->index, -relativeIndex, splitMoveId));
1086 ++insertion;
1087 insertion->index += -relativeIndex;
1088 insertion->count -= -relativeIndex;
1089 }
1090
1091 if (it->prepend()) {
1092 // If the current range has the prepend flag preserve its flags to transfer
1093 // to its new location.
1094 removeFlags |= it->flags & CacheFlag;
1095 translatedRemoval.moveId = ++m_moveId;
1096 movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag));
1097
1098 if (removeCount < removal->count) {
1099 // If the remove doesn't encompass all of the current range,
1100 // split it and the corresponding insert.
1101 removal = removals->insert(removal, QQmlChangeSet::Change(
1102 removal->index, removeCount, translatedRemoval.moveId));
1103 ++removal;
1104 insertion = insertions->insert(insertion, QQmlChangeSet::Change(
1105 insertion->index, removeCount, translatedRemoval.moveId));
1106 ++insertion;
1107
1108 removal->count -= removeCount;
1109 insertion->index += removeCount;
1110 insertion->count -= removeCount;
1111 } else {
1112 // If there's no need to split the move simply replace its moveId
1113 // with that of the translated move.
1114 removal->moveId = translatedRemoval.moveId;
1115 insertion->moveId = translatedRemoval.moveId;
1116 }
1117 } else {
1118 // If the current range doesn't have prepend flags then insert a new range
1119 // with list indexes from the corresponding insert location. The MoveFlag
1120 // is to notify listItemsInserted that it can skip evaluation of that range.
1121 if (offset > 0) {
1122 *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
1123 it->index += offset;
1124 it->count -= offset;
1125 it.incrementIndexes(offset);
1126 }
1127 if (it->previous != &m_ranges
1128 && it->previous->list == it->list
1129 && it->end() == insertion->index
1130 && it->previous->flags == (it->flags | MovedFlag)) {
1131 it->previous->count += removeCount;
1132 } else {
1133 *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next;
1134 }
1135 // Clear the changed flags as the item hasn't been removed.
1136 translatedRemoval.flags = 0;
1137 removeFlags = 0;
1138 }
1139 } else if (it->inCache()) {
1140 // If not moving and the current range has the cache flag, insert a new range
1141 // with just the cache flag set to retain caching information for the removed
1142 // range.
1143 if (offset > 0) {
1144 *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
1145 it->index += offset;
1146 it->count -= offset;
1147 it.incrementIndexes(offset);
1148 }
1149 if (it->previous != &m_ranges
1150 && it->previous->list == it->list
1151 && it->previous->flags == CacheFlag) {
1152 it->previous->count += removeCount;
1153 } else {
1154 *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next;
1155 }
1156 it.index[Cache] += removeCount;
1157 }
1158 if (removeFlags & GroupMask)
1159 translatedRemovals->append(translatedRemoval);
1160 m_end.decrementIndexes(removeCount, removeFlags);
1161 if (it->count == 0 && !it->append()) {
1162 // Erase empty non-append ranges.
1163 *it = erase(*it)->previous;
1164 removed = true;
1165 } else if (relativeIndex <= 0) {
1166 // If the remove started before the current range move the start index of
1167 // the range to the remove index.
1168 it->index = removal->index;
1169 }
1170 } else if (relativeIndex < 0) {
1171 // If the remove was before the current range decrement the start index by the
1172 // number of items removed.
1173 it->index -= itemsRemoved;
1174
1175 if (it->previous != &m_ranges
1176 && it->previous->list == it->list
1177 && it->previous->end() == it->index
1178 && it->previous->flags == (it->flags & ~AppendFlag)) {
1179 // Compress ranges made continuous by the removal of separating ranges.
1180 it.decrementIndexes(it->previous->count);
1181 it->previous->count += it->count;
1182 it->previous->flags = it->flags;
1183 *it = erase(*it)->previous;
1184 }
1185 }
1186 }
1187 if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) {
1188 // Compress consecutive cache only ranges.
1189 it.index[Cache] += it->next->count;
1190 it->count += it->next->count;
1191 erase(it->next);
1192 } else if (!removed) {
1193 it.incrementIndexes(it->count);
1194 }
1195 }
1196 m_cacheIt = m_end;
1198}
1199
1200
1201/*!
1202 Updates the contents of a compositor when \a count items are removed from a \a list at
1203 \a index.
1204
1205 Ranges that intersect the specified range are removed from the compositor and the indexes of
1206 ranges after index + count are updated.
1207
1208 The \a translatedRemovals list is populated with remove notifications for the affected
1209 groups.
1210*/
1211
1212
1213void QQmlListCompositor::listItemsRemoved(
1214 void *list, int index, int count, QVector<Remove> *translatedRemovals)
1215{
1216 QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count)
1217 Q_ASSERT(count >= 0);
1218
1219 QVector<QQmlChangeSet::Change> removals;
1220 removals.append(QQmlChangeSet::Change(index, count));
1221 listItemsRemoved(translatedRemovals, list, &removals, nullptr, nullptr);
1222}
1223
1224/*!
1225 Updates the contents of a compositor when \a count items are removed from a \a list at
1226 \a index.
1227
1228 Ranges that intersect the specified range are removed from the compositor and the indexes of
1229 ranges after index + count are updated.
1230
1231 The \a translatedRemovals and translatedInserts lists are populated with move
1232 notifications for the affected groups.
1233*/
1234
1235void QQmlListCompositor::listItemsMoved(
1236 void *list,
1237 int from,
1238 int to,
1239 int count,
1240 QVector<Remove> *translatedRemovals,
1241 QVector<Insert> *translatedInsertions)
1242{
1243 QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count)
1244 Q_ASSERT(count >= 0);
1245
1246 QVector<QQmlChangeSet::Change> removals;
1247 QVector<QQmlChangeSet::Change> insertions;
1248 QVector<MovedFlags> movedFlags;
1249 removals.append(QQmlChangeSet::Change(from, count, 0));
1250 insertions.append(QQmlChangeSet::Change(to, count, 0));
1251
1252 listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags);
1253 listItemsInserted(translatedInsertions, list, insertions, &movedFlags);
1254}
1255
1256void QQmlListCompositor::listItemsChanged(
1257 QVector<Change> *translatedChanges,
1258 void *list,
1259 const QVector<QQmlChangeSet::Change> &changes)
1260{
1261 QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes)
1262 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
1263 if (it->list != list || it->flags == CacheFlag) {
1264 it.incrementIndexes(it->count);
1265 continue;
1266 } else if (!it->inGroup()) {
1267 continue;
1268 }
1269 for (const QQmlChangeSet::Change &change : changes) {
1270 const int offset = change.index - it->index;
1271 if (offset + change.count > 0 && offset < it->count) {
1272 const int changeOffset = qMax(0, offset);
1273 const int changeCount = qMin(it->count, offset + change.count) - changeOffset;
1274
1275 Change translatedChange(it, changeCount, it->flags);
1276 for (int i = 0; i < m_groupCount; ++i) {
1277 if (it->inGroup(i))
1278 translatedChange.index[i] += changeOffset;
1279 }
1280 translatedChanges->append(translatedChange);
1281 }
1282 }
1283 it.incrementIndexes(it->count);
1284 }
1285}
1286
1287
1288/*!
1289 Translates the positions of \a count changed items at \a index in a \a list.
1290
1291 The \a translatedChanges list is populated with change notifications for the
1292 affected groups.
1293*/
1294
1295void QQmlListCompositor::listItemsChanged(
1296 void *list, int index, int count, QVector<Change> *translatedChanges)
1297{
1298 QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count)
1299 Q_ASSERT(count >= 0);
1300 QVector<QQmlChangeSet::Change> changes;
1301 changes.append(QQmlChangeSet::Change(index, count));
1302 listItemsChanged(translatedChanges, list, changes);
1303}
1304
1305void QQmlListCompositor::transition(
1306 Group from,
1307 Group to,
1308 QVector<QQmlChangeSet::Change> *removes,
1309 QVector<QQmlChangeSet::Change> *inserts)
1310{
1311 int removeCount = 0;
1312 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
1313 if (it == from && it != to) {
1314 removes->append(QQmlChangeSet::Change(it.index[from]- removeCount, it->count));
1315 removeCount += it->count;
1316 } else if (it != from && it == to) {
1317 inserts->append(QQmlChangeSet::Change(it.index[to], it->count));
1318 }
1319 it.incrementIndexes(it->count);
1320 }
1321}
1322
1323/*!
1324 \internal
1325 Writes the name of \a group to \a debug.
1326*/
1327
1328QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group)
1329{
1330 switch (group) {
1331 case QQmlListCompositor::Cache: return debug << "Cache";
1332 case QQmlListCompositor::Default: return debug << "Default";
1333 default: return (debug.nospace() << "Group" << int(group)).space();
1334 }
1335
1336}
1337
1338/*!
1339 \internal
1340 Writes the contents of \a range to \a debug.
1341*/
1342
1343QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range)
1344{
1345 (debug.nospace()
1346 << "Range("
1347 << range.list) << ' '
1348 << range.index << ' '
1349 << range.count << ' '
1350 << (range.isUnresolved() ? 'U' : '0')
1351 << (range.append() ? 'A' : '0')
1352 << (range.prepend() ? 'P' : '0');
1353 for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i)
1354 debug << (range.inGroup(i) ? '1' : '0');
1355 return (debug
1356 << (range.inGroup(QQmlListCompositor::Default) ? 'D' : '0')
1357 << (range.inGroup(QQmlListCompositor::Cache) ? 'C' : '0'));
1358}
1359
1360static void qt_print_indexes(QDebug &debug, int count, const int *indexes)
1361{
1362 for (int i = count - 1; i >= 0; --i)
1363 debug << indexes[i];
1364}
1365
1366/*!
1367 \internal
1368 Writes the contents of \a it to \a debug.
1369*/
1370
1371QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it)
1372{
1373 (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset;
1374 qt_print_indexes(debug, it.groupCount, it.index);
1375 return ((debug << **it).nospace() << ')').space();
1376}
1377
1378static QDebug qt_print_change(QDebug debug, const char *name, const QQmlListCompositor::Change &change)
1379{
1380 debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' ';
1381 for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i)
1382 debug << (change.inGroup(i) ? '1' : '0');
1383 debug << (change.inGroup(QQmlListCompositor::Default) ? 'D' : '0')
1384 << (change.inGroup(QQmlListCompositor::Cache) ? 'C' : '0');
1385 int i = QQmlListCompositor::MaximumGroupCount - 1;
1386 for (; i >= 0 && !change.inGroup(i); --i) {}
1387 for (; i >= 0; --i)
1388 debug << ' ' << change.index[i];
1389 return (debug << ')').maybeSpace();
1390}
1391
1392/*!
1393 \internal
1394 Writes the contents of \a change to \a debug.
1395*/
1396
1397QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change)
1398{
1399 return qt_print_change(debug, "Change", change);
1400}
1401
1402/*!
1403 \internal
1404 Writes the contents of \a remove to \a debug.
1405*/
1406
1407QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove)
1408{
1409 return qt_print_change(debug, "Remove", remove);
1410}
1411
1412/*!
1413 \internal
1414 Writes the contents of \a insert to \a debug.
1415*/
1416
1417QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert)
1418{
1419 return qt_print_change(debug, "Insert", insert);
1420}
1421
1422/*!
1423 \internal
1424 Writes the contents of \a list to \a debug.
1425*/
1426
1427QDebug operator <<(QDebug debug, const QQmlListCompositor &list)
1428{
1429 int indexes[QQmlListCompositor::MaximumGroupCount];
1430 for (int i = 0; i < QQmlListCompositor::MaximumGroupCount; ++i)
1431 indexes[i] = 0;
1432 debug.nospace() << "QQmlListCompositor(";
1433 qt_print_indexes(debug, list.m_groupCount, list.m_end.index);
1434 for (QQmlListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) {
1435 (debug << '\n').space();
1436 qt_print_indexes(debug, list.m_groupCount, indexes);
1437 debug << ' ' << *range;
1438
1439 for (int i = 0; i < list.m_groupCount; ++i) {
1440 if (range->inGroup(i))
1441 indexes[i] += range->count;
1442 }
1443 }
1444 return (debug << ')').maybeSpace();
1445}
1446
1447QT_END_NAMESPACE
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
#define QT_QML_VERIFY_LISTCOMPOSITOR
static QDebug qt_print_change(QDebug debug, const char *name, const QQmlListCompositor::Change &change)
static void qt_print_indexes(QDebug &debug, int count, const int *indexes)
#define QT_QML_TRACE_LISTCOMPOSITOR(args)