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
qqmlchangeset.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
8
9
10/*!
11 \class QQmlChangeSet
12 \brief The QQmlChangeSet class stores an ordered list of notifications about
13 changes to a linear data set.
14 \internal
15
16 QQmlChangeSet can be used to record a series of notifications about items in an indexed list
17 being inserted, removed, moved, and changed. Notifications in the set are re-ordered so that
18 all notifications of a single type are grouped together and sorted in order of ascending index,
19 with remove notifications preceding all others, followed by insert notification, and then
20 change notifications.
21
22 Moves in a change set are represented by a remove notification paired with an insert
23 notification by way of a shared unique moveId. Re-ordering may result in one or both of the
24 paired notifications being divided, when this happens the offset member of the notification
25 will indicate the relative offset of the divided notification from the beginning of the
26 original.
27*/
28
29/*!
30 Constructs an empty change set.
31*/
32
33QQmlChangeSet::QQmlChangeSet()
34 : m_difference(0)
35{
36}
37
38/*!
39 Constructs a copy of a \a changeSet.
40*/
41
42QQmlChangeSet::QQmlChangeSet(const QQmlChangeSet &changeSet)
43 : m_removes(changeSet.m_removes)
44 , m_inserts(changeSet.m_inserts)
45 , m_changes(changeSet.m_changes)
46 , m_difference(changeSet.m_difference)
47{
48}
49
50/*!
51 Destroys a change set.
52*/
53
54QQmlChangeSet::~QQmlChangeSet()
55{
56}
57
58/*!
59 Assigns the value of a \a changeSet to another.
60*/
61
62QQmlChangeSet &QQmlChangeSet::operator =(const QQmlChangeSet &changeSet)
63{
64 m_removes = changeSet.m_removes;
65 m_inserts = changeSet.m_inserts;
66 m_changes = changeSet.m_changes;
67 m_difference = changeSet.m_difference;
68 return *this;
69}
70
71/*!
72 Appends a notification that \a count items were inserted at \a index.
73*/
74
75void QQmlChangeSet::insert(int index, int count)
76{
77 insert(QVector<Change>() << Change(index, count));
78}
79
80/*!
81 Appends a notification that \a count items were removed at \a index.
82*/
83
84void QQmlChangeSet::remove(int index, int count)
85{
86 QVector<Change> removes;
87 removes.append(Change(index, count));
88 remove(&removes, nullptr);
89}
90
91/*!
92 Appends a notification that \a count items were moved \a from one index \a to another.
93
94 The \a moveId must be unique across the lifetime of the change set and any related
95 change sets.
96*/
97
98void QQmlChangeSet::move(int from, int to, int count, int moveId)
99{
100 QVector<Change> removes;
101 removes.append(Change(from, count, moveId));
102 QVector<Change> inserts;
103 inserts.append(Change(to, count, moveId));
104 remove(&removes, &inserts);
105 insert(inserts);
106}
107
108/*!
109 Appends a notification that \a count items were changed at \a index.
110*/
111
112void QQmlChangeSet::change(int index, int count)
113{
114 QVector<Change> changes;
115 changes.append(Change(index, count));
116 change(changes);
117}
118
119/*!
120 Applies the changes in a \a changeSet to another.
121*/
122
123void QQmlChangeSet::apply(const QQmlChangeSet &changeSet)
124{
125 QVector<Change> r = changeSet.m_removes;
126 QVector<Change> i = changeSet.m_inserts;
127 QVector<Change> c = changeSet.m_changes;
128 remove(&r, &i);
129 insert(i);
130 change(c);
131}
132
133/*!
134 Applies a list of \a removes to a change set.
135
136 If a remove contains a moveId then any intersecting insert in the set will replace the
137 corresponding intersection in the optional \a inserts list.
138*/
139
140void QQmlChangeSet::remove(const QVector<Change> &removes, QVector<Change> *inserts)
141{
142 QVector<Change> r = removes;
143 remove(&r, inserts);
144}
145
146void QQmlChangeSet::remove(QVector<Change> *removes, QVector<Change> *inserts)
147{
148 int removeCount = 0;
149 int insertCount = 0;
150 QVector<Change>::iterator insert = m_inserts.begin();
151 QVector<Change>::iterator change = m_changes.begin();
152 QVector<Change>::iterator rit = removes->begin();
153 for (; rit != removes->end(); ++rit) {
154 int index = rit->index + removeCount;
155 int count = rit->count;
156
157 // Decrement the accumulated remove count from the indexes of any changes prior to the
158 // current remove.
159 for (; change != m_changes.end() && change->end() < rit->index; ++change)
160 change->index -= removeCount;
161 // Remove any portion of a change notification that intersects the current remove.
162 for (; change != m_changes.end() && change->index > rit->end(); ++change) {
163 change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index);
164 if (change->count == 0) {
165 change = m_changes.erase(change);
166 } else if (rit->index < change->index) {
167 change->index = rit->index;
168 }
169 }
170
171 // Decrement the accumulated remove count from the indexes of any inserts prior to the
172 // current remove.
173 for (; insert != m_inserts.end() && insert->end() <= index; ++insert) {
174 insertCount += insert->count;
175 insert->index -= removeCount;
176 }
177
178 rit->index -= insertCount;
179
180 // Remove any portion of a insert notification that intersects the current remove.
181 while (insert != m_inserts.end() && insert->index < index + count) {
182 int offset = index - insert->index;
183 const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index);
184
185 // If part of the remove or insert that precedes the intersection has a moveId create
186 // a new delta for that portion and subtract the size of that delta from the current
187 // one.
188 if (offset < 0 && rit->moveId != -1) {
189 rit = removes->insert(rit, Change(
190 rit->index, -offset, rit->moveId, rit->offset));
191 ++rit;
192 rit->count -= -offset;
193 rit->offset += -offset;
194 index += -offset;
195 count -= -offset;
196 removeCount += -offset;
197 offset = 0;
198 } else if (offset > 0 && insert->moveId != -1) {
199 insert = m_inserts.insert(insert, Change(
200 insert->index - removeCount, offset, insert->moveId, insert->offset));
201 ++insert;
202 insert->index += offset;
203 insert->count -= offset;
204 insert->offset += offset;
205 rit->index -= offset;
206 insertCount += offset;
207 }
208
209 // If the current remove has a move id, find any inserts with the same move id and
210 // replace the corresponding sections with the insert removed from the change set.
211 if (rit->moveId != -1 && difference > 0 && inserts) {
212 for (QVector<Change>::iterator iit = inserts->begin(); iit != inserts->end(); ++iit) {
213 if (iit->moveId != rit->moveId
214 || rit->offset > iit->offset + iit->count
215 || iit->offset > rit->offset + difference) {
216 continue;
217 }
218 // If the intersecting insert starts before the replacement one create
219 // a new insert for the portion prior to the replacement insert.
220 const int overlapOffset = rit->offset - iit->offset;
221 if (overlapOffset > 0) {
222 iit = inserts->insert(iit, Change(
223 iit->index, overlapOffset, iit->moveId, iit->offset));
224 ++iit;
225 iit->index += overlapOffset;
226 iit->count -= overlapOffset;
227 iit->offset += overlapOffset;
228 }
229 if (iit->offset >= rit->offset
230 && iit->offset + iit->count <= rit->offset + difference) {
231 // If the replacement insert completely encapsulates the existing
232 // one just change the moveId.
233 iit->moveId = insert->moveId;
234 iit->offset = insert->offset + qMax(0, -overlapOffset);
235 } else {
236 // Create a new insertion before the intersecting one with the number of intersecting
237 // items and remove that number from that insert.
238 const int count
239 = qMin(iit->offset + iit->count, rit->offset + difference)
240 - qMax(iit->offset, rit->offset);
241 iit = inserts->insert(iit, Change(
242 iit->index,
243 count,
244 insert->moveId,
245 insert->offset + qMax(0, -overlapOffset)));
246 ++iit;
247 iit->index += count;
248 iit->count -= count;
249 iit->offset += count;
250 }
251 }
252 }
253
254 // Subtract the number of intersecting items from the current remove and insert.
255 insert->count -= difference;
256 insert->offset += difference;
257 rit->count -= difference;
258 rit->offset += difference;
259
260 index += difference;
261 count -= difference;
262 removeCount += difference;
263
264 if (insert->count == 0) {
265 insert = m_inserts.erase(insert);
266 } else if (rit->count == -offset || rit->count == 0) {
267 insert->index += difference;
268 break;
269 } else {
270 insert->index -= removeCount - difference;
271 rit->index -= insert->count;
272 insertCount += insert->count;
273 ++insert;
274 }
275 }
276 removeCount += rit->count;
277 }
278 for (; insert != m_inserts.end(); ++insert)
279 insert->index -= removeCount;
280
281 removeCount = 0;
282 QVector<Change>::iterator remove = m_removes.begin();
283 for (rit = removes->begin(); rit != removes->end(); ++rit) {
284 if (rit->count == 0)
285 continue;
286 // Accumulate consecutive removes into a single delta before attempting to apply.
287 for (QVector<Change>::iterator next = rit + 1; next != removes->end()
288 && next->index == rit->index
289 && next->moveId == -1
290 && rit->moveId == -1; ++next) {
291 next->count += rit->count;
292 rit = next;
293 }
294 int index = rit->index + removeCount;
295 // Decrement the accumulated remove count from the indexes of any inserts prior to the
296 // current remove.
297 for (; remove != m_removes.end() && index > remove->index; ++remove)
298 remove->index -= removeCount;
299 while (remove != m_removes.end() && index + rit->count >= remove->index) {
300 int count = 0;
301 const int offset = remove->index - index;
302 QVector<Change>::iterator rend = remove;
303 for (; rend != m_removes.end()
304 && rit->moveId == -1
305 && rend->moveId == -1
306 && index + rit->count >= rend->index; ++rend) {
307 count += rend->count;
308 }
309 if (remove != rend) {
310 // Accumulate all existing non-move removes that are encapsulated by or immediately
311 // follow the current remove into it.
312 int difference = 0;
313 if (rend == m_removes.end()) {
314 difference = rit->count;
315 } else if (rit->index + rit->count < rend->index - removeCount) {
316 difference = rit->count;
317 } else if (rend->moveId != -1) {
318 difference = rend->index - removeCount - rit->index;
319 index += difference;
320 }
321 count += difference;
322
323 rit->count -= difference;
324 removeCount += difference;
325 remove->index = rit->index;
326 remove->count = count;
327 remove = m_removes.erase(++remove, rend);
328 } else {
329 // Insert a remove for the portion of the unmergable current remove prior to the
330 // point of intersection.
331 if (offset > 0) {
332 remove = m_removes.insert(remove, Change(
333 rit->index, offset, rit->moveId, rit->offset));
334 ++remove;
335 rit->count -= offset;
336 rit->offset += offset;
337 removeCount += offset;
338 index += offset;
339 }
340 remove->index = rit->index;
341
342 ++remove;
343 }
344 }
345
346 if (rit->count > 0) {
347 remove = m_removes.insert(remove, *rit);
348 ++remove;
349 }
350 removeCount += rit->count;
351 }
352 for (; remove != m_removes.end(); ++remove)
353 remove->index -= removeCount;
354 m_difference -= removeCount;
355}
356
357/*!
358 Applies a list of \a inserts to a change set.
359*/
360
361void QQmlChangeSet::insert(const QVector<Change> &inserts)
362{
363 int insertCount = 0;
364 QVector<Change>::iterator insert = m_inserts.begin();
365 QVector<Change>::iterator change = m_changes.begin();
366 for (QVector<Change>::const_iterator iit = inserts.begin(); iit != inserts.end(); ++iit) {
367 if (iit->count == 0)
368 continue;
369 int index = iit->index - insertCount;
370
371 Change current = *iit;
372 // Accumulate consecutive inserts into a single delta before attempting to insert.
373 for (QVector<Change>::const_iterator next = iit + 1; next != inserts.end()
374 && next->index == iit->index + iit->count
375 && next->moveId == -1
376 && iit->moveId == -1; ++next) {
377 current.count += next->count;
378 iit = next;
379 }
380
381 // Increment the index of any changes before the current insert by the accumlated insert
382 // count.
383 for (; change != m_changes.end() && change->index >= index; ++change)
384 change->index += insertCount;
385 // If the current insert index is in the middle of a change split it in two at that
386 // point and increment the index of the latter half.
387 if (change != m_changes.end() && change->index < index + iit->count) {
388 int offset = index - change->index;
389 change = m_changes.insert(change, Change(change->index + insertCount, offset));
390 ++change;
391 change->index += iit->count + offset;
392 change->count -= offset;
393 }
394
395 // Increment the index of any inserts before the current insert by the accumlated insert
396 // count.
397 for (; insert != m_inserts.end() && index > insert->index + insert->count; ++insert)
398 insert->index += insertCount;
399 if (insert == m_inserts.end()) {
400 insert = m_inserts.insert(insert, current);
401 ++insert;
402 } else {
403 const int offset = index - insert->index;
404
405 if (offset < 0) {
406 // If the current insert is before an existing insert and not adjacent just insert
407 // it into the list.
408 insert = m_inserts.insert(insert, current);
409 ++insert;
410 } else if (iit->moveId == -1 && insert->moveId == -1) {
411 // If neither the current nor existing insert has a moveId add the current insert
412 // to the existing one.
413 if (offset < insert->count) {
414 insert->index -= current.count;
415 insert->count += current.count;
416 } else {
417 insert->index += insertCount;
418 insert->count += current.count;
419 ++insert;
420 }
421 } else if (offset < insert->count) {
422 // If either insert has a moveId then split the existing insert and insert the
423 // current one in the middle.
424 if (offset > 0) {
425 insert = m_inserts.insert(insert, Change(
426 insert->index + insertCount, offset, insert->moveId, insert->offset));
427 ++insert;
428 insert->index += offset;
429 insert->count -= offset;
430 insert->offset += offset;
431 }
432 insert = m_inserts.insert(insert, current);
433 ++insert;
434 } else {
435 insert->index += insertCount;
436 ++insert;
437 insert = m_inserts.insert(insert, current);
438 ++insert;
439 }
440 }
441 insertCount += current.count;
442 }
443 for (; insert != m_inserts.end(); ++insert)
444 insert->index += insertCount;
445 m_difference += insertCount;
446}
447
448/*!
449 Applies a combined list of \a removes and \a inserts to a change set. This is equivalent
450 calling \l remove() followed by \l insert() with the same lists.
451*/
452
453void QQmlChangeSet::move(const QVector<Change> &removes, const QVector<Change> &inserts)
454{
455 QVector<Change> r = removes;
456 QVector<Change> i = inserts;
457 remove(&r, &i);
458 insert(i);
459}
460
461/*!
462 Applies a list of \a changes to a change set.
463*/
464
465void QQmlChangeSet::change(const QVector<Change> &changes)
466{
467 QVector<Change> c = changes;
468 change(&c);
469}
470
471void QQmlChangeSet::change(QVector<Change> *changes)
472{
473 QVector<Change>::iterator insert = m_inserts.begin();
474 QVector<Change>::iterator change = m_changes.begin();
475 for (QVector<Change>::iterator cit = changes->begin(); cit != changes->end(); ++cit) {
476 for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {}
477 for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) {
478 const int offset = insert->index - cit->index;
479 const int count = cit->count + cit->index - insert->index - insert->count;
480 if (offset == 0) {
481 cit->index = insert->index + insert->count;
482 cit->count = count;
483 } else {
484 cit = changes->insert(++cit, Change(insert->index + insert->count, count));
485 --cit;
486 cit->count = offset;
487 }
488 }
489
490 for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {}
491 if (change == m_changes.end() || change->index > cit->index + cit->count) {
492 if (cit->count > 0) {
493 change = m_changes.insert(change, *cit);
494 ++change;
495 }
496 } else {
497 if (cit->index < change->index) {
498 change->count += change->index - cit->index;
499 change->index = cit->index;
500 }
501
502 if (cit->index + cit->count > change->index + change->count) {
503 change->count = cit->index + cit->count - change->index;
504 QVector<Change>::iterator cbegin = change;
505 QVector<Change>::iterator cend = ++cbegin;
506 for (; cend != m_changes.end() && cend->index <= change->index + change->count; ++cend) {
507 if (cend->index + cend->count > change->index + change->count)
508 change->count = cend->index + cend->count - change->index;
509 }
510 if (cbegin != cend) {
511 change = m_changes.erase(cbegin, cend);
512 --change;
513 }
514 }
515 }
516 }
517}
518
519/*!
520 \internal
521 \relates QQmlChangeSet
522 Prints the contents of a change \a set to the \a debug stream.
523*/
524
525QDebug operator <<(QDebug debug, const QQmlChangeSet &set)
526{
527 QDebugStateSaver stateSaver(debug);
528 debug.nospace() << "QQmlChangeSet(";
529 const QVector<QQmlChangeSet::Change> &removes = set.removes();
530 for (const QQmlChangeSet::Change &remove : removes)
531 debug << remove << ' ';
532 const QVector<QQmlChangeSet::Change> &inserts = set.inserts();
533 for (const QQmlChangeSet::Change &insert : inserts)
534 debug << insert << ' ';
535 const QVector<QQmlChangeSet::Change> &changes = set.changes();
536 for (const QQmlChangeSet::Change &change : changes)
537 debug << change << ' ';
538 return debug.nospace() << ')';
539}
540
541/*!
542 \internal
543 \relates QQmlChangeSet
544 Prints a \a change to the \a debug stream.
545*/
546
547QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change)
548{
549 QDebugStateSaver stateSaver(debug);
550 return debug.nospace() << "Change(" << change.index << ',' << change.count << ')';
551}
552
553QT_END_NAMESPACE
QDebug operator<<(QDebug dbg, const QFileInfo &fi)