Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
5
6#include <QtCore/qvarlengtharray.h>
7
8//#define QT_QML_VERIFY_MINIMAL
9//#define QT_QML_VERIFY_INTEGRITY
10
12
69#ifdef QT_QML_VERIFY_MINIMAL
70#define QT_QML_VERIFY_INTEGRITY
71/*
72 Diagnostic to verify there are no consecutive ranges, or that the compositor contains the
73 most compact representation possible.
74
75 Returns false and prints a warning if any range has a starting index equal to the end
76 (index + count) index of the previous range, and both ranges also have the same flags and list
77 property.
78
79 If there are no consecutive ranges this will return true.
80*/
81
82static bool qt_verifyMinimal(
85{
86 bool minimal = true;
87 int index = 0;
88
89 for (const QQmlListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) {
90 if (range->previous->list == range->list
91 && range->previous->flags == (range->flags & ~QQmlListCompositor::AppendFlag)
92 && range->previous->end() == range->index) {
93 qWarning() << index << "Consecutive ranges";
94 qWarning() << *range->previous;
95 qWarning() << *range;
96 minimal = false;
97 }
98 }
99
100 return minimal;
101}
102
103#endif
104
105#ifdef QT_QML_VERIFY_INTEGRITY
106static bool qt_printInfo(const QQmlListCompositor &compositor)
107{
108 qWarning() << compositor;
109 return true;
110}
111
112/*
113 Diagnostic to verify the integrity of a compositor.
114
115 Per range this verifies there are no invalid range combinations, that non-append ranges have
116 positive non-zero counts, and that list ranges have non-negative indexes.
117
118 Accumulatively this verifies that the cached total group counts match the sum of counts
119 of member ranges.
120*/
121
122static bool qt_verifyIntegrity(
125 const QQmlListCompositor::iterator &cachedIt)
126{
127 bool valid = true;
128
129 int index = 0;
131 for (it = begin; *it != *end; *it = it->next) {
132 if (it->count == 0 && !it->append()) {
133 qWarning() << index << "Empty non-append range";
134 valid = false;
135 }
136 if (it->count < 0) {
137 qWarning() << index << "Negative count";
138 valid = false;
139 }
140 if (it->list && it->flags != QQmlListCompositor::CacheFlag && it->index < 0) {
141 qWarning() << index <<"Negative index";
142 valid = false;
143 }
144 if (it->previous->next != it.range) {
145 qWarning() << index << "broken list: it->previous->next != it.range";
146 valid = false;
147 }
148 if (it->next->previous != it.range) {
149 qWarning() << index << "broken list: it->next->previous != it.range";
150 valid = false;
151 }
152 if (*it == *cachedIt) {
153 for (int i = 0; i < end.groupCount; ++i) {
154 int groupIndex = it.index[i];
155 if (cachedIt->flags & (1 << i))
156 groupIndex += cachedIt.offset;
157 if (groupIndex != cachedIt.index[i]) {
158 qWarning() << index
159 << "invalid cached index"
161 << "Expected:"
162 << groupIndex
163 << "Actual"
164 << cachedIt.index[i]
165 << cachedIt;
166 valid = false;
167 }
168 }
169 }
170 it.incrementIndexes(it->count);
171 ++index;
172 }
173
174 for (int i = 0; i < end.groupCount; ++i) {
175 if (end.index[i] != it.index[i]) {
176 qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i];
177 valid = false;
178 }
179 }
180 return valid;
181}
182#endif
183
184#if defined(QT_QML_VERIFY_MINIMAL)
185# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \
186 && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \
187 && qt_printInfo(*this)));
188#elif defined(QT_QML_VERIFY_INTEGRITY)
189# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \
190 && qt_printInfo(*this)));
191#else
192# define QT_QML_VERIFY_LISTCOMPOSITOR
193#endif
194
195//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args;
196#define QT_QML_TRACE_LISTCOMPOSITOR(args)
197
199{
200 // Update all indexes to the start of the range.
202
203 // If the iterator group isn't a member of the current range ignore the current offset.
204 if (!(range->flags & groupFlag))
205 offset = 0;
206
207 offset += difference;
208
209 // Iterate backwards looking for a range with a positive offset.
210 while (offset <= 0 && range->previous->flags) {
211 range = range->previous;
212 if (range->flags & groupFlag)
213 offset += range->count;
214 decrementIndexes(range->count);
215 }
216
217 // Iterate forwards looking for the first range which contains both the offset and the
218 // iterator group.
219 while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) {
220 if (range->flags & groupFlag)
221 offset -= range->count;
222 incrementIndexes(range->count);
223 range = range->next;
224 }
225
226 // Update all the indexes to inclue the remaining offset.
228
229 return *this;
230}
231
233{
234 iterator::operator +=(difference);
235
236 // If the previous range contains the append flag move the iterator to the tail of the previous
237 // range so that appended appear after the insert position.
238 if (offset == 0 && range->previous->append()) {
239 range = range->previous;
240 offset = range->inGroup() ? range->count : 0;
241 }
242
243 return *this;
244}
245
246
252 : m_end(m_ranges.next, 0, Default, 2)
253 , m_cacheIt(m_end)
254 , m_groupCount(2)
255 , m_defaultFlags(PrependFlag | DefaultFlag)
256 , m_removeFlags(AppendFlag | PrependFlag | GroupMask)
257 , m_moveId(0)
258{
259}
260
266{
267 for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) {
268 next = range->next;
269 delete range;
270 }
271}
279 Range *before, void *list, int index, int count, uint flags)
280{
281 return new Range(before, list, index, count, flags);
282}
283
290inline QQmlListCompositor::Range *QQmlListCompositor::erase(
291 Range *range)
292{
293 Range *next = range->next;
294 next->previous = range->previous;
295 next->previous->next = range->next;
296 delete range;
297 return next;
298}
299
305{
306 m_groupCount = count;
307 m_end = iterator(&m_ranges, 0, Default, m_groupCount);
308 m_cacheIt = m_end;
309}
310
316{
317 return m_end.index[group];
318}
319
327{
329 Q_ASSERT(index >=0 && index < count(group));
330 if (m_cacheIt == m_end) {
331 m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount);
332 m_cacheIt += index;
333 } else {
334 const int offset = index - m_cacheIt.index[group];
335 m_cacheIt.setGroup(group);
336 m_cacheIt += offset;
337 }
338 Q_ASSERT(m_cacheIt.index[group] == index);
339 Q_ASSERT(m_cacheIt->inGroup(group));
341 return m_cacheIt;
342}
343
354
368{
370 Q_ASSERT(index >=0 && index <= count(group));
372 if (m_cacheIt == m_end) {
373 it = iterator(m_ranges.next, 0, group, m_groupCount);
374 it += index;
375 } else {
376 const int offset = index - m_cacheIt.index[group];
377 it = m_cacheIt;
378 it.setGroup(group);
379 it += offset;
380 }
381 Q_ASSERT(it.index[group] == index);
382 return it;
383}
384
394 void *list, int index, int count, uint flags, QVector<Insert> *inserts)
395{
397 insert(m_end, list, index, count, flags, inserts);
398}
399
409 Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts)
410{
411 QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags)
412 insert(findInsertPosition(group, before), list, index, count, flags, inserts);
413}
414
424 iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts)
425{
426 QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags)
427 if (inserts) {
428 inserts->append(Insert(before, count, flags & GroupMask));
429 }
430 if (before.offset > 0) {
431 // Inserting into the middle of a range. Split it two and update the iterator so it's
432 // positioned at the start of the second half.
433 *before = insert(
434 *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next;
435 before->index += before.offset;
436 before->count -= before.offset;
437 before.offset = 0;
438 }
439
440
441 if (!(flags & AppendFlag) && *before != m_ranges.next
442 && before->previous->list == list
443 && before->previous->flags == flags
444 && (!list || before->previous->end() == index)) {
445 // The insert arguments represent a continuation of the previous range so increment
446 // its count instead of inserting a new range.
447 before->previous->count += count;
449 } else {
450 *before = insert(*before, list, index, count, flags);
451 before.offset = 0;
452 }
453
454 if (!(flags & AppendFlag) && before->next != &m_ranges
455 && before->list == before->next->list
456 && before->flags == before->next->flags
457 && (!list || before->end() == before->next->index)) {
458 // The current range and the next are continuous so add their counts and delete one.
459 before->next->index = before->index;
460 before->next->count += before->count;
461 *before = erase(*before);
462 }
463
465 m_cacheIt = before;
467 return before;
468}
469
478 Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts)
479{
480 QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags)
481 setFlags(find(fromGroup, from), count, group, flags, inserts);
482}
483
492 iterator from, int count, Group group, uint flags, QVector<Insert> *inserts)
493{
495 if (!flags || !count)
496 return;
497
498 if (from != group) {
499 // Skip to the next full range if the start one is not a member of the target group.
500 from.incrementIndexes(from->count - from.offset);
501 from.offset = 0;
502 *from = from->next;
503 } else if (from.offset > 0) {
504 // If the start position is mid range split off the portion unaffected.
505 *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next;
506 from->index += from.offset;
507 from->count -= from.offset;
508 from.offset = 0;
509 }
510
511 for (; count > 0; *from = from->next) {
512 if (from != from.group) {
513 // Skip ranges that are not members of the target group.
514 from.incrementIndexes(from->count);
515 continue;
516 }
517 // Find the number of items affected in the current range.
518 const int difference = qMin(count, from->count);
519 count -= difference;
520
521 // Determine the actual changes made to the range and increment counts accordingly.
522 const uint insertFlags = ~from->flags & flags;
523 const uint setFlags = (from->flags | flags) & ~AppendFlag;
524 if (insertFlags && inserts)
525 inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag)));
526 m_end.incrementIndexes(difference, insertFlags);
527 from.incrementIndexes(difference, setFlags);
528
529 if (from->previous != &m_ranges
530 && from->previous->list == from->list
531 && (!from->list || from->previous->end() == from->index)
532 && from->previous->flags == setFlags) {
533 // If the additional flags make the current range a continuation of the previous
534 // then move the affected items over to the previous range.
535 from->previous->count += difference;
536 from->index += difference;
537 from->count -= difference;
538 if (from->count == 0) {
539 // Delete the current range if it is now empty, preserving the append flag
540 // in the previous range.
541 if (from->append())
542 from->previous->flags |= AppendFlag;
543 *from = erase(*from)->previous;
544 continue;
545 } else {
546 break;
547 }
548 } else if (!insertFlags) {
549 // No new flags, so roll onto the next range.
550 from.incrementIndexes(from->count - difference);
551 continue;
552 } else if (difference < from->count) {
553 // Create a new range with the updated flags, and remove the affected items
554 // from the current range.
555 *from = insert(*from, from->list, from->index, difference, setFlags)->next;
556 from->index += difference;
557 from->count -= difference;
558 } else {
559 // The whole range is affected so simply update the flags.
560 from->flags |= flags;
561 continue;
562 }
563 from.incrementIndexes(from->count);
564 }
565
566 if (from->previous != &m_ranges
567 && from->previous->list == from->list
568 && (!from->list || from->previous->end() == from->index)
569 && from->previous->flags == (from->flags & ~AppendFlag)) {
570 // If the following range is now a continuation, merge it with its previous range.
571 from.offset = from->previous->count;
572 from->previous->count += from->count;
573 from->previous->flags = from->flags;
574 *from = erase(*from)->previous;
575 }
576 m_cacheIt = from;
578}
579
588 Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removes)
589{
590 QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags)
591 clearFlags(find(fromGroup, from), count, group, flags, removes);
592}
593
602 iterator from, int count, Group group, uint flags, QVector<Remove> *removes)
603{
605 if (!flags || !count)
606 return;
607
608 const bool clearCache = flags & CacheFlag;
609
610 if (from != group) {
611 // Skip to the next full range if the start one is not a member of the target group.
612 from.incrementIndexes(from->count - from.offset);
613 from.offset = 0;
614 *from = from->next;
615 } else if (from.offset > 0) {
616 // If the start position is mid range split off the portion unaffected.
617 *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next;
618 from->index += from.offset;
619 from->count -= from.offset;
620 from.offset = 0;
621 }
622
623 for (; count > 0; *from = from->next) {
624 if (from != group) {
625 // Skip ranges that are not members of the target group.
626 from.incrementIndexes(from->count);
627 continue;
628 }
629 // Find the number of items affected in the current range.
630 const int difference = qMin(count, from->count);
631 count -= difference;
632
633
634 // Determine the actual changes made to the range and decrement counts accordingly.
635 const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag);
636 const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag);
637 if (removeFlags && removes) {
638 const int maskedFlags = clearCache
639 ? (removeFlags & ~CacheFlag)
640 : (removeFlags | (from->flags & CacheFlag));
641 if (maskedFlags)
642 removes->append(Remove(from, difference, maskedFlags));
643 }
644 m_end.decrementIndexes(difference, removeFlags);
645 from.incrementIndexes(difference, clearedFlags);
646
647 if (from->previous != &m_ranges
648 && from->previous->list == from->list
649 && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index)
650 && from->previous->flags == clearedFlags) {
651 // If the removed flags make the current range a continuation of the previous
652 // then move the affected items over to the previous range.
653 from->previous->count += difference;
654 from->index += difference;
655 from->count -= difference;
656 if (from->count == 0) {
657 // Delete the current range if it is now empty, preserving the append flag
658 if (from->append())
659 from->previous->flags |= AppendFlag;
660 *from = erase(*from)->previous;
661 } else {
662 from.incrementIndexes(from->count);
663 }
664 } else if (difference < from->count) {
665 // Create a new range with the reduced flags, and remove the affected items from
666 // the current range.
667 if (clearedFlags)
668 *from = insert(*from, from->list, from->index, difference, clearedFlags)->next;
669 from->index += difference;
670 from->count -= difference;
671 from.incrementIndexes(from->count);
672 } else if (clearedFlags) {
673 // The whole range is affected so simply update the flags.
674 from->flags &= ~flags;
675 } else {
676 // All flags have been removed from the range so remove it.
677 *from = erase(*from)->previous;
678 }
679 }
680
681 if (*from != &m_ranges && from->previous != &m_ranges
682 && from->previous->list == from->list
683 && (!from->list || from->previous->end() == from->index)
684 && from->previous->flags == (from->flags & ~AppendFlag)) {
685 // If the following range is now a continuation, merge it with its previous range.
686 from.offset = from->previous->count;
687 from->previous->count += from->count;
688 from->previous->flags = from->flags;
689 *from = erase(*from)->previous;
690 }
691 m_cacheIt = from;
693}
694
696 Group fromGroup, int from, Group toGroup, int to, int count, Group group) const
697{
698 if (group != toGroup) {
699 // determine how many items from the destination group intersect with the source group.
700 iterator fromIt = find(fromGroup, from);
701
702 int intersectingCount = 0;
703
704 for (; count > 0; *fromIt = fromIt->next) {
705 if (*fromIt == &m_ranges)
706 return false;
707 if (!fromIt->inGroup(group))
708 continue;
709 if (fromIt->inGroup(toGroup))
710 intersectingCount += qMin(count, fromIt->count - fromIt.offset);
711 count -= fromIt->count - fromIt.offset;
712 fromIt.offset = 0;
713 }
714 count = intersectingCount;
715 }
716
717 return to >= 0 && to + count <= m_end.index[toGroup];
718}
719
731 Group fromGroup,
732 int from,
733 Group toGroup,
734 int to,
735 int count,
736 Group moveGroup,
737 QVector<Remove> *removes,
738 QVector<Insert> *inserts)
739{
740 QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count)
741 Q_ASSERT(count > 0);
742 Q_ASSERT(from >=0);
743 Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup));
744
745 // Find the position of the first item to move.
746 iterator fromIt = find(fromGroup, from);
747
748 if (fromIt != moveGroup) {
749 // If the range at the from index doesn't contain items from the move group; skip
750 // to the next range.
751 fromIt.incrementIndexes(fromIt->count - fromIt.offset);
752 fromIt.offset = 0;
753 *fromIt = fromIt->next;
754 } else if (fromIt.offset > 0) {
755 // If the range at the from index contains items from the move group and the index isn't
756 // at the start of the range; split the range at the index and move the iterator to start
757 // of the second range.
758 *fromIt = insert(
759 *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next;
760 fromIt->index += fromIt.offset;
761 fromIt->count -= fromIt.offset;
762 fromIt.offset = 0;
763 }
764
765 // Remove count items belonging to the move group from the list.
766 Range movedFlags;
767 for (int moveId = m_moveId; count > 0;) {
768 if (fromIt != moveGroup) {
769 // Skip ranges not containing items from the move group.
770 fromIt.incrementIndexes(fromIt->count);
771 *fromIt = fromIt->next;
772 continue;
773 }
774 int difference = qMin(count, fromIt->count);
775
776 // Create a new static range containing the moved items from an existing range.
777 new Range(
778 &movedFlags,
779 fromIt->list,
780 fromIt->index,
781 difference,
782 fromIt->flags & ~(PrependFlag | AppendFlag));
783 // Remove moved items from the count, the existing range, and a remove notification.
784 if (removes)
785 removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId));
786 count -= difference;
787 fromIt->count -= difference;
788
789 // If the existing range contains the prepend flag replace the removed items with
790 // a placeholder range for new items inserted into the source model.
791 int removeIndex = fromIt->index;
792 if (fromIt->prepend()
793 && fromIt->previous != &m_ranges
794 && fromIt->previous->flags == PrependFlag
795 && fromIt->previous->list == fromIt->list
796 && fromIt->previous->end() == fromIt->index) {
797 // Grow the previous range instead of creating a new one if possible.
798 fromIt->previous->count += difference;
799 } else if (fromIt->prepend()) {
800 *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next;
801 }
802 fromIt->index += difference;
803
804 if (fromIt->count == 0) {
805 // If the existing range has no items remaining; remove it from the list.
806 if (fromIt->append())
807 fromIt->previous->flags |= AppendFlag;
808 *fromIt = erase(*fromIt);
809
810 // If the ranges before and after the removed range can be joined, do so.
811 if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag
812 && fromIt->previous != &m_ranges
813 && fromIt->previous->flags == PrependFlag
814 && fromIt->previous->list == fromIt->list
815 && fromIt->previous->end() == fromIt->index) {
816 fromIt.incrementIndexes(fromIt->count);
817 fromIt->previous->count += fromIt->count;
818 *fromIt = erase(*fromIt);
819 }
820 } else if (count > 0) {
821 *fromIt = fromIt->next;
822 }
823 }
824
825 // Try and join the range following the removed items to the range preceding it.
826 if (*fromIt != m_ranges.next
827 && *fromIt != &m_ranges
828 && fromIt->previous->list == fromIt->list
829 && (!fromIt->list || fromIt->previous->end() == fromIt->index)
830 && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) {
831 if (fromIt == fromIt.group)
832 fromIt.offset = fromIt->previous->count;
833 fromIt.offset = fromIt->previous->count;
834 fromIt->previous->count += fromIt->count;
835 fromIt->previous->flags = fromIt->flags;
836 *fromIt = erase(*fromIt)->previous;
837 }
838
839 // Find the destination position of the move.
840 insert_iterator toIt = fromIt;
841 toIt.setGroup(toGroup);
842
843 const int difference = to - toIt.index[toGroup];
844 toIt += difference;
845
846 // If the insert position is part way through a range; split it and move the iterator to the
847 // start of the second range.
848 if (toIt.offset > 0) {
849 *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next;
850 toIt->index += toIt.offset;
851 toIt->count -= toIt.offset;
852 toIt.offset = 0;
853 }
854
855 // Insert the moved ranges before the insert iterator, growing the previous range if that
856 // is an option.
857 for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) {
858 if (*toIt != &m_ranges
859 && range->list == toIt->list
860 && (!range->list || range->end() == toIt->index)
861 && range->flags == (toIt->flags & ~AppendFlag)) {
862 toIt->index -= range->count;
863 toIt->count += range->count;
864 } else {
865 *toIt = insert(*toIt, range->list, range->index, range->count, range->flags);
866 }
867 }
868
869 // Try and join the range after the inserted ranges to the last range inserted.
870 if (*toIt != m_ranges.next
871 && toIt->previous->list == toIt->list
872 && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) {
873 toIt.offset = toIt->previous->count;
874 toIt->previous->count += toIt->count;
875 toIt->previous->flags = toIt->flags;
876 *toIt = erase(*toIt)->previous;
877 }
878 // Create insert notification for the ranges moved.
879 Insert insert(toIt, 0, 0, 0);
880 for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) {
881 insert.count = range->count;
882 insert.flags = range->flags;
883 if (inserts) {
884 insert.moveId = ++m_moveId;
885 inserts->append(insert);
886 }
887 for (int i = 0; i < m_groupCount; ++i) {
888 if (insert.inGroup(i))
889 insert.index[i] += range->count;
890 }
891
892 next = range->next;
893 delete range;
894 }
895
896 m_cacheIt = toIt;
897
899}
900
906{
908 for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {}
909 m_end = iterator(m_ranges.next, 0, Default, m_groupCount);
910 m_cacheIt = m_end;
911}
912
914 QVector<Insert> *translatedInsertions,
915 void *list,
916 const QVector<QQmlChangeSet::Change> &insertions,
917 const QVector<MovedFlags> *movedFlags)
918{
919 QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions)
920 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
921 if (it->list != list || it->flags == CacheFlag) {
922 // Skip ranges that don't reference list.
923 it.incrementIndexes(it->count);
924 continue;
925 } else if (it->flags & MovedFlag) {
926 // Skip ranges that were already moved in listItemsRemoved.
927 it->flags &= ~MovedFlag;
928 it.incrementIndexes(it->count);
929 continue;
930 }
931 for (const QQmlChangeSet::Change &insertion : insertions) {
932 int offset = insertion.index - it->index;
933 if ((offset > 0 && offset < it->count)
934 || (offset == 0 && it->prepend())
935 || (offset == it->count && it->append())) {
936 // The insert index is within the current range.
937 if (it->prepend()) {
938 // The range has the prepend flag set so we insert new items into the range.
939 uint flags = m_defaultFlags;
940 if (insertion.isMove()) {
941 // If the insert was part of a move replace the default flags with
942 // the flags from the source range.
943 for (QVector<MovedFlags>::const_iterator move = movedFlags->begin();
944 move != movedFlags->end();
945 ++move) {
946 if (move->moveId == insertion.moveId) {
947 flags = move->flags;
948 break;
949 }
950 }
951 }
952 if (flags & ~(AppendFlag | PrependFlag)) {
953 // If any items are added to groups append an insert notification.
954 Insert translatedInsert(it, insertion.count, flags, insertion.moveId);
955 for (int i = 0; i < m_groupCount; ++i) {
956 if (it->inGroup(i))
957 translatedInsert.index[i] += offset;
958 }
959 translatedInsertions->append(translatedInsert);
960 }
961 if ((it->flags & ~AppendFlag) == flags) {
962 // Accumulate items on the current range it its flags are the same as
963 // the insert flags.
964 it->count += insertion.count;
965 } else if (offset == 0
966 && it->previous != &m_ranges
967 && it->previous->list == list
968 && it->previous->end() == insertion.index
969 && it->previous->flags == flags) {
970 // Attempt to append to the previous range if the insert position is at
971 // the start of the current range.
972 it->previous->count += insertion.count;
973 it->index += insertion.count;
974 it.incrementIndexes(insertion.count);
975 } else {
976 if (offset > 0) {
977 // Divide the current range at the insert position.
978 it.incrementIndexes(offset);
979 *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
980 }
981 // Insert a new range, and increment the start index of the current range.
982 *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next;
983 it.incrementIndexes(insertion.count, flags);
984 it->index += offset + insertion.count;
985 it->count -= offset;
986 }
987 m_end.incrementIndexes(insertion.count, flags);
988 } else {
989 // The range doesn't have the prepend flag set so split the range into parts;
990 // one before the insert position and one after the last inserted item.
991 if (offset > 0) {
992 *it = insert(*it, it->list, it->index, offset, it->flags)->next;
993 it->index += offset;
994 it->count -= offset;
995 }
996 it->index += insertion.count;
997 }
998 } else if (offset <= 0) {
999 // The insert position was before the current range so increment the start index.
1000 it->index += insertion.count;
1001 }
1002 }
1003 it.incrementIndexes(it->count);
1004 }
1005 m_cacheIt = m_end;
1007}
1008
1024 void *list, int index, int count, QVector<Insert> *translatedInsertions)
1025{
1027 Q_ASSERT(count > 0);
1028
1029 QVector<QQmlChangeSet::Change> insertions;
1030 insertions.append(QQmlChangeSet::Change(index, count));
1031
1032 listItemsInserted(translatedInsertions, list, insertions);
1033}
1034
1036 QVector<Remove> *translatedRemovals,
1037 void *list,
1038 QVector<QQmlChangeSet::Change> *removals,
1039 QVector<QQmlChangeSet::Change> *insertions,
1040 QVector<MovedFlags> *movedFlags)
1041{
1042 QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals)
1043
1044 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
1045 if (it->list != list || it->flags == CacheFlag) {
1046 // Skip ranges that don't reference list.
1047 it.incrementIndexes(it->count);
1048 continue;
1049 }
1050 bool removed = false;
1051 for (QVector<QQmlChangeSet::Change>::iterator removal = removals->begin();
1052 !removed && removal != removals->end();
1053 ++removal) {
1054 int relativeIndex = removal->index - it->index;
1055 int itemsRemoved = removal->count;
1056 if (relativeIndex + removal->count > 0 && relativeIndex < it->count) {
1057 // If the current range intersects the remove; remove the intersecting items.
1058 const int offset = qMax(0, relativeIndex);
1059 int removeCount = qMin(it->count, relativeIndex + removal->count) - offset;
1060 it->count -= removeCount;
1061 int removeFlags = it->flags & m_removeFlags;
1062 Remove translatedRemoval(it, removeCount, it->flags);
1063 for (int i = 0; i < m_groupCount; ++i) {
1064 if (it->inGroup(i))
1065 translatedRemoval.index[i] += offset;
1066 }
1067 if (removal->isMove()) {
1068 // If the removal was part of a move find the corresponding insert.
1069 QVector<QQmlChangeSet::Change>::iterator insertion = insertions->begin();
1070 for (; insertion != insertions->end() && insertion->moveId != removal->moveId;
1071 ++insertion) {}
1072 Q_ASSERT(insertion != insertions->end());
1073 Q_ASSERT(insertion->count == removal->count);
1074
1075 if (relativeIndex < 0) {
1076 // If the remove started before the current range, split it and the
1077 // corresponding insert so we're only working with intersecting part.
1078 int splitMoveId = ++m_moveId;
1079 removal = removals->insert(removal, QQmlChangeSet::Change(
1080 removal->index, -relativeIndex, splitMoveId));
1081 ++removal;
1082 removal->count -= -relativeIndex;
1083 insertion = insertions->insert(insertion, QQmlChangeSet::Change(
1084 insertion->index, -relativeIndex, splitMoveId));
1085 ++insertion;
1086 insertion->index += -relativeIndex;
1087 insertion->count -= -relativeIndex;
1088 }
1089
1090 if (it->prepend()) {
1091 // If the current range has the prepend flag preserve its flags to transfer
1092 // to its new location.
1093 removeFlags |= it->flags & CacheFlag;
1094 translatedRemoval.moveId = ++m_moveId;
1095 movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag));
1096
1097 if (removeCount < removal->count) {
1098 // If the remove doesn't encompass all of the current range,
1099 // split it and the corresponding insert.
1100 removal = removals->insert(removal, QQmlChangeSet::Change(
1101 removal->index, removeCount, translatedRemoval.moveId));
1102 ++removal;
1103 insertion = insertions->insert(insertion, QQmlChangeSet::Change(
1104 insertion->index, removeCount, translatedRemoval.moveId));
1105 ++insertion;
1106
1107 removal->count -= removeCount;
1108 insertion->index += removeCount;
1109 insertion->count -= removeCount;
1110 } else {
1111 // If there's no need to split the move simply replace its moveId
1112 // with that of the translated move.
1113 removal->moveId = translatedRemoval.moveId;
1114 insertion->moveId = translatedRemoval.moveId;
1115 }
1116 } else {
1117 // If the current range doesn't have prepend flags then insert a new range
1118 // with list indexes from the corresponding insert location. The MoveFlag
1119 // is to notify listItemsInserted that it can skip evaluation of that range.
1120 if (offset > 0) {
1121 *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
1122 it->index += offset;
1123 it->count -= offset;
1124 it.incrementIndexes(offset);
1125 }
1126 if (it->previous != &m_ranges
1127 && it->previous->list == it->list
1128 && it->end() == insertion->index
1129 && it->previous->flags == (it->flags | MovedFlag)) {
1130 it->previous->count += removeCount;
1131 } else {
1132 *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next;
1133 }
1134 // Clear the changed flags as the item hasn't been removed.
1135 translatedRemoval.flags = 0;
1136 removeFlags = 0;
1137 }
1138 } else if (it->inCache()) {
1139 // If not moving and the current range has the cache flag, insert a new range
1140 // with just the cache flag set to retain caching information for the removed
1141 // range.
1142 if (offset > 0) {
1143 *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
1144 it->index += offset;
1145 it->count -= offset;
1146 it.incrementIndexes(offset);
1147 }
1148 if (it->previous != &m_ranges
1149 && it->previous->list == it->list
1150 && it->previous->flags == CacheFlag) {
1151 it->previous->count += removeCount;
1152 } else {
1153 *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next;
1154 }
1155 it.index[Cache] += removeCount;
1156 }
1157 if (removeFlags & GroupMask)
1158 translatedRemovals->append(translatedRemoval);
1159 m_end.decrementIndexes(removeCount, removeFlags);
1160 if (it->count == 0 && !it->append()) {
1161 // Erase empty non-append ranges.
1162 *it = erase(*it)->previous;
1163 removed = true;
1164 } else if (relativeIndex <= 0) {
1165 // If the remove started before the current range move the start index of
1166 // the range to the remove index.
1167 it->index = removal->index;
1168 }
1169 } else if (relativeIndex < 0) {
1170 // If the remove was before the current range decrement the start index by the
1171 // number of items removed.
1172 it->index -= itemsRemoved;
1173
1174 if (it->previous != &m_ranges
1175 && it->previous->list == it->list
1176 && it->previous->end() == it->index
1177 && it->previous->flags == (it->flags & ~AppendFlag)) {
1178 // Compress ranges made continuous by the removal of separating ranges.
1179 it.decrementIndexes(it->previous->count);
1180 it->previous->count += it->count;
1181 it->previous->flags = it->flags;
1182 *it = erase(*it)->previous;
1183 }
1184 }
1185 }
1186 if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) {
1187 // Compress consecutive cache only ranges.
1188 it.index[Cache] += it->next->count;
1189 it->count += it->next->count;
1190 erase(it->next);
1191 } else if (!removed) {
1192 it.incrementIndexes(it->count);
1193 }
1194 }
1195 m_cacheIt = m_end;
1197}
1198
1199
1213 void *list, int index, int count, QVector<Remove> *translatedRemovals)
1214{
1216 Q_ASSERT(count >= 0);
1217
1218 QVector<QQmlChangeSet::Change> removals;
1219 removals.append(QQmlChangeSet::Change(index, count));
1220 listItemsRemoved(translatedRemovals, list, &removals, nullptr, nullptr);
1221}
1222
1235 void *list,
1236 int from,
1237 int to,
1238 int count,
1239 QVector<Remove> *translatedRemovals,
1240 QVector<Insert> *translatedInsertions)
1241{
1242 QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count)
1243 Q_ASSERT(count >= 0);
1244
1245 QVector<QQmlChangeSet::Change> removals;
1246 QVector<QQmlChangeSet::Change> insertions;
1247 QVector<MovedFlags> movedFlags;
1248 removals.append(QQmlChangeSet::Change(from, count, 0));
1249 insertions.append(QQmlChangeSet::Change(to, count, 0));
1250
1251 listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags);
1252 listItemsInserted(translatedInsertions, list, insertions, &movedFlags);
1253}
1254
1256 QVector<Change> *translatedChanges,
1257 void *list,
1258 const QVector<QQmlChangeSet::Change> &changes)
1259{
1260 QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes)
1261 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
1262 if (it->list != list || it->flags == CacheFlag) {
1263 it.incrementIndexes(it->count);
1264 continue;
1265 } else if (!it->inGroup()) {
1266 continue;
1267 }
1268 for (const QQmlChangeSet::Change &change : changes) {
1269 const int offset = change.index - it->index;
1270 if (offset + change.count > 0 && offset < it->count) {
1271 const int changeOffset = qMax(0, offset);
1272 const int changeCount = qMin(it->count, offset + change.count) - changeOffset;
1273
1274 Change translatedChange(it, changeCount, it->flags);
1275 for (int i = 0; i < m_groupCount; ++i) {
1276 if (it->inGroup(i))
1277 translatedChange.index[i] += changeOffset;
1278 }
1279 translatedChanges->append(translatedChange);
1280 }
1281 }
1282 it.incrementIndexes(it->count);
1283 }
1284}
1285
1286
1295 void *list, int index, int count, QVector<Change> *translatedChanges)
1296{
1298 Q_ASSERT(count >= 0);
1299 QVector<QQmlChangeSet::Change> changes;
1300 changes.append(QQmlChangeSet::Change(index, count));
1301 listItemsChanged(translatedChanges, list, changes);
1302}
1303
1305 Group from,
1306 Group to,
1307 QVector<QQmlChangeSet::Change> *removes,
1308 QVector<QQmlChangeSet::Change> *inserts)
1309{
1310 int removeCount = 0;
1311 for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
1312 if (it == from && it != to) {
1313 removes->append(QQmlChangeSet::Change(it.index[from]- removeCount, it->count));
1314 removeCount += it->count;
1315 } else if (it != from && it == to) {
1316 inserts->append(QQmlChangeSet::Change(it.index[to], it->count));
1317 }
1318 it.incrementIndexes(it->count);
1319 }
1320}
1321
1328{
1329 switch (group) {
1330 case QQmlListCompositor::Cache: return debug << "Cache";
1331 case QQmlListCompositor::Default: return debug << "Default";
1332 default: return (debug.nospace() << "Group" << int(group)).space();
1333 }
1334
1335}
1336
1343{
1344 (debug.nospace()
1345 << "Range("
1346 << range.list) << ' '
1347 << range.index << ' '
1348 << range.count << ' '
1349 << (range.isUnresolved() ? 'U' : '0')
1350 << (range.append() ? 'A' : '0')
1351 << (range.prepend() ? 'P' : '0');
1352 for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i)
1353 debug << (range.inGroup(i) ? '1' : '0');
1354 return (debug
1355 << (range.inGroup(QQmlListCompositor::Default) ? 'D' : '0')
1356 << (range.inGroup(QQmlListCompositor::Cache) ? 'C' : '0'));
1357}
1358
1359static void qt_print_indexes(QDebug &debug, int count, const int *indexes)
1360{
1361 for (int i = count - 1; i >= 0; --i)
1362 debug << indexes[i];
1363}
1364
1371{
1372 (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset;
1373 qt_print_indexes(debug, it.groupCount, it.index);
1374 return ((debug << **it).nospace() << ')').space();
1375}
1376
1378{
1379 debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' ';
1380 for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i)
1381 debug << (change.inGroup(i) ? '1' : '0');
1382 debug << (change.inGroup(QQmlListCompositor::Default) ? 'D' : '0')
1383 << (change.inGroup(QQmlListCompositor::Cache) ? 'C' : '0');
1385 for (; i >= 0 && !change.inGroup(i); --i) {}
1386 for (; i >= 0; --i)
1387 debug << ' ' << change.index[i];
1388 return (debug << ')').maybeSpace();
1389}
1390
1397{
1398 return qt_print_change(debug, "Change", change);
1399}
1400
1410
1420
1427{
1429 for (int i = 0; i < QQmlListCompositor::MaximumGroupCount; ++i)
1430 indexes[i] = 0;
1431 debug.nospace() << "QQmlListCompositor(";
1432 qt_print_indexes(debug, list.m_groupCount, list.m_end.index);
1433 for (QQmlListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) {
1434 (debug << '\n').space();
1435 qt_print_indexes(debug, list.m_groupCount, indexes);
1436 debug << ' ' << *range;
1437
1438 for (int i = 0; i < list.m_groupCount; ++i) {
1439 if (range->inGroup(i))
1440 indexes[i] += range->count;
1441 }
1442 }
1443 return (debug << ')').maybeSpace();
1444}
1445
\inmodule QtCore
insert_iterator & operator+=(int difference)
void decrementIndexes(int difference)
void incrementIndexes(int difference)
iterator & operator+=(int difference)
The QQmlListCompositor class provides a lookup table for filtered, or re-ordered list indexes.
void listItemsChanged(void *list, int index, int count, QVector< Change > *changes)
Translates the positions of count changed items at index in a list.
iterator find(Group group, int index)
Returns an iterator representing the item at index in a group.
void listItemsInserted(void *list, int index, int count, QVector< Insert > *inserts)
Updates the contents of a compositor when count items are inserted into a list at index.
void listItemsMoved(void *list, int from, int to, int count, QVector< Remove > *removals, QVector< Insert > *inserts)
Updates the contents of a compositor when count items are removed from a list at index.
void transition(Group from, Group to, QVector< QQmlChangeSet::Change > *removes, QVector< QQmlChangeSet::Change > *inserts)
void append(void *list, int index, int count, uint flags, QVector< Insert > *inserts=nullptr)
Appends a range of count indexes starting at index from a list into a compositor with the given flags...
QQmlListCompositor()
Constructs an empty list compositor.
int count(Group group) const
Returns the number of items that belong to a group.
void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector< Remove > *removals=nullptr)
Clears the given flags flags on count items belonging to group starting at the position from.
~QQmlListCompositor()
Destroys a list compositor.
void listItemsRemoved(void *list, int index, int count, QVector< Remove > *removals)
Updates the contents of a compositor when count items are removed from a list at index.
bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const
void move(Group fromGroup, int from, Group toGroup, int to, int count, Group group, QVector< Remove > *removals=nullptr, QVector< Insert > *inserts=nullptr)
insert_iterator findInsertPosition(Group group, int index)
Returns an iterator representing an insert position in front of the item at index in a group.
void setFlags(Group fromGroup, int from, int count, Group group, int flags, QVector< Insert > *inserts=nullptr)
Sets the given flags flags on count items belonging to group starting at the position identified by f...
void clear()
Clears the contents of a compositor.
void setGroupCount(int count)
Sets the number (count) of possible groups that items may belong to in a compositor.
void insert(Group group, int before, void *list, int index, int count, uint flags, QVector< Insert > *inserts=nullptr)
Inserts a range of count indexes starting at index from a list with the given flags into a group at i...
iterator end()
Definition qset.h:140
qsizetype count() const
Definition qset.h:154
cache insert(employee->id(), employee)
QSet< QString >::iterator it
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static QOpenGLCompositor * compositor
GLuint index
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLsizei range
GLboolean GLuint group
GLbitfield flags
GLenum GLuint GLintptr offset
GLuint name
#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)
QDebug operator<<(QDebug debug, const QQmlListCompositor::Group &group)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
unsigned int uint
Definition qtypes.h:34
QList< int > list
[14]
settings remove("monkey")