4#include <private/qtools_p.h>
7#include <qscopedvaluerollback.h>
10#include <qtextformat.h>
20#include <QtCore/q20utility.h>
26#define PMDEBUG if(0
) qDebug
29#if !defined(Q_CC_DIAB)
30# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8)
31 QTextUndoCommand c = { a1, a2, 0
, 0
, quint8(a3), a4, quint32(a5), quint32(a6), { int(a7) }, quint32(a8) }
33# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8)
34 QTextUndoCommand c = { a1, a2, 0
, 0
, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
91 layout->engine()->invalidate();
96 return ch == QChar::ParagraphSeparator
103 return !str.contains(QChar::ParagraphSeparator)
110 if (command != other.command)
113 if (command == Inserted
114 && (pos + length == other.pos)
115 && (strPos + length == other.strPos)
116 && format == other.format) {
123 if (command == Removed
125 && (strPos + length == other.strPos)
126 && format == other.format) {
133 if (command == Removed
134 && (other.pos + other.length == pos)
135 && (other.strPos + other.length == strPos)
136 && (format == other.format)) {
148QTextDocumentPrivate::QTextDocumentPrivate()
149 : wasUndoAvailable(
false),
150 wasRedoAvailable(
false),
151 docChangeOldLength(0),
155 initialBlockCharFormatIndex(-1),
156 resourceProvider(
nullptr),
157 cssMedia(QStringLiteral(
"screen"))
160 editBlockCursorPosition = -1;
172 inContentsChange =
false;
173 blockCursorAdjustment =
false;
175 defaultTextOption.setTabStopDistance(80);
176 defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
177 defaultCursorMoveStyle = Qt::LogicalMoveStyle;
182 maximumBlockCount = 0;
183 needsEnsureMaximumBlockCount =
false;
184 unreachableCharacterCount = 0;
188void QTextDocumentPrivate::init()
192 bool undoState = undoEnabled;
194 initialBlockCharFormatIndex = formats.indexForFormat(QTextCharFormat());
195 insertBlock(0, formats.indexForFormat(QTextBlockFormat()), formats.indexForFormat(QTextCharFormat()));
196 undoEnabled = undoState;
200 qRegisterMetaType<QTextDocument *>();
203void QTextDocumentPrivate::clear()
207 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
208 curs->setPosition(0);
209 curs->currentCharFormat = -1;
211 curs->adjusted_anchor = 0;
214 QSet<QTextCursorPrivate *> oldCursors = cursors;
218 QMap<
int, QTextObject *>::Iterator objectIt = objects.begin();
219 while (objectIt != objects.end()) {
220 if (*objectIt != rtFrame) {
222 objectIt = objects.erase(objectIt);
232 clearUndoRedoStacks(QTextDocument::UndoAndRedoStacks);
234 unreachableCharacterCount = 0;
238 int len = fragments.length();
241 cachedResources.clear();
245 cursors = oldCursors;
247 QScopedValueRollback<
bool> bg(inContentsChange,
true);
248 emit q->contentsChange(0, len, 0);
251 lout->documentChanged(0, len, 0);
253 cursors = oldCursors;
258QTextDocumentPrivate::~QTextDocumentPrivate()
260 for (QTextCursorPrivate *curs : std::as_const(cursors))
261 curs->priv =
nullptr;
265 clearUndoRedoStacks(QTextDocument::RedoStack);
268void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
273 const bool firstLayout = !lout;
278 for (BlockMap::Iterator it = blocks.begin(); !it.atEnd(); ++it)
281 emit q->documentLayoutChanged();
283 QScopedValueRollback<
bool> bg(inContentsChange,
true);
284 emit q->contentsChange(0, 0, length());
287 lout->documentChanged(0, 0, length());
291void QTextDocumentPrivate::insert_string(
int pos, uint strPos, uint length,
int format, QTextUndoCommand::Operation op)
294 Q_ASSERT(noBlockInString(QStringView{text}.mid(strPos, length)));
297 uint x = fragments.insert_single(pos, length);
298 QTextFragmentData *X = fragments.fragment(x);
300 X->stringPosition = strPos;
301 uint w = fragments.previous(x);
305 int b = blocks.findNode(pos);
306 blocks.setSize(b, blocks.size(b)+length);
308 Q_ASSERT(blocks.length() == fragments.length());
310 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
312 frame->d_func()->fragmentAdded(text.at(strPos), x);
316 adjustDocumentChangesAndCursors(pos, length, op);
319int QTextDocumentPrivate::insert_block(
int pos, uint strPos,
int format,
int blockFormat, QTextUndoCommand::Operation op,
int command)
322 uint x = fragments.insert_single(pos, 1);
323 QTextFragmentData *X = fragments.fragment(x);
325 X->stringPosition = strPos;
328 Q_ASSERT(isValidBlockSeparator(text.at(strPos)));
329 Q_ASSERT(blocks.length()+1 == fragments.length());
332 if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
335 int n = blocks.findNode(block_pos);
336 int key = n ? blocks.position(n) : blocks.length();
338 Q_ASSERT(n || block_pos == blocks.length());
339 if (key != block_pos) {
340 Q_ASSERT(key < block_pos);
341 int oldSize = blocks.size(n);
342 blocks.setSize(n, block_pos-key);
343 size += oldSize - (block_pos-key);
345 int b = blocks.insert_single(block_pos, size);
346 QTextBlockData *B = blocks.fragment(b);
347 B->format = blockFormat;
349 Q_ASSERT(blocks.length() == fragments.length());
351 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
353 group->blockInserted(QTextBlock(
this, b));
354 if (command != QTextUndoCommand::BlockDeleted) {
355 docChangeOldLength--;
360 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
362 frame->d_func()->fragmentAdded(text.at(strPos), x);
366 adjustDocumentChangesAndCursors(pos, 1, op);
370int QTextDocumentPrivate::insertBlock(QChar blockSeparator,
371 int pos,
int blockFormat,
int charFormat, QTextUndoCommand::Operation op)
373 Q_ASSERT(formats.format(blockFormat).isBlockFormat());
374 Q_ASSERT(formats.format(charFormat).isCharFormat());
375 Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
376 Q_ASSERT(isValidBlockSeparator(blockSeparator));
380 int strPos = text.size();
381 text.append(blockSeparator);
383 int ob = blocks.findNode(pos);
384 bool atBlockEnd =
true;
385 bool atBlockStart =
true;
388 atBlockEnd = (pos - blocks.position(ob) == blocks.size(ob)-1);
389 atBlockStart = ((
int)blocks.position(ob) == pos);
390 oldRevision = blocks.fragment(ob)->revision;
393 const int fragment = insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
395 Q_ASSERT(blocks.length() == fragments.length());
397 int b = blocks.findNode(pos);
398 QTextBlockData *B = blocks.fragment(b);
401 op, charFormat, strPos, pos, blockFormat,
405 Q_ASSERT(undoState == undoStack.size());
408 B->revision = (atBlockEnd && !atBlockStart)? oldRevision : revision;
411 B = blocks.fragment(b);
412 B->revision = atBlockStart ? oldRevision : revision;
415 if (formats.charFormat(charFormat).objectIndex() == -1)
416 needsEnsureMaximumBlockCount =
true;
422int QTextDocumentPrivate::insertBlock(
int pos,
int blockFormat,
int charFormat, QTextUndoCommand::Operation op)
424 return insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
427void QTextDocumentPrivate::insert(
int pos,
int strPos,
int strLength,
int format)
432 Q_ASSERT(pos >= 0 && pos < fragments.length());
433 Q_ASSERT(formats.format(format).isCharFormat());
435 insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
437 int b = blocks.findNode(pos);
438 QTextBlockData *B = blocks.fragment(b);
441 QTextUndoCommand::MoveCursor, format, strPos, pos, strLength,
444 B->revision = revision;
445 Q_ASSERT(undoState == undoStack.size());
450void QTextDocumentPrivate::insert(
int pos, QStringView str,
int format)
455 Q_ASSERT(noBlockInString(str));
457 int strPos = text.size();
459 insert(pos, strPos, str.size(), format);
462int QTextDocumentPrivate::remove_string(
int pos, uint length, QTextUndoCommand::Operation op)
465 Q_ASSERT(blocks.length() == fragments.length());
466 Q_ASSERT(q20::cmp_greater_equal(blocks.length(), pos+length));
468 int b = blocks.findNode(pos);
469 uint x = fragments.findNode(pos);
471 Q_ASSERT(blocks.size(b) > length);
472 Q_ASSERT(x && q20::cmp_equal(fragments.position(x), pos) && fragments.size(x) == length);
473 Q_ASSERT(noBlockInString(QStringView{text}.mid(fragments.fragment(x)->stringPosition, length)));
475 blocks.setSize(b, blocks.size(b)-length);
477 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
479 frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
483 const int w = fragments.erase_single(x);
486 unreachableCharacterCount += length;
488 adjustDocumentChangesAndCursors(pos, -
int(length), op);
493int QTextDocumentPrivate::remove_block(
int pos,
int *blockFormat,
int command, QTextUndoCommand::Operation op)
496 Q_ASSERT(blocks.length() == fragments.length());
497 Q_ASSERT(blocks.length() > pos);
499 int b = blocks.findNode(pos);
500 uint x = fragments.findNode(pos);
502 Q_ASSERT(x && (
int)fragments.position(x) == pos);
503 Q_ASSERT(fragments.size(x) == 1);
504 Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));
507 if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
508 Q_ASSERT((
int)blocks.position(b) == pos);
514 int n = blocks.next(b);
515 Q_ASSERT((
int)blocks.position(n) == pos + 1);
516 blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
517 blocks.fragment(b)->userState = blocks.fragment(n)->userState;
520 *blockFormat = blocks.fragment(b)->format;
522 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
524 group->blockRemoved(QTextBlock(
this, b));
526 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
528 frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
532 blocks.erase_single(b);
533 const int w = fragments.erase_single(x);
535 adjustDocumentChangesAndCursors(pos, -1, op);
540#if !defined(QT_NO_DEBUG)
544 if (child == possibleAncestor)
546 child = child->parentFrame();
552void QTextDocumentPrivate::move(
int pos,
int to,
int length, QTextUndoCommand::Operation op)
554 Q_ASSERT(to <= fragments.length() && to <= pos);
555 Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
556 Q_ASSERT(blocks.length() == fragments.length());
561 const bool needsInsert = to != -1;
563#if !defined(QT_NO_DEBUG)
564 const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
566 const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
567 && text.at(find(pos + length - 1)->stringPosition) ==
QTextEndOfFrame);
569 const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
572 && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
574 const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
575 && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));
577 Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
583 uint dst = needsInsert ? fragments.findNode(to) : 0;
584 uint dstKey = needsInsert ? fragments.position(dst) : 0;
586 uint x = fragments.findNode(pos);
587 uint end = fragments.findNode(pos+length);
591 uint n = fragments.next(x);
593 uint key = fragments.position(x);
594 uint b = blocks.findNode(key+1);
595 QTextBlockData *B = blocks.fragment(b);
596 int blockRevision = B->revision;
598 QTextFragmentData *X = fragments.fragment(x);
600 op, X->format, X->stringPosition, key, X->size_array[0],
603 op, X->format, X->stringPosition, dstKey, X->size_array[0],
606 if (key+1 != blocks.position(b)) {
608 Q_ASSERT(noBlockInString(QStringView{text}.mid(X->stringPosition, X->size_array[0])));
609 w = remove_string(key, X->size_array[0], op);
612 insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
613 dstKey += X->size_array[0];
617 Q_ASSERT(X->size_array[0] == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
618 b = blocks.previous(b);
620 c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
621 w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
624 insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
625 cInsert.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockAdded : QTextUndoCommand::BlockInserted;
626 cInsert.blockFormat = c.blockFormat;
631 B->revision = revision;
635 appendUndoItem(cInsert);
640 Q_ASSERT(blocks.length() == fragments.length());
642 if (!blockCursorAdjustment)
646void QTextDocumentPrivate::remove(
int pos,
int length, QTextUndoCommand::Operation op)
650 blockCursorAdjustment =
true;
651 move(pos, -1, length, op);
652 blockCursorAdjustment =
false;
653 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
654 if (curs->adjustPosition(pos, -length, op) == QTextCursorPrivate::CursorMoved) {
655 curs->changed =
true;
661void QTextDocumentPrivate::setCharFormat(
int pos,
int length,
const QTextCharFormat &newFormat, FormatChangeMode mode)
665 Q_ASSERT(newFormat.isValid());
667 int newFormatIdx = -1;
668 if (mode == SetFormatAndPreserveObjectIndices) {
669 QTextCharFormat cleanFormat = newFormat;
670 cleanFormat.clearProperty(QTextFormat::ObjectIndex);
671 newFormatIdx = formats.indexForFormat(cleanFormat);
672 }
else if (mode == SetFormat) {
673 newFormatIdx = formats.indexForFormat(newFormat);
677 if (mode == MergeFormat) {
678 QTextFormat format = formats.format(initialBlockCharFormatIndex);
679 format.merge(newFormat);
680 initialBlockCharFormatIndex = formats.indexForFormat(format);
681 }
else if (mode == SetFormatAndPreserveObjectIndices
682 && formats.format(initialBlockCharFormatIndex).objectIndex() != -1) {
683 QTextCharFormat f = newFormat;
684 f.setObjectIndex(formats.format(initialBlockCharFormatIndex).objectIndex());
685 initialBlockCharFormatIndex = formats.indexForFormat(f);
687 initialBlockCharFormatIndex = newFormatIdx;
694 const int startPos = pos;
695 const int endPos = pos + length;
700 while (pos < endPos) {
701 FragmentMap::Iterator it = fragments.find(pos);
702 Q_ASSERT(!it.atEnd());
704 QTextFragmentData *fragment = it.value();
706 Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
708 int offset = pos - it.position();
709 int length = qMin(endPos - pos,
int(fragment->size_array[0] - offset));
710 int oldFormat = fragment->format;
712 if (mode == MergeFormat) {
713 QTextFormat format = formats.format(fragment->format);
714 format.merge(newFormat);
715 fragment->format = formats.indexForFormat(format);
716 }
else if (mode == SetFormatAndPreserveObjectIndices
717 && formats.format(oldFormat).objectIndex() != -1) {
718 QTextCharFormat f = newFormat;
719 f.setObjectIndex(formats.format(oldFormat).objectIndex());
720 fragment->format = formats.indexForFormat(f);
722 fragment->format = newFormatIdx;
730 Q_ASSERT(q20::cmp_equal(pos, (it.position() + fragment->size_array[0])) || pos >= endPos);
733 int n = fragments.findNode(startPos - 1);
737 n = fragments.findNode(endPos);
741 QTextBlock blockIt = blocksFind(startPos);
742 QTextBlock endIt = blocksFind(endPos);
744 endIt = endIt.next();
745 for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
746 QTextDocumentPrivate::block(blockIt)->invalidate();
748 documentChange(startPos, length);
753void QTextDocumentPrivate::setBlockFormat(
const QTextBlock &from,
const QTextBlock &to,
754 const QTextBlockFormat &newFormat, FormatChangeMode mode)
758 Q_ASSERT(mode != SetFormatAndPreserveObjectIndices);
760 Q_ASSERT(newFormat.isValid());
762 int newFormatIdx = -1;
763 if (mode == SetFormat)
764 newFormatIdx = formats.indexForFormat(newFormat);
765 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
767 QTextBlock it = from;
772 for (; it != end; it = it.next()) {
773 int oldFormat = block(it)->format;
774 QTextBlockFormat format = formats.blockFormat(oldFormat);
775 QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
776 if (mode == MergeFormat) {
777 format.merge(newFormat);
778 newFormatIdx = formats.indexForFormat(format);
779 group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
781 block(it)->format = newFormatIdx;
783 block(it)->invalidate();
785 QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockFormatChanged,
true, QTextUndoCommand::MoveCursor, oldFormat,
786 0, it.position(), 1, 0);
789 if (group != oldGroup) {
791 oldGroup->blockRemoved(it);
793 group->blockInserted(it);
795 group->blockFormatChanged(it);
799 documentChange(from.position(), to.position() + to.length() - from.position());
805bool QTextDocumentPrivate::split(
int pos)
807 uint x = fragments.findNode(pos);
809 int k = fragments.position(x);
815 QTextFragmentData *X = fragments.fragment(x);
816 int oldsize = X->size_array[0];
817 fragments.setSize(x, pos-k);
818 uint n = fragments.insert_single(pos, oldsize-(pos-k));
819 X = fragments.fragment(x);
820 QTextFragmentData *N = fragments.fragment(n);
821 N->stringPosition = X->stringPosition + pos-k;
822 N->format = X->format;
829bool QTextDocumentPrivate::unite(uint f)
831 uint n = fragments.next(f);
835 QTextFragmentData *ff = fragments.fragment(f);
836 QTextFragmentData *nf = fragments.fragment(n);
838 if (nf->format == ff->format && (ff->stringPosition + (
int)ff->size_array[0] == nf->stringPosition)) {
839 if (isValidBlockSeparator(text.at(ff->stringPosition))
840 || isValidBlockSeparator(text.at(nf->stringPosition)))
843 fragments.setSize(f, ff->size_array[0] + nf->size_array[0]);
844 fragments.erase_single(n);
851int QTextDocumentPrivate::undoRedo(
bool undo)
853 PMDEBUG(
"%s, undoState=%d, undoStack size=%d", undo ?
"undo:" :
"redo:", undoState,
int(undoStack.size()));
854 if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
864 QTextUndoCommand &c = undoStack[undoState];
865 int resetBlockRevision = c.pos;
868 case QTextUndoCommand::Inserted:
869 remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
870 PMDEBUG(
" erase: from %d, length %d", c.pos, c.length);
871 c.command = QTextUndoCommand::Removed;
875 case QTextUndoCommand::Removed:
876 PMDEBUG(
" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
877 insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
878 c.command = QTextUndoCommand::Inserted;
879 if (editPos != (
int)c.pos)
882 editLength += c.length;
884 case QTextUndoCommand::BlockInserted:
885 case QTextUndoCommand::BlockAdded:
886 remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
887 PMDEBUG(
" blockremove: from %d", c.pos);
888 if (c.command == QTextUndoCommand::BlockInserted)
889 c.command = QTextUndoCommand::BlockRemoved;
891 c.command = QTextUndoCommand::BlockDeleted;
895 case QTextUndoCommand::BlockRemoved:
896 case QTextUndoCommand::BlockDeleted:
897 PMDEBUG(
" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
898 insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
899 resetBlockRevision += 1;
900 if (c.command == QTextUndoCommand::BlockRemoved)
901 c.command = QTextUndoCommand::BlockInserted;
903 c.command = QTextUndoCommand::BlockAdded;
904 if (editPos != (
int)c.pos)
909 case QTextUndoCommand::CharFormatChanged: {
910 resetBlockRevision = -1;
911 PMDEBUG(
" charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
912 FragmentIterator it = find(c.pos);
913 Q_ASSERT(!it.atEnd());
915 int oldFormat = it.value()->format;
916 setCharFormat(c.pos, c.length, formats.charFormat(c.format));
917 c.format = oldFormat;
918 if (editPos != (
int)c.pos)
921 editLength += c.length;
924 case QTextUndoCommand::BlockFormatChanged: {
925 resetBlockRevision = -1;
926 PMDEBUG(
" blockformat: format %d pos %d", c.format, c.pos);
927 QTextBlock it = blocksFind(c.pos);
928 Q_ASSERT(it.isValid());
930 int oldFormat = block(it)->format;
931 block(it)->format = c.format;
932 QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
933 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
934 c.format = oldFormat;
935 if (group != oldGroup) {
937 oldGroup->blockRemoved(it);
939 group->blockInserted(it);
941 group->blockFormatChanged(it);
943 documentChange(it.position(), it.length());
947 case QTextUndoCommand::GroupFormatChange: {
948 resetBlockRevision = -1;
949 PMDEBUG(
" group format change");
950 QTextObject *object = objectForIndex(c.objectIndex);
951 int oldFormat = formats.objectFormatIndex(c.objectIndex);
952 changeObjectFormat(object, c.format);
953 c.format = oldFormat;
957 case QTextUndoCommand::CursorMoved:
961 case QTextUndoCommand::Custom:
962 resetBlockRevision = -1;
973 if (resetBlockRevision >= 0) {
974 int b = blocks.findNode(resetBlockRevision);
975 QTextBlockData *B = blocks.fragment(b);
976 B->revision = c.revision;
984 && undoState < undoStack.size()
985 && undoStack.at(undoState).block_part
986 && undoStack.at(undoState - 1).block_part
987 && !undoStack.at(undoState - 1).block_end
994 int newCursorPos = -1;
997 newCursorPos = editPos + editLength;
998 else if (docChangeFrom >= 0)
999 newCursorPos= qMin(docChangeFrom + docChangeLength, length() - 1);
1002 emitUndoAvailable(isUndoAvailable());
1003 emitRedoAvailable(isRedoAvailable());
1005 return newCursorPos;
1009
1010
1011void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
1019 c.command = QTextUndoCommand::Custom;
1020 c.block_part = editBlock != 0;
1022 c.operation = QTextUndoCommand::MoveCursor;
1032void QTextDocumentPrivate::appendUndoItem(
const QTextUndoCommand &c)
1034 PMDEBUG(
"appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
1037 if (undoState < undoStack.size())
1038 clearUndoRedoStacks(QTextDocument::RedoStack);
1040 if (editBlock != 0 && editBlockCursorPosition >= 0) {
1041 if (q20::cmp_not_equal(c.pos, editBlockCursorPosition)) {
1044 0, 0, editBlockCursorPosition, 0, 0);
1045 undoStack.append(cc);
1047 editBlockCursorPosition = -1;
1052 if (!undoStack.isEmpty() && modified) {
1053 const int lastIdx = undoState - 1;
1054 const QTextUndoCommand &last = undoStack.at(lastIdx);
1056 if ( (last.block_part && c.block_part && !last.block_end)
1057 || (!c.block_part && !last.block_part)
1058 || (c.command == QTextUndoCommand::Inserted && last.command == c.command && (last.block_part && !c.block_part))) {
1060 if (undoStack[lastIdx].tryMerge(c))
1064 if (modifiedState > undoState)
1066 undoStack.append(c);
1068 emitUndoAvailable(
true);
1069 emitRedoAvailable(
false);
1072 emit document()->undoCommandAdded();
1075void QTextDocumentPrivate::clearUndoRedoStacks(QTextDocument::Stacks stacksToClear,
1078 bool undoCommandsAvailable = undoState != 0;
1079 bool redoCommandsAvailable = undoState != undoStack.size();
1080 if (stacksToClear == QTextDocument::UndoStack && undoCommandsAvailable) {
1081 for (
int i = 0; i < undoState; ++i) {
1082 QTextUndoCommand c = undoStack.at(i);
1083 if (c.command & QTextUndoCommand::Custom)
1086 undoStack.remove(0, undoState);
1089 emitUndoAvailable(
false);
1090 }
else if (stacksToClear == QTextDocument::RedoStack
1091 && redoCommandsAvailable) {
1092 for (
int i = undoState; i < undoStack.size(); ++i) {
1093 QTextUndoCommand c = undoStack.at(i);
1094 if (c.command & QTextUndoCommand::Custom)
1097 undoStack.resize(undoState);
1099 emitRedoAvailable(
false);
1100 }
else if (stacksToClear == QTextDocument::UndoAndRedoStacks
1101 && !undoStack.isEmpty()) {
1102 for (
int i = 0; i < undoStack.size(); ++i) {
1103 QTextUndoCommand c = undoStack.at(i);
1104 if (c.command & QTextUndoCommand::Custom)
1109 if (emitSignals && undoCommandsAvailable)
1110 emitUndoAvailable(
false);
1111 if (emitSignals && redoCommandsAvailable)
1112 emitRedoAvailable(
false);
1116void QTextDocumentPrivate::emitUndoAvailable(
bool available)
1118 if (available != wasUndoAvailable) {
1120 emit q->undoAvailable(available);
1121 wasUndoAvailable = available;
1125void QTextDocumentPrivate::emitRedoAvailable(
bool available)
1127 if (available != wasRedoAvailable) {
1129 emit q->redoAvailable(available);
1130 wasRedoAvailable = available;
1134void QTextDocumentPrivate::enableUndoRedo(
bool enable)
1136 if (enable && maximumBlockCount > 0)
1141 clearUndoRedoStacks(QTextDocument::RedoStack);
1142 emitUndoAvailable(
false);
1143 emitRedoAvailable(
false);
1145 modifiedState = modified ? -1 : undoState;
1146 undoEnabled = enable;
1148 compressPieceTable();
1151void QTextDocumentPrivate::joinPreviousEditBlock()
1155 if (undoEnabled && undoState)
1156 undoStack[undoState - 1].block_end =
false;
1159void QTextDocumentPrivate::endEditBlock()
1161 Q_ASSERT(editBlock > 0);
1165 if (undoEnabled && undoState > 0) {
1166 const bool wasBlocking = !undoStack.at(undoState - 1).block_end;
1167 if (undoStack.at(undoState - 1).block_part) {
1168 undoStack[undoState - 1].block_end =
true;
1170 emit document()->undoCommandAdded();
1174 editBlockCursorPosition = -1;
1179void QTextDocumentPrivate::finishEdit()
1187 scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
1189 if (lout && docChangeFrom >= 0) {
1190 if (!inContentsChange) {
1191 QScopedValueRollback<
bool> bg(inContentsChange,
true);
1192 emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
1194 lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
1199 if (needsEnsureMaximumBlockCount) {
1200 needsEnsureMaximumBlockCount =
false;
1201 if (ensureMaximumBlockCount()) {
1210 QList<QTextCursor> changedCursors;
1211 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
1212 if (curs->changed) {
1213 curs->changed =
false;
1214 changedCursors.append(QTextCursor(curs));
1217 for (
const QTextCursor &cursor : std::as_const(changedCursors))
1218 emit q->cursorPositionChanged(cursor);
1222 if (blocks.numNodes() != lastBlockCount) {
1223 lastBlockCount = blocks.numNodes();
1224 emit q->blockCountChanged(lastBlockCount);
1227 if (!undoEnabled && unreachableCharacterCount)
1228 compressPieceTable();
1231void QTextDocumentPrivate::documentChange(
int from,
int length)
1234 if (docChangeFrom < 0) {
1235 docChangeFrom = from;
1236 docChangeOldLength = length;
1237 docChangeLength = length;
1240 int start = qMin(from, docChangeFrom);
1241 int end = qMax(from + length, docChangeFrom + docChangeLength);
1242 int diff = qMax(0, end - start - docChangeLength);
1243 docChangeFrom = start;
1244 docChangeOldLength += diff;
1245 docChangeLength += diff;
1249
1250
1251
1252
1253
1254
1255void QTextDocumentPrivate::adjustDocumentChangesAndCursors(
int from,
int addedOrRemoved, QTextUndoCommand::Operation op)
1260 if (blockCursorAdjustment) {
1263 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
1264 if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
1265 curs->changed =
true;
1271 if (docChangeFrom < 0) {
1272 docChangeFrom = from;
1273 if (addedOrRemoved > 0) {
1274 docChangeOldLength = 0;
1275 docChangeLength = addedOrRemoved;
1277 docChangeOldLength = -addedOrRemoved;
1278 docChangeLength = 0;
1286 int added = qMax(0, addedOrRemoved);
1287 int removed = qMax(0, -addedOrRemoved);
1290 if (from + removed < docChangeFrom)
1291 diff = docChangeFrom - from - removed;
1292 else if (from > docChangeFrom + docChangeLength)
1293 diff = from - (docChangeFrom + docChangeLength);
1295 int overlap_start = qMax(from, docChangeFrom);
1296 int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
1297 int removedInside = qMax(0, overlap_end - overlap_start);
1298 removed -= removedInside;
1301 docChangeFrom = qMin(docChangeFrom, from);
1302 docChangeOldLength += removed + diff;
1303 docChangeLength += added - removedInside + diff;
1309QString QTextDocumentPrivate::plainText()
const
1312 result.resize(length());
1313 const QChar *text_unicode = text.unicode();
1314 QChar *data = result.data();
1315 for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
1316 const QTextFragmentData *f = *it;
1317 ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] *
sizeof(QChar));
1318 data += f->size_array[0];
1325int QTextDocumentPrivate::blockCharFormatIndex(
int node)
const
1327 int pos = blocks.position(node);
1329 return initialBlockCharFormatIndex;
1331 return fragments.find(pos - 1)->format;
1334int QTextDocumentPrivate::nextCursorPosition(
int position, QTextLayout::CursorMode mode)
const
1336 if (position == length()-1)
1339 QTextBlock it = blocksFind(position);
1340 int start = it.position();
1341 int end = start + it.length() - 1;
1342 if (position == end)
1345 return it.layout()->nextCursorPosition(position-start, mode) + start;
1348int QTextDocumentPrivate::previousCursorPosition(
int position, QTextLayout::CursorMode mode)
const
1353 QTextBlock it = blocksFind(position);
1354 int start = it.position();
1355 if (position == start)
1358 return it.layout()->previousCursorPosition(position-start, mode) + start;
1361int QTextDocumentPrivate::leftCursorPosition(
int position)
const
1363 QTextBlock it = blocksFind(position);
1364 int start = it.position();
1365 return it.layout()->leftCursorPosition(position-start) + start;
1368int QTextDocumentPrivate::rightCursorPosition(
int position)
const
1370 QTextBlock it = blocksFind(position);
1371 int start = it.position();
1372 return it.layout()->rightCursorPosition(position-start) + start;
1375void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj,
int format)
1378 int objectIndex = obj->objectIndex();
1379 int oldFormatIndex = formats.objectFormatIndex(objectIndex);
1380 formats.setObjectFormatIndex(objectIndex, format);
1382 QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
1384 b->d_func()->markBlocksDirty();
1386 QTextFrame *f = qobject_cast<QTextFrame *>(obj);
1388 documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
1390 QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, (editBlock != 0), QTextUndoCommand::MoveCursor, oldFormatIndex,
1391 0, 0, obj->d_func()->objectIndex, 0);
1400 const QList<QTextFrame *> children = f->childFrames();
1402 int last = children.size() - 1;
1403 while (first <= last) {
1404 int mid = (first + last) / 2;
1405 QTextFrame *c = children.at(mid);
1406 if (pos > c->lastPosition())
1408 else if (pos < c->firstPosition())
1416QTextFrame *QTextDocumentPrivate::rootFrame()
const
1419 QTextFrameFormat defaultRootFrameFormat;
1420 defaultRootFrameFormat.setMargin(documentMargin);
1421 rtFrame = qobject_cast<QTextFrame *>(
const_cast<QTextDocumentPrivate *>(
this)->createObject(defaultRootFrameFormat));
1426void QTextDocumentPrivate::addCursor(QTextCursorPrivate *c)
1431void QTextDocumentPrivate::removeCursor(QTextCursorPrivate *c)
1436QTextFrame *QTextDocumentPrivate::frameAt(
int pos)
const
1438 QTextFrame *f = rootFrame();
1441 QTextFrame *c = findChildFrame(f, pos);
1448void QTextDocumentPrivate::clearFrame(QTextFrame *f)
1450 for (
int i = 0; i < f->d_func()->childFrames.size(); ++i)
1451 clearFrame(f->d_func()->childFrames.at(i));
1452 f->d_func()->childFrames.clear();
1453 f->d_func()->parentFrame =
nullptr;
1456void QTextDocumentPrivate::scan_frames(
int pos,
int charsRemoved,
int charsAdded)
1460 Q_UNUSED(charsRemoved);
1461 Q_UNUSED(charsAdded);
1463 QTextFrame *f = rootFrame();
1466 for (FragmentIterator it = begin(); it != end(); ++it) {
1468 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
1472 Q_ASSERT(it.size() == 1);
1473 QChar ch = text.at(it->stringPosition);
1478 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1479 frame->d_func()->parentFrame = f;
1480 f->d_func()->childFrames.append(frame);
1484 Q_ASSERT(f == frame);
1485 Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1486 f = frame->d_func()->parentFrame;
1487 }
else if (ch == QChar::ObjectReplacementCharacter) {
1488 Q_ASSERT(f != frame);
1489 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1490 Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1491 frame->d_func()->parentFrame = f;
1492 f->d_func()->childFrames.append(frame);
1497 Q_ASSERT(f == rtFrame);
1498 framesDirty =
false;
1501void QTextDocumentPrivate::insert_frame(QTextFrame *f)
1503 int start = f->firstPosition();
1504 int end = f->lastPosition();
1505 QTextFrame *parent = frameAt(start-1);
1506 Q_ASSERT(parent == frameAt(end+1));
1510 for (
int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
1511 QTextFrame *c = parent->d_func()->childFrames.at(i);
1512 if (start < c->firstPosition() && end > c->lastPosition()) {
1513 parent->d_func()->childFrames.removeAt(i);
1514 f->d_func()->childFrames.append(c);
1515 c->d_func()->parentFrame = f;
1521 for (; i < parent->d_func()->childFrames.size(); ++i) {
1522 QTextFrame *c = parent->d_func()->childFrames.at(i);
1523 if (c->firstPosition() > end)
1526 parent->d_func()->childFrames.insert(i, f);
1527 f->d_func()->parentFrame = parent;
1530QTextFrame *QTextDocumentPrivate::insertFrame(
int start,
int end,
const QTextFrameFormat &format)
1532 Q_ASSERT(start >= 0 && start < length());
1533 Q_ASSERT(end >= 0 && end < length());
1534 Q_ASSERT(start <= end || end == -1);
1536 if (start != end && frameAt(start) != frameAt(end))
1541 QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
1545 int idx = formats.indexForFormat(QTextBlockFormat());
1546 QTextCharFormat cfmt;
1547 cfmt.setObjectIndex(frame->objectIndex());
1548 int charIdx = formats.indexForFormat(cfmt);
1551 insertBlock(
QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
1553 frame->d_func()->fragment_start = find(start).n;
1554 frame->d_func()->fragment_end = find(end).n;
1556 insert_frame(frame);
1563void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
1565 QTextFrame *parent = frame->d_func()->parentFrame;
1569 int start = frame->firstPosition();
1570 int end = frame->lastPosition();
1571 Q_ASSERT(end >= start);
1582QTextObject *QTextDocumentPrivate::objectForIndex(
int objectIndex)
const
1584 if (objectIndex < 0)
1587 QTextObject *object = objects.value(objectIndex,
nullptr);
1589 QTextDocumentPrivate *that =
const_cast<QTextDocumentPrivate *>(
this);
1590 QTextFormat fmt = formats.objectFormat(objectIndex);
1591 object = that->createObject(fmt, objectIndex);
1596QTextObject *QTextDocumentPrivate::objectForFormat(
int formatIndex)
const
1598 int objectIndex = formats.format(formatIndex).objectIndex();
1599 return objectForIndex(objectIndex);
1602QTextObject *QTextDocumentPrivate::objectForFormat(
const QTextFormat &f)
const
1604 return objectForIndex(f.objectIndex());
1607QTextObject *QTextDocumentPrivate::createObject(
const QTextFormat &f,
int objectIndex)
1609 QTextObject *obj = document()->createObject(f);
1612 obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
1613 objects[obj->d_func()->objectIndex] = obj;
1619void QTextDocumentPrivate::deleteObject(QTextObject *object)
1621 const int objIdx = object->d_func()->objectIndex;
1622 objects.remove(objIdx);
1626void QTextDocumentPrivate::contentsChanged()
1632 bool m = undoEnabled ? (modifiedState != undoState) :
true;
1633 if (modified != m) {
1635 emit q->modificationChanged(modified);
1638 emit q->contentsChanged();
1641void QTextDocumentPrivate::compressPieceTable()
1646 const uint garbageCollectionThreshold = 96 * 1024;
1650 bool compressTable = unreachableCharacterCount *
sizeof(QChar) > garbageCollectionThreshold
1651 && text.size() >= text.capacity() * 0.9;
1656 newText.resize(text.size());
1657 QChar *newTextPtr = newText.data();
1660 for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
1661 memcpy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] *
sizeof(QChar));
1662 it->stringPosition = newLen;
1663 newTextPtr += it->size_array[0];
1664 newLen += it->size_array[0];
1667 newText.resize(newLen);
1671 unreachableCharacterCount = 0;
1674void QTextDocumentPrivate::setModified(
bool m)
1682 modifiedState = undoState;
1686 emit q->modificationChanged(modified);
1689bool QTextDocumentPrivate::ensureMaximumBlockCount()
1691 if (maximumBlockCount <= 0)
1693 if (blocks.numNodes() <= maximumBlockCount)
1698 const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
1699 QTextCursor cursor(
this, 0);
1700 cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, blocksToRemove);
1702 unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
1705 QTextCharFormat charFmt = cursor.blockCharFormat();
1706 cursor.removeSelectedText();
1707 cursor.setBlockCharFormat(charFmt);
1711 compressPieceTable();
1717void QTextDocumentPrivate::aboutToRemoveCell(
int from,
int to)
1719 Q_ASSERT(from <= to);
1720 for (QTextCursorPrivate *curs : std::as_const(cursors))
1721 curs->aboutToRemoveCell(from, to);
bool tryMerge(const QTextUndoCommand &other)
#define QTextBeginningOfFrame
static bool isValidBlockSeparator(QChar ch)
static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
static bool noBlockInString(QStringView str)
#define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8)
static QTextFrame * findChildFrame(QTextFrame *f, int pos)