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 QVarLengthArray<QTextCursor, 4> changedCursors;
208 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
209 if (!editBlock && curs->position != 0)
210 changedCursors.append(QTextCursor(curs));
211 curs->setPosition(0);
212 curs->currentCharFormat = -1;
214 curs->adjusted_anchor = 0;
217 QSet<QTextCursorPrivate *> oldCursors = cursors;
221 QMap<
int, QTextObject *>::Iterator objectIt = objects.begin();
222 while (objectIt != objects.end()) {
223 if (*objectIt != rtFrame) {
225 objectIt = objects.erase(objectIt);
235 clearUndoRedoStacks(QTextDocument::UndoAndRedoStacks);
237 unreachableCharacterCount = 0;
241 int len = fragments.length();
244 cachedResources.clear();
248 cursors = oldCursors;
250 QScopedValueRollback<
bool> bg(inContentsChange,
true);
251 emit q->contentsChange(0, len, 0);
254 lout->documentChanged(0, len, 0);
256 cursors = oldCursors;
260 for (
const QTextCursor &cursor : std::as_const(changedCursors))
261 emit q->cursorPositionChanged(cursor);
264QTextDocumentPrivate::~QTextDocumentPrivate()
266 for (QTextCursorPrivate *curs : std::as_const(cursors))
267 curs->priv =
nullptr;
271 clearUndoRedoStacks(QTextDocument::RedoStack);
274void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
279 const bool firstLayout = !lout;
284 for (BlockMap::Iterator it = blocks.begin(); !it.atEnd(); ++it)
287 emit q->documentLayoutChanged();
289 QScopedValueRollback<
bool> bg(inContentsChange,
true);
290 emit q->contentsChange(0, 0, length());
293 lout->documentChanged(0, 0, length());
297void QTextDocumentPrivate::insert_string(
int pos, uint strPos, uint length,
int format, QTextUndoCommand::Operation op)
300 Q_ASSERT(noBlockInString(QStringView{text}.mid(strPos, length)));
303 uint x = fragments.insert_single(pos, length);
304 QTextFragmentData *X = fragments.fragment(x);
306 X->stringPosition = strPos;
307 uint w = fragments.previous(x);
311 int b = blocks.findNode(pos);
312 blocks.setSize(b, blocks.size(b)+length);
314 Q_ASSERT(blocks.length() == fragments.length());
316 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
318 frame->d_func()->fragmentAdded(text.at(strPos), x);
322 adjustDocumentChangesAndCursors(pos, length, op);
325int QTextDocumentPrivate::insert_block(
int pos, uint strPos,
int format,
int blockFormat, QTextUndoCommand::Operation op,
int command)
328 uint x = fragments.insert_single(pos, 1);
329 QTextFragmentData *X = fragments.fragment(x);
331 X->stringPosition = strPos;
334 Q_ASSERT(isValidBlockSeparator(text.at(strPos)));
335 Q_ASSERT(blocks.length()+1 == fragments.length());
338 if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
341 int n = blocks.findNode(block_pos);
342 int key = n ? blocks.position(n) : blocks.length();
344 Q_ASSERT(n || block_pos == blocks.length());
345 if (key != block_pos) {
346 Q_ASSERT(key < block_pos);
347 int oldSize = blocks.size(n);
348 blocks.setSize(n, block_pos-key);
349 size += oldSize - (block_pos-key);
351 int b = blocks.insert_single(block_pos, size);
352 QTextBlockData *B = blocks.fragment(b);
353 B->format = blockFormat;
355 Q_ASSERT(blocks.length() == fragments.length());
357 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
359 group->blockInserted(QTextBlock(
this, b));
360 if (command != QTextUndoCommand::BlockDeleted) {
361 docChangeOldLength--;
366 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
368 frame->d_func()->fragmentAdded(text.at(strPos), x);
372 adjustDocumentChangesAndCursors(pos, 1, op);
376int QTextDocumentPrivate::insertBlock(QChar blockSeparator,
377 int pos,
int blockFormat,
int charFormat, QTextUndoCommand::Operation op)
379 Q_ASSERT(formats.format(blockFormat).isBlockFormat());
380 Q_ASSERT(formats.format(charFormat).isCharFormat());
381 Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
382 Q_ASSERT(isValidBlockSeparator(blockSeparator));
386 int strPos = text.size();
387 text.append(blockSeparator);
389 int ob = blocks.findNode(pos);
390 bool atBlockEnd =
true;
391 bool atBlockStart =
true;
394 atBlockEnd = (pos - blocks.position(ob) == blocks.size(ob)-1);
395 atBlockStart = ((
int)blocks.position(ob) == pos);
396 oldRevision = blocks.fragment(ob)->revision;
399 const int fragment = insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
401 Q_ASSERT(blocks.length() == fragments.length());
403 int b = blocks.findNode(pos);
404 QTextBlockData *B = blocks.fragment(b);
407 op, charFormat, strPos, pos, blockFormat,
411 Q_ASSERT(undoState == undoStack.size());
414 B->revision = (atBlockEnd && !atBlockStart)? oldRevision : revision;
417 B = blocks.fragment(b);
418 B->revision = atBlockStart ? oldRevision : revision;
421 if (formats.charFormat(charFormat).objectIndex() == -1)
422 needsEnsureMaximumBlockCount =
true;
428int QTextDocumentPrivate::insertBlock(
int pos,
int blockFormat,
int charFormat, QTextUndoCommand::Operation op)
430 return insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
433void QTextDocumentPrivate::insert(
int pos,
int strPos,
int strLength,
int format)
438 Q_ASSERT(pos >= 0 && pos < fragments.length());
439 Q_ASSERT(formats.format(format).isCharFormat());
441 insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
443 int b = blocks.findNode(pos);
444 QTextBlockData *B = blocks.fragment(b);
447 QTextUndoCommand::MoveCursor, format, strPos, pos, strLength,
450 B->revision = revision;
451 Q_ASSERT(undoState == undoStack.size());
456void QTextDocumentPrivate::insert(
int pos, QStringView str,
int format)
461 Q_ASSERT(noBlockInString(str));
463 int strPos = text.size();
465 insert(pos, strPos, str.size(), format);
468int QTextDocumentPrivate::remove_string(
int pos, uint length, QTextUndoCommand::Operation op)
471 Q_ASSERT(blocks.length() == fragments.length());
472 Q_ASSERT(q20::cmp_greater_equal(blocks.length(), pos+length));
474 int b = blocks.findNode(pos);
475 uint x = fragments.findNode(pos);
477 Q_ASSERT(blocks.size(b) > length);
478 Q_ASSERT(x && q20::cmp_equal(fragments.position(x), pos) && fragments.size(x) == length);
479 Q_ASSERT(noBlockInString(QStringView{text}.mid(fragments.fragment(x)->stringPosition, length)));
481 blocks.setSize(b, blocks.size(b)-length);
483 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
485 frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
489 const int w = fragments.erase_single(x);
492 unreachableCharacterCount += length;
494 adjustDocumentChangesAndCursors(pos, -
int(length), op);
499int QTextDocumentPrivate::remove_block(
int pos,
int *blockFormat,
int command, QTextUndoCommand::Operation op)
502 Q_ASSERT(blocks.length() == fragments.length());
503 Q_ASSERT(blocks.length() > pos);
505 int b = blocks.findNode(pos);
506 uint x = fragments.findNode(pos);
508 Q_ASSERT(x && (
int)fragments.position(x) == pos);
509 Q_ASSERT(fragments.size(x) == 1);
510 Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));
513 if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
514 Q_ASSERT((
int)blocks.position(b) == pos);
520 int n = blocks.next(b);
521 Q_ASSERT((
int)blocks.position(n) == pos + 1);
522 blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
523 blocks.fragment(b)->userState = blocks.fragment(n)->userState;
526 *blockFormat = blocks.fragment(b)->format;
528 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
530 group->blockRemoved(QTextBlock(
this, b));
532 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
534 frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
538 blocks.erase_single(b);
539 const int w = fragments.erase_single(x);
541 adjustDocumentChangesAndCursors(pos, -1, op);
546#if !defined(QT_NO_DEBUG)
550 if (child == possibleAncestor)
552 child = child->parentFrame();
558void QTextDocumentPrivate::move(
int pos,
int to,
int length, QTextUndoCommand::Operation op)
560 Q_ASSERT(to <= fragments.length() && to <= pos);
561 Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
562 Q_ASSERT(blocks.length() == fragments.length());
567 const bool needsInsert = to != -1;
569#if !defined(QT_NO_DEBUG)
570 const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
572 const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
573 && text.at(find(pos + length - 1)->stringPosition) ==
QTextEndOfFrame);
575 const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
578 && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
580 const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
581 && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));
583 Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
589 uint dst = needsInsert ? fragments.findNode(to) : 0;
590 uint dstKey = needsInsert ? fragments.position(dst) : 0;
592 uint x = fragments.findNode(pos);
593 uint end = fragments.findNode(pos+length);
597 uint n = fragments.next(x);
599 uint key = fragments.position(x);
600 uint b = blocks.findNode(key+1);
601 QTextBlockData *B = blocks.fragment(b);
602 int blockRevision = B->revision;
604 QTextFragmentData *X = fragments.fragment(x);
606 op, X->format, X->stringPosition, key, X->size_array[0],
609 op, X->format, X->stringPosition, dstKey, X->size_array[0],
612 if (key+1 != blocks.position(b)) {
614 Q_ASSERT(noBlockInString(QStringView{text}.mid(X->stringPosition, X->size_array[0])));
615 w = remove_string(key, X->size_array[0], op);
618 insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
619 dstKey += X->size_array[0];
623 Q_ASSERT(X->size_array[0] == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
624 b = blocks.previous(b);
626 c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
627 w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
630 insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
631 cInsert.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockAdded : QTextUndoCommand::BlockInserted;
632 cInsert.blockFormat = c.blockFormat;
637 B->revision = revision;
641 appendUndoItem(cInsert);
646 Q_ASSERT(blocks.length() == fragments.length());
648 if (!blockCursorAdjustment)
652void QTextDocumentPrivate::remove(
int pos,
int length, QTextUndoCommand::Operation op)
656 blockCursorAdjustment =
true;
657 move(pos, -1, length, op);
658 blockCursorAdjustment =
false;
659 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
660 if (curs->adjustPosition(pos, -length, op) == QTextCursorPrivate::CursorMoved) {
661 curs->changed =
true;
667void QTextDocumentPrivate::setCharFormat(
int pos,
int length,
const QTextCharFormat &newFormat, FormatChangeMode mode)
671 Q_ASSERT(newFormat.isValid());
673 int newFormatIdx = -1;
674 if (mode == SetFormatAndPreserveObjectIndices) {
675 QTextCharFormat cleanFormat = newFormat;
676 cleanFormat.clearProperty(QTextFormat::ObjectIndex);
677 newFormatIdx = formats.indexForFormat(cleanFormat);
678 }
else if (mode == SetFormat) {
679 newFormatIdx = formats.indexForFormat(newFormat);
683 if (mode == MergeFormat) {
684 QTextFormat format = formats.format(initialBlockCharFormatIndex);
685 format.merge(newFormat);
686 initialBlockCharFormatIndex = formats.indexForFormat(format);
687 }
else if (mode == SetFormatAndPreserveObjectIndices
688 && formats.format(initialBlockCharFormatIndex).objectIndex() != -1) {
689 QTextCharFormat f = newFormat;
690 f.setObjectIndex(formats.format(initialBlockCharFormatIndex).objectIndex());
691 initialBlockCharFormatIndex = formats.indexForFormat(f);
693 initialBlockCharFormatIndex = newFormatIdx;
700 const int startPos = pos;
701 const int endPos = pos + length;
706 while (pos < endPos) {
707 FragmentMap::Iterator it = fragments.find(pos);
708 Q_ASSERT(!it.atEnd());
710 QTextFragmentData *fragment = it.value();
712 Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
714 int offset = pos - it.position();
715 int length = qMin(endPos - pos,
int(fragment->size_array[0] - offset));
716 int oldFormat = fragment->format;
718 if (mode == MergeFormat) {
719 QTextFormat format = formats.format(fragment->format);
720 format.merge(newFormat);
721 fragment->format = formats.indexForFormat(format);
722 }
else if (mode == SetFormatAndPreserveObjectIndices
723 && formats.format(oldFormat).objectIndex() != -1) {
724 QTextCharFormat f = newFormat;
725 f.setObjectIndex(formats.format(oldFormat).objectIndex());
726 fragment->format = formats.indexForFormat(f);
728 fragment->format = newFormatIdx;
736 Q_ASSERT(q20::cmp_equal(pos, (it.position() + fragment->size_array[0])) || pos >= endPos);
739 int n = fragments.findNode(startPos - 1);
743 n = fragments.findNode(endPos);
747 QTextBlock blockIt = blocksFind(startPos);
748 QTextBlock endIt = blocksFind(endPos);
750 endIt = endIt.next();
751 for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
752 QTextDocumentPrivate::block(blockIt)->invalidate();
754 documentChange(startPos, length);
759void QTextDocumentPrivate::setBlockFormat(
const QTextBlock &from,
const QTextBlock &to,
760 const QTextBlockFormat &newFormat, FormatChangeMode mode)
764 Q_ASSERT(mode != SetFormatAndPreserveObjectIndices);
766 Q_ASSERT(newFormat.isValid());
768 int newFormatIdx = -1;
769 if (mode == SetFormat)
770 newFormatIdx = formats.indexForFormat(newFormat);
771 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
773 QTextBlock it = from;
778 for (; it != end; it = it.next()) {
779 int oldFormat = block(it)->format;
780 QTextBlockFormat format = formats.blockFormat(oldFormat);
781 QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
782 if (mode == MergeFormat) {
783 format.merge(newFormat);
784 newFormatIdx = formats.indexForFormat(format);
785 group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
787 block(it)->format = newFormatIdx;
789 block(it)->invalidate();
791 QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockFormatChanged,
true, QTextUndoCommand::MoveCursor, oldFormat,
792 0, it.position(), 1, 0);
795 if (group != oldGroup) {
797 oldGroup->blockRemoved(it);
799 group->blockInserted(it);
801 group->blockFormatChanged(it);
805 documentChange(from.position(), to.position() + to.length() - from.position());
811bool QTextDocumentPrivate::split(
int pos)
813 uint x = fragments.findNode(pos);
815 int k = fragments.position(x);
821 QTextFragmentData *X = fragments.fragment(x);
822 int oldsize = X->size_array[0];
823 fragments.setSize(x, pos-k);
824 uint n = fragments.insert_single(pos, oldsize-(pos-k));
825 X = fragments.fragment(x);
826 QTextFragmentData *N = fragments.fragment(n);
827 N->stringPosition = X->stringPosition + pos-k;
828 N->format = X->format;
835bool QTextDocumentPrivate::unite(uint f)
837 uint n = fragments.next(f);
841 QTextFragmentData *ff = fragments.fragment(f);
842 QTextFragmentData *nf = fragments.fragment(n);
844 if (nf->format == ff->format && (ff->stringPosition + (
int)ff->size_array[0] == nf->stringPosition)) {
845 if (isValidBlockSeparator(text.at(ff->stringPosition))
846 || isValidBlockSeparator(text.at(nf->stringPosition)))
849 fragments.setSize(f, ff->size_array[0] + nf->size_array[0]);
850 fragments.erase_single(n);
857int QTextDocumentPrivate::undoRedo(
bool undo)
859 PMDEBUG(
"%s, undoState=%d, undoStack size=%d", undo ?
"undo:" :
"redo:", undoState,
int(undoStack.size()));
860 if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
870 QTextUndoCommand &c = undoStack[undoState];
871 int resetBlockRevision = c.pos;
874 case QTextUndoCommand::Inserted:
875 remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
876 PMDEBUG(
" erase: from %d, length %d", c.pos, c.length);
877 c.command = QTextUndoCommand::Removed;
881 case QTextUndoCommand::Removed:
882 PMDEBUG(
" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
883 insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
884 c.command = QTextUndoCommand::Inserted;
885 if (editPos != (
int)c.pos)
888 editLength += c.length;
890 case QTextUndoCommand::BlockInserted:
891 case QTextUndoCommand::BlockAdded:
892 remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
893 PMDEBUG(
" blockremove: from %d", c.pos);
894 if (c.command == QTextUndoCommand::BlockInserted)
895 c.command = QTextUndoCommand::BlockRemoved;
897 c.command = QTextUndoCommand::BlockDeleted;
901 case QTextUndoCommand::BlockRemoved:
902 case QTextUndoCommand::BlockDeleted:
903 PMDEBUG(
" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
904 insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
905 resetBlockRevision += 1;
906 if (c.command == QTextUndoCommand::BlockRemoved)
907 c.command = QTextUndoCommand::BlockInserted;
909 c.command = QTextUndoCommand::BlockAdded;
910 if (editPos != (
int)c.pos)
915 case QTextUndoCommand::CharFormatChanged: {
916 resetBlockRevision = -1;
917 PMDEBUG(
" charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
918 FragmentIterator it = find(c.pos);
919 Q_ASSERT(!it.atEnd());
921 int oldFormat = it.value()->format;
922 setCharFormat(c.pos, c.length, formats.charFormat(c.format));
923 c.format = oldFormat;
924 if (editPos != (
int)c.pos)
927 editLength += c.length;
930 case QTextUndoCommand::BlockFormatChanged: {
931 resetBlockRevision = -1;
932 PMDEBUG(
" blockformat: format %d pos %d", c.format, c.pos);
933 QTextBlock it = blocksFind(c.pos);
934 Q_ASSERT(it.isValid());
936 int oldFormat = block(it)->format;
937 block(it)->format = c.format;
938 QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
939 QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
940 c.format = oldFormat;
941 if (group != oldGroup) {
943 oldGroup->blockRemoved(it);
945 group->blockInserted(it);
947 group->blockFormatChanged(it);
949 documentChange(it.position(), it.length());
953 case QTextUndoCommand::GroupFormatChange: {
954 resetBlockRevision = -1;
955 PMDEBUG(
" group format change");
956 QTextObject *object = objectForIndex(c.objectIndex);
957 int oldFormat = formats.objectFormatIndex(c.objectIndex);
958 changeObjectFormat(object, c.format);
959 c.format = oldFormat;
963 case QTextUndoCommand::CursorMoved:
967 case QTextUndoCommand::Custom:
968 resetBlockRevision = -1;
979 if (resetBlockRevision >= 0) {
980 int b = blocks.findNode(resetBlockRevision);
981 QTextBlockData *B = blocks.fragment(b);
982 B->revision = c.revision;
990 && undoState < undoStack.size()
991 && undoStack.at(undoState).block_part
992 && undoStack.at(undoState - 1).block_part
993 && !undoStack.at(undoState - 1).block_end
1000 int newCursorPos = -1;
1003 newCursorPos = editPos + editLength;
1004 else if (docChangeFrom >= 0)
1005 newCursorPos= qMin(docChangeFrom + docChangeLength, length() - 1);
1008 emitUndoAvailable(isUndoAvailable());
1009 emitRedoAvailable(isRedoAvailable());
1011 return newCursorPos;
1015
1016
1017
1018void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
1026 c.command = QTextUndoCommand::Custom;
1027 c.block_part = editBlock != 0;
1029 c.operation = QTextUndoCommand::MoveCursor;
1039void QTextDocumentPrivate::appendUndoItem(
const QTextUndoCommand &c)
1041 PMDEBUG(
"appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
1044 if (undoState < undoStack.size())
1045 clearUndoRedoStacks(QTextDocument::RedoStack);
1047 if (editBlock != 0 && editBlockCursorPosition >= 0) {
1048 if (q20::cmp_not_equal(c.pos, editBlockCursorPosition)) {
1051 0, 0, editBlockCursorPosition, 0, 0);
1052 undoStack.append(cc);
1054 editBlockCursorPosition = -1;
1059 if (!undoStack.isEmpty() && modified) {
1060 const int lastIdx = undoState - 1;
1061 const QTextUndoCommand &last = undoStack.at(lastIdx);
1063 if ( (last.block_part && c.block_part && !last.block_end)
1064 || (!c.block_part && !last.block_part)
1065 || (c.command == QTextUndoCommand::Inserted && last.command == c.command && (last.block_part && !c.block_part))) {
1067 if (undoStack[lastIdx].tryMerge(c))
1071 if (modifiedState > undoState)
1073 undoStack.append(c);
1075 emitUndoAvailable(
true);
1076 emitRedoAvailable(
false);
1079 emit document()->undoCommandAdded();
1082void QTextDocumentPrivate::clearUndoRedoStacks(QTextDocument::Stacks stacksToClear,
1085 bool undoCommandsAvailable = undoState != 0;
1086 bool redoCommandsAvailable = undoState != undoStack.size();
1087 if (stacksToClear == QTextDocument::UndoStack && undoCommandsAvailable) {
1088 for (
int i = 0; i < undoState; ++i) {
1089 QTextUndoCommand c = undoStack.at(i);
1090 if (c.command & QTextUndoCommand::Custom)
1093 undoStack.remove(0, undoState);
1096 emitUndoAvailable(
false);
1097 }
else if (stacksToClear == QTextDocument::RedoStack
1098 && redoCommandsAvailable) {
1099 for (
int i = undoState; i < undoStack.size(); ++i) {
1100 QTextUndoCommand c = undoStack.at(i);
1101 if (c.command & QTextUndoCommand::Custom)
1104 undoStack.resize(undoState);
1106 emitRedoAvailable(
false);
1107 }
else if (stacksToClear == QTextDocument::UndoAndRedoStacks
1108 && !undoStack.isEmpty()) {
1109 for (
int i = 0; i < undoStack.size(); ++i) {
1110 QTextUndoCommand c = undoStack.at(i);
1111 if (c.command & QTextUndoCommand::Custom)
1116 if (emitSignals && undoCommandsAvailable)
1117 emitUndoAvailable(
false);
1118 if (emitSignals && redoCommandsAvailable)
1119 emitRedoAvailable(
false);
1123void QTextDocumentPrivate::emitUndoAvailable(
bool available)
1125 if (available != wasUndoAvailable) {
1127 emit q->undoAvailable(available);
1128 wasUndoAvailable = available;
1132void QTextDocumentPrivate::emitRedoAvailable(
bool available)
1134 if (available != wasRedoAvailable) {
1136 emit q->redoAvailable(available);
1137 wasRedoAvailable = available;
1141void QTextDocumentPrivate::enableUndoRedo(
bool enable)
1143 if (enable && maximumBlockCount > 0)
1148 clearUndoRedoStacks(QTextDocument::RedoStack);
1149 emitUndoAvailable(
false);
1150 emitRedoAvailable(
false);
1152 modifiedState = modified ? -1 : undoState;
1153 undoEnabled = enable;
1155 compressPieceTable();
1158void QTextDocumentPrivate::joinPreviousEditBlock()
1162 if (undoEnabled && undoState)
1163 undoStack[undoState - 1].block_end =
false;
1166void QTextDocumentPrivate::endEditBlock()
1168 Q_ASSERT(editBlock > 0);
1172 if (undoEnabled && undoState > 0) {
1173 const bool wasBlocking = !undoStack.at(undoState - 1).block_end;
1174 if (undoStack.at(undoState - 1).block_part) {
1175 undoStack[undoState - 1].block_end =
true;
1177 emit document()->undoCommandAdded();
1181 editBlockCursorPosition = -1;
1186void QTextDocumentPrivate::finishEdit()
1194 scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
1196 if (lout && docChangeFrom >= 0) {
1197 if (!inContentsChange) {
1198 QScopedValueRollback<
bool> bg(inContentsChange,
true);
1199 emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
1201 lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
1206 if (needsEnsureMaximumBlockCount) {
1207 needsEnsureMaximumBlockCount =
false;
1208 if (ensureMaximumBlockCount()) {
1217 QList<QTextCursor> changedCursors;
1218 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
1219 if (curs->changed) {
1220 curs->changed =
false;
1221 changedCursors.append(QTextCursor(curs));
1224 for (
const QTextCursor &cursor : std::as_const(changedCursors))
1225 emit q->cursorPositionChanged(cursor);
1229 if (blocks.numNodes() != lastBlockCount) {
1230 lastBlockCount = blocks.numNodes();
1231 emit q->blockCountChanged(lastBlockCount);
1234 if (!undoEnabled && unreachableCharacterCount)
1235 compressPieceTable();
1238void QTextDocumentPrivate::documentChange(
int from,
int length)
1241 if (docChangeFrom < 0) {
1242 docChangeFrom = from;
1243 docChangeOldLength = length;
1244 docChangeLength = length;
1247 int start = qMin(from, docChangeFrom);
1248 int end = qMax(from + length, docChangeFrom + docChangeLength);
1249 int diff = qMax(0, end - start - docChangeLength);
1250 docChangeFrom = start;
1251 docChangeOldLength += diff;
1252 docChangeLength += diff;
1256
1257
1258
1259
1260
1261
1262void QTextDocumentPrivate::adjustDocumentChangesAndCursors(
int from,
int addedOrRemoved, QTextUndoCommand::Operation op)
1267 if (blockCursorAdjustment) {
1270 for (QTextCursorPrivate *curs : std::as_const(cursors)) {
1271 if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
1272 curs->changed =
true;
1278 if (docChangeFrom < 0) {
1279 docChangeFrom = from;
1280 if (addedOrRemoved > 0) {
1281 docChangeOldLength = 0;
1282 docChangeLength = addedOrRemoved;
1284 docChangeOldLength = -addedOrRemoved;
1285 docChangeLength = 0;
1293 int added = qMax(0, addedOrRemoved);
1294 int removed = qMax(0, -addedOrRemoved);
1297 if (from + removed < docChangeFrom)
1298 diff = docChangeFrom - from - removed;
1299 else if (from > docChangeFrom + docChangeLength)
1300 diff = from - (docChangeFrom + docChangeLength);
1302 int overlap_start = qMax(from, docChangeFrom);
1303 int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
1304 int removedInside = qMax(0, overlap_end - overlap_start);
1305 removed -= removedInside;
1308 docChangeFrom = qMin(docChangeFrom, from);
1309 docChangeOldLength += removed + diff;
1310 docChangeLength += added - removedInside + diff;
1316QString QTextDocumentPrivate::plainText()
const
1319 result.resize(length());
1320 const QChar *text_unicode = text.unicode();
1321 QChar *data = result.data();
1322 for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
1323 const QTextFragmentData *f = *it;
1324 ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] *
sizeof(QChar));
1325 data += f->size_array[0];
1332int QTextDocumentPrivate::blockCharFormatIndex(
int node)
const
1334 int pos = blocks.position(node);
1336 return initialBlockCharFormatIndex;
1338 return fragments.find(pos - 1)->format;
1341int QTextDocumentPrivate::nextCursorPosition(
int position, QTextLayout::CursorMode mode)
const
1343 if (position == length()-1)
1346 QTextBlock it = blocksFind(position);
1347 int start = it.position();
1348 int end = start + it.length() - 1;
1349 if (position == end)
1352 return it.layout()->nextCursorPosition(position-start, mode) + start;
1355int QTextDocumentPrivate::previousCursorPosition(
int position, QTextLayout::CursorMode mode)
const
1360 QTextBlock it = blocksFind(position);
1361 int start = it.position();
1362 if (position == start)
1365 return it.layout()->previousCursorPosition(position-start, mode) + start;
1368int QTextDocumentPrivate::leftCursorPosition(
int position)
const
1370 QTextBlock it = blocksFind(position);
1371 int start = it.position();
1372 return it.layout()->leftCursorPosition(position-start) + start;
1375int QTextDocumentPrivate::rightCursorPosition(
int position)
const
1377 QTextBlock it = blocksFind(position);
1378 int start = it.position();
1379 return it.layout()->rightCursorPosition(position-start) + start;
1382void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj,
int format)
1385 int objectIndex = obj->objectIndex();
1386 int oldFormatIndex = formats.objectFormatIndex(objectIndex);
1387 formats.setObjectFormatIndex(objectIndex, format);
1389 QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
1391 b->d_func()->markBlocksDirty();
1393 QTextFrame *f = qobject_cast<QTextFrame *>(obj);
1395 documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
1397 QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, (editBlock != 0), QTextUndoCommand::MoveCursor, oldFormatIndex,
1398 0, 0, obj->d_func()->objectIndex, 0);
1407 const QList<QTextFrame *> children = f->childFrames();
1409 int last = children.size() - 1;
1410 while (first <= last) {
1411 int mid = (first + last) / 2;
1412 QTextFrame *c = children.at(mid);
1413 if (pos > c->lastPosition())
1415 else if (pos < c->firstPosition())
1423QTextFrame *QTextDocumentPrivate::rootFrame()
const
1426 QTextFrameFormat defaultRootFrameFormat;
1427 defaultRootFrameFormat.setMargin(documentMargin);
1428 rtFrame = qobject_cast<QTextFrame *>(
const_cast<QTextDocumentPrivate *>(
this)->createObject(defaultRootFrameFormat));
1433void QTextDocumentPrivate::addCursor(QTextCursorPrivate *c)
1438void QTextDocumentPrivate::removeCursor(QTextCursorPrivate *c)
1443QTextFrame *QTextDocumentPrivate::frameAt(
int pos)
const
1445 QTextFrame *f = rootFrame();
1448 QTextFrame *c = findChildFrame(f, pos);
1455void QTextDocumentPrivate::clearFrame(QTextFrame *f)
1457 for (
int i = 0; i < f->d_func()->childFrames.size(); ++i)
1458 clearFrame(f->d_func()->childFrames.at(i));
1459 f->d_func()->childFrames.clear();
1460 f->d_func()->parentFrame =
nullptr;
1463void QTextDocumentPrivate::scan_frames(
int pos,
int charsRemoved,
int charsAdded)
1467 Q_UNUSED(charsRemoved);
1468 Q_UNUSED(charsAdded);
1470 QTextFrame *f = rootFrame();
1473 for (FragmentIterator it = begin(); it != end(); ++it) {
1475 QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
1479 Q_ASSERT(it.size() == 1);
1480 QChar ch = text.at(it->stringPosition);
1485 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1486 frame->d_func()->parentFrame = f;
1487 f->d_func()->childFrames.append(frame);
1491 Q_ASSERT(f == frame);
1492 Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1493 f = frame->d_func()->parentFrame;
1494 }
else if (ch == QChar::ObjectReplacementCharacter) {
1495 Q_ASSERT(f != frame);
1496 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1497 Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1498 frame->d_func()->parentFrame = f;
1499 f->d_func()->childFrames.append(frame);
1504 Q_ASSERT(f == rtFrame);
1505 framesDirty =
false;
1508void QTextDocumentPrivate::insert_frame(QTextFrame *f)
1510 int start = f->firstPosition();
1511 int end = f->lastPosition();
1512 QTextFrame *parent = frameAt(start-1);
1513 Q_ASSERT(parent == frameAt(end+1));
1517 for (
int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
1518 QTextFrame *c = parent->d_func()->childFrames.at(i);
1519 if (start < c->firstPosition() && end > c->lastPosition()) {
1520 parent->d_func()->childFrames.removeAt(i);
1521 f->d_func()->childFrames.append(c);
1522 c->d_func()->parentFrame = f;
1528 for (; i < parent->d_func()->childFrames.size(); ++i) {
1529 QTextFrame *c = parent->d_func()->childFrames.at(i);
1530 if (c->firstPosition() > end)
1533 parent->d_func()->childFrames.insert(i, f);
1534 f->d_func()->parentFrame = parent;
1537QTextFrame *QTextDocumentPrivate::insertFrame(
int start,
int end,
const QTextFrameFormat &format)
1539 Q_ASSERT(start >= 0 && start < length());
1540 Q_ASSERT(end >= 0 && end < length());
1541 Q_ASSERT(start <= end || end == -1);
1543 if (start != end && frameAt(start) != frameAt(end))
1548 QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
1552 int idx = formats.indexForFormat(QTextBlockFormat());
1553 QTextCharFormat cfmt;
1554 cfmt.setObjectIndex(frame->objectIndex());
1555 int charIdx = formats.indexForFormat(cfmt);
1558 insertBlock(
QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
1560 frame->d_func()->fragment_start = find(start).n;
1561 frame->d_func()->fragment_end = find(end).n;
1563 insert_frame(frame);
1570void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
1572 QTextFrame *parent = frame->d_func()->parentFrame;
1576 int start = frame->firstPosition();
1577 int end = frame->lastPosition();
1578 Q_ASSERT(end >= start);
1589QTextObject *QTextDocumentPrivate::objectForIndex(
int objectIndex)
const
1591 if (objectIndex < 0)
1594 QTextObject *object = objects.value(objectIndex,
nullptr);
1596 QTextDocumentPrivate *that =
const_cast<QTextDocumentPrivate *>(
this);
1597 QTextFormat fmt = formats.objectFormat(objectIndex);
1598 object = that->createObject(fmt, objectIndex);
1603QTextObject *QTextDocumentPrivate::objectForFormat(
int formatIndex)
const
1605 int objectIndex = formats.format(formatIndex).objectIndex();
1606 return objectForIndex(objectIndex);
1609QTextObject *QTextDocumentPrivate::objectForFormat(
const QTextFormat &f)
const
1611 return objectForIndex(f.objectIndex());
1614QTextObject *QTextDocumentPrivate::createObject(
const QTextFormat &f,
int objectIndex)
1616 QTextObject *obj = document()->createObject(f);
1619 obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
1620 objects[obj->d_func()->objectIndex] = obj;
1626void QTextDocumentPrivate::deleteObject(QTextObject *object)
1628 const int objIdx = object->d_func()->objectIndex;
1629 objects.remove(objIdx);
1633void QTextDocumentPrivate::contentsChanged()
1639 bool m = undoEnabled ? (modifiedState != undoState) :
true;
1640 if (modified != m) {
1642 emit q->modificationChanged(modified);
1645 emit q->contentsChanged();
1648void QTextDocumentPrivate::compressPieceTable()
1653 const uint garbageCollectionThreshold = 96 * 1024;
1657 bool compressTable = unreachableCharacterCount *
sizeof(QChar) > garbageCollectionThreshold
1658 && text.size() >= text.capacity() * 0.9;
1663 newText.resize(text.size());
1664 QChar *newTextPtr = newText.data();
1667 for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
1668 memcpy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] *
sizeof(QChar));
1669 it->stringPosition = newLen;
1670 newTextPtr += it->size_array[0];
1671 newLen += it->size_array[0];
1674 newText.resize(newLen);
1678 unreachableCharacterCount = 0;
1681void QTextDocumentPrivate::setModified(
bool m)
1689 modifiedState = undoState;
1693 emit q->modificationChanged(modified);
1696bool QTextDocumentPrivate::ensureMaximumBlockCount()
1698 if (maximumBlockCount <= 0)
1700 if (blocks.numNodes() <= maximumBlockCount)
1705 const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
1706 QTextCursor cursor(
this, 0);
1707 cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, blocksToRemove);
1709 unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
1712 QTextCharFormat charFmt = cursor.blockCharFormat();
1713 cursor.removeSelectedText();
1714 cursor.setBlockCharFormat(charFmt);
1718 compressPieceTable();
1724void QTextDocumentPrivate::aboutToRemoveCell(
int from,
int to)
1726 Q_ASSERT(from <= to);
1727 for (QTextCursorPrivate *curs : std::as_const(cursors))
1728 curs->aboutToRemoveCell(from, to);
bool tryMerge(const QTextUndoCommand &other)
Combined button and popup list for selecting options.
#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)