4#include <private/qtools_p.h>
24#define PMDEBUG if(0) qDebug
27#if !defined(Q_CC_DIAB)
28# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
29 QTextUndoCommand c = { a1, a2, 0, 0, quint8(a3), a4, quint32(a5), quint32(a6), { int(a7) }, quint32(a8) }
31# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
32 QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8
94 return ch == QChar::ParagraphSeparator
147 : wasUndoAvailable(
false),
148 wasRedoAvailable(
false),
149 docChangeOldLength(0),
153 initialBlockCharFormatIndex(-1),
158 editBlockCursorPosition = -1;
182 unreachableCharacterCount = 0;
190 bool undoState = undoEnabled;
194 undoEnabled = undoState;
198 qRegisterMetaType<QTextDocument *>();
206 curs->setPosition(0);
207 curs->currentCharFormat = -1;
209 curs->adjusted_anchor = 0;
212 QSet<QTextCursorPrivate *> oldCursors = cursors;
217 while (objectIt != objects.
end()) {
218 if (*objectIt != rtFrame) {
220 objectIt = objects.
erase(objectIt);
232 unreachableCharacterCount = 0;
239 cachedResources.
clear();
243 cursors = oldCursors;
251 cursors = oldCursors;
259 curs->priv =
nullptr;
271 const bool firstLayout = !lout;
279 emit q->documentLayoutChanged();
298 X->stringPosition = strPos;
310 frame->d_func()->fragmentAdded(text.
at(strPos),
x);
314 adjustDocumentChangesAndCursors(
pos,
length, op);
323 X->stringPosition = strPos;
337 if (
key != block_pos) {
339 int oldSize = blocks.
size(
n);
341 size += oldSize - (block_pos-
key);
345 B->format = blockFormat;
353 docChangeOldLength--;
360 frame->d_func()->fragmentAdded(text.
at(strPos),
x);
364 adjustDocumentChangesAndCursors(
pos, 1, op);
378 int strPos = text.
size();
379 text.
append(blockSeparator);
382 bool atBlockEnd =
true;
383 bool atBlockStart =
true;
399 op, charFormat, strPos,
pos, blockFormat,
406 B->revision = (atBlockEnd && !atBlockStart)? oldRevision : revision;
410 B->revision = atBlockStart ? oldRevision : revision;
413 if (
formats.charFormat(charFormat).objectIndex() == -1)
422 return insertBlock(QChar::ParagraphSeparator,
pos, blockFormat, charFormat, op);
442 B->revision = revision;
455 int strPos = text.
size();
484 unreachableCharacterCount +=
length;
486 adjustDocumentChangesAndCursors(
pos, -
int(
length), op);
533 adjustDocumentChangesAndCursors(
pos, -1, op);
538#if !defined(QT_NO_DEBUG)
542 if (
child == possibleAncestor)
559 const bool needsInsert = to != -1;
561#if !defined(QT_NO_DEBUG)
567 const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
572 const bool isFirstTableCell = (qobject_cast<QTextTable *>(
frameAt(
pos +
length - 1))
575 Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
594 int blockRevision =
B->revision;
598 op, X->format, X->stringPosition,
key, X->size_array[0],
601 op, X->format, X->stringPosition, dstKey, X->size_array[0],
607 w = remove_string(
key, X->size_array[0], op);
610 insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
611 dstKey += X->size_array[0];
624 cInsert.blockFormat =
c.blockFormat;
629 B->revision = revision;
653 curs->changed =
true;
665 int newFormatIdx = -1;
669 newFormatIdx =
formats.indexForFormat(cleanFormat);
671 newFormatIdx =
formats.indexForFormat(newFormat);
678 initialBlockCharFormatIndex =
formats.indexForFormat(
format);
680 &&
formats.format(initialBlockCharFormatIndex).objectIndex() != -1) {
683 initialBlockCharFormatIndex =
formats.indexForFormat(
f);
685 initialBlockCharFormatIndex = newFormatIdx;
692 const int startPos =
pos;
698 while (
pos < endPos) {
708 int oldFormat = fragment->
format;
715 &&
formats.format(oldFormat).objectIndex() != -1) {
720 fragment->
format = newFormatIdx;
731 int n = fragments.
findNode(startPos - 1);
742 endIt = endIt.next();
743 for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
760 int newFormatIdx = -1;
762 newFormatIdx =
formats.indexForFormat(newFormat);
771 int oldFormat =
block(
it)->format;
779 block(
it)->format = newFormatIdx;
784 0,
it.position(), 1, 0);
787 if (
group != oldGroup) {
789 oldGroup->blockRemoved(
it);
803bool QTextDocumentPrivate::split(
int pos)
819 N->stringPosition = X->stringPosition +
pos-k;
820 N->format = X->format;
827bool QTextDocumentPrivate::unite(
uint f)
836 if (nf->format == ff->format && (ff->stringPosition + (
int)ff->size_array[0] == nf->stringPosition)) {
841 fragments.
setSize(
f, ff->size_array[0] + nf->size_array[0]);
851 PMDEBUG(
"%s, undoState=%d, undoStack size=%d",
undo ?
"undo:" :
"redo:", undoState, int(undoStack.
size()));
852 if (!undoEnabled || (
undo && undoState == 0) || (!
undo && undoState == undoStack.
size()))
863 int resetBlockRevision =
c.pos;
868 PMDEBUG(
" erase: from %d, length %d",
c.pos,
c.length);
874 PMDEBUG(
" insert: format %d (from %d, length %d, strpos=%d)",
c.format,
c.pos,
c.length,
c.strPos);
877 if (editPos != (
int)
c.pos)
880 editLength +=
c.length;
885 PMDEBUG(
" blockremove: from %d",
c.pos);
895 PMDEBUG(
" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)",
c.format,
c.blockFormat,
c.pos,
c.strPos);
897 resetBlockRevision += 1;
902 if (editPos != (
int)
c.pos)
908 resetBlockRevision = -1;
909 PMDEBUG(
" charFormat: format %d (from %d, length %d)",
c.format,
c.pos,
c.length);
913 int oldFormat =
it.value()->format;
915 c.format = oldFormat;
916 if (editPos != (
int)
c.pos)
919 editLength +=
c.length;
923 resetBlockRevision = -1;
924 PMDEBUG(
" blockformat: format %d pos %d",
c.format,
c.pos);
928 int oldFormat =
block(
it)->format;
932 c.format = oldFormat;
933 if (
group != oldGroup) {
935 oldGroup->blockRemoved(
it);
946 resetBlockRevision = -1;
947 PMDEBUG(
" group format change");
949 int oldFormat =
formats.objectFormatIndex(
c.objectIndex);
951 c.format = oldFormat;
960 resetBlockRevision = -1;
971 if (resetBlockRevision >= 0) {
972 int b = blocks.
findNode(resetBlockRevision);
974 B->revision =
c.revision;
982 && undoState < undoStack.
size()
992 int newCursorPos = -1;
995 newCursorPos = editPos + editLength;
996 else if (docChangeFrom >= 0)
997 newCursorPos=
qMin(docChangeFrom + docChangeLength,
length() - 1);
1003 return newCursorPos;
1018 c.block_part = editBlock != 0;
1032 PMDEBUG(
"appendUndoItem, command=%d enabled=%d",
c.command, undoEnabled);
1035 if (undoState < undoStack.
size())
1038 if (editBlock != 0 && editBlockCursorPosition >= 0) {
1039 if (
c.pos != (
quint32) editBlockCursorPosition) {
1042 0, 0, editBlockCursorPosition, 0, 0);
1045 editBlockCursorPosition = -1;
1050 if (!undoStack.
isEmpty() && modified) {
1051 const int lastIdx = undoState - 1;
1058 if (undoStack[lastIdx].tryMerge(
c))
1062 if (modifiedState > undoState)
1076 bool undoCommandsAvailable = undoState != 0;
1077 bool redoCommandsAvailable = undoState != undoStack.
size();
1079 for (
int i = 0;
i < undoState; ++
i) {
1084 undoStack.
remove(0, undoState);
1089 && redoCommandsAvailable) {
1090 for (
int i = undoState;
i < undoStack.
size(); ++
i) {
1095 undoStack.
resize(undoState);
1100 for (
int i = 0;
i < undoStack.
size(); ++
i) {
1107 if (emitSignals && undoCommandsAvailable)
1109 if (emitSignals && redoCommandsAvailable)
1116 if (available != wasUndoAvailable) {
1118 emit q->undoAvailable(available);
1119 wasUndoAvailable = available;
1125 if (available != wasRedoAvailable) {
1127 emit q->redoAvailable(available);
1128 wasRedoAvailable = available;
1143 modifiedState = modified ? -1 : undoState;
1146 compressPieceTable();
1153 if (undoEnabled && undoState)
1154 undoStack[undoState - 1].block_end =
false;
1163 if (undoEnabled && undoState > 0) {
1164 const bool wasBlocking = !undoStack.
at(undoState - 1).
block_end;
1166 undoStack[undoState - 1].block_end =
true;
1172 editBlockCursorPosition = -1;
1185 scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
1187 if (lout && docChangeFrom >= 0) {
1190 emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
1192 lout->
documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
1208 QList<QTextCursor> changedCursors;
1210 if (curs->changed) {
1211 curs->changed =
false;
1220 if (blocks.
numNodes() != lastBlockCount) {
1221 lastBlockCount = blocks.
numNodes();
1222 emit q->blockCountChanged(lastBlockCount);
1225 if (!undoEnabled && unreachableCharacterCount)
1226 compressPieceTable();
1232 if (docChangeFrom < 0) {
1233 docChangeFrom = from;
1234 docChangeOldLength =
length;
1235 docChangeLength =
length;
1239 int end =
qMax(from +
length, docChangeFrom + docChangeLength);
1241 docChangeFrom =
start;
1242 docChangeOldLength += diff;
1243 docChangeLength += diff;
1263 curs->changed =
true;
1269 if (docChangeFrom < 0) {
1270 docChangeFrom = from;
1271 if (addedOrRemoved > 0) {
1272 docChangeOldLength = 0;
1273 docChangeLength = addedOrRemoved;
1275 docChangeOldLength = -addedOrRemoved;
1276 docChangeLength = 0;
1284 int added =
qMax(0, addedOrRemoved);
1285 int removed =
qMax(0, -addedOrRemoved);
1288 if (from + removed < docChangeFrom)
1289 diff = docChangeFrom - from - removed;
1290 else if (from > docChangeFrom + docChangeLength)
1291 diff = from - (docChangeFrom + docChangeLength);
1293 int overlap_start =
qMax(from, docChangeFrom);
1294 int overlap_end =
qMin(from + removed, docChangeFrom + docChangeLength);
1295 int removedInside =
qMax(0, overlap_end - overlap_start);
1296 removed -= removedInside;
1299 docChangeFrom =
qMin(docChangeFrom, from);
1300 docChangeOldLength += removed + diff;
1301 docChangeLength += added - removedInside + diff;
1315 ::memcpy(
data, text_unicode +
f->stringPosition,
f->size_array[0] *
sizeof(
QChar));
1316 data +=
f->size_array[0];
1327 return initialBlockCharFormatIndex;
1329 return fragments.
find(
pos - 1)->format;
1376 int objectIndex =
obj->objectIndex();
1377 int oldFormatIndex =
formats.objectFormatIndex(objectIndex);
1382 b->d_func()->markBlocksDirty();
1389 0, 0,
obj->d_func()->objectIndex, 0);
1398 const QList<QTextFrame *> children =
f->childFrames();
1400 int last = children.size() - 1;
1401 while (
first <= last) {
1402 int mid = (
first + last) / 2;
1404 if (
pos >
c->lastPosition())
1406 else if (pos < c->firstPosition())
1446void QTextDocumentPrivate::clearFrame(
QTextFrame *
f)
1448 for (
int i = 0;
i <
f->d_func()->childFrames.size(); ++
i)
1449 clearFrame(
f->d_func()->childFrames.at(
i));
1450 f->d_func()->childFrames.clear();
1451 f->d_func()->parentFrame =
nullptr;
1454void QTextDocumentPrivate::scan_frames(
int pos,
int charsRemoved,
int charsAdded)
1477 frame->d_func()->parentFrame =
f;
1478 f->d_func()->childFrames.append(
frame);
1484 f =
frame->d_func()->parentFrame;
1485 }
else if (
ch == QChar::ObjectReplacementCharacter) {
1489 frame->d_func()->parentFrame =
f;
1490 f->d_func()->childFrames.append(
frame);
1496 framesDirty =
false;
1499void QTextDocumentPrivate::insert_frame(
QTextFrame *
f)
1501 int start =
f->firstPosition();
1502 int end =
f->lastPosition();
1508 for (
int i = 0;
i <
parent->d_func()->childFrames.size(); ++
i) {
1510 if (start < c->firstPosition() &&
end >
c->lastPosition()) {
1511 parent->d_func()->childFrames.removeAt(
i);
1512 f->d_func()->childFrames.append(
c);
1513 c->d_func()->parentFrame =
f;
1519 for (;
i <
parent->d_func()->childFrames.size(); ++
i) {
1521 if (
c->firstPosition() >
end)
1524 parent->d_func()->childFrames.insert(
i,
f);
1525 f->d_func()->parentFrame =
parent;
1546 int charIdx =
formats.indexForFormat(cfmt);
1554 insert_frame(
frame);
1582 if (objectIndex < 0)
1589 object = that->createObject(
fmt, objectIndex);
1610 obj->d_func()->objectIndex = objectIndex == -1 ?
formats.createObjectIndex(
f) : objectIndex;
1611 objects[
obj->d_func()->objectIndex] =
obj;
1619 const int objIdx =
object->d_func()->objectIndex;
1624void QTextDocumentPrivate::contentsChanged()
1630 bool m = undoEnabled ? (modifiedState != undoState) : true;
1631 if (modified !=
m) {
1633 emit q->modificationChanged(modified);
1636 emit q->contentsChanged();
1639void QTextDocumentPrivate::compressPieceTable()
1644 const uint garbageCollectionThreshold = 96 * 1024;
1648 bool compressTable = unreachableCharacterCount *
sizeof(
QChar) > garbageCollectionThreshold
1655 QChar *newTextPtr = newText.data();
1659 memcpy(newTextPtr, text.
constData() +
it->stringPosition,
it->size_array[0] *
sizeof(
QChar));
1660 it->stringPosition = newLen;
1661 newTextPtr +=
it->size_array[0];
1662 newLen +=
it->size_array[0];
1665 newText.resize(newLen);
1669 unreachableCharacterCount = 0;
1680 modifiedState = undoState;
1684 emit q->modificationChanged(modified);
1700 unreachableCharacterCount +=
cursor.selectionEnd() -
cursor.selectionStart();
1704 cursor.removeSelectedText();
1705 cursor.setBlockCharFormat(charFmt);
1709 compressPieceTable();
1719 curs->aboutToRemoveCell(from, to);
virtual void documentChanged(int from, int charsRemoved, int charsAdded)=0
This function is called whenever the contents of the document change.
uint position(uint node, uint field=0) const
uint insert_single(int key, uint length)
void setSize(uint node, int new_size, uint field=0)
uint previous(uint n) const
Iterator find(int k, uint field=0)
uint erase_single(uint f)
Fragment * fragment(uint index)
uint findNode(int k, uint field=0) const
uint size(uint node, uint field=0) const
int length(uint field=0) const
qsizetype size() const noexcept
bool isEmpty() const noexcept
const_reference at(qsizetype i) const noexcept
void remove(qsizetype i, qsizetype n=1)
void resize(qsizetype size)
void append(parameter_type t)
T value(const Key &key, const T &defaultValue=T()) const
iterator erase(const_iterator it)
size_type remove(const Key &key)
bool remove(const T &value)
iterator insert(const T &value)
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString mid(qsizetype position, qsizetype n=-1) const &
void clear()
Clears the contents of the string and makes it null.
const QChar * constData() const
Returns a pointer to the data stored in the QString.
qsizetype size() const noexcept
Returns the number of characters in this string.
qsizetype capacity() const
Returns the maximum number of characters that can be stored in the string without forcing a reallocat...
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
QString & append(QChar c)
const QChar * unicode() const
Returns a Unicode representation of the string.
void resize(qsizetype size)
Sets the size of the string to size characters.
int length() const
Returns the length of the block in characters.
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
int position() const
Returns the index of the block's first character within the document.
\reentrant \inmodule QtGui
QTextObject * objectForIndex(int objectIndex) const
QAbstractTextDocumentLayout * layout() const
void documentChange(int from, int length)
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
QTextFrame * frameAt(int pos) const
void insert(int pos, QStringView text, int format)
void remove(int pos, int length, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
int rightCursorPosition(int position) const
QTextFrame * insertFrame(int start, int end, const QTextFrameFormat &format)
void appendUndoItem(QAbstractUndoItem *)
Appends a custom undo item to the undo stack.
QTextFrame * rootFrame() const
void removeCursor(QTextCursorPrivate *c)
int blockCharFormatIndex(int node) const
int nextCursorPosition(int position, QTextLayout::CursorMode mode) const
void emitUndoAvailable(bool available)
QTextDocument * document()
static const QTextBlockData * block(const QTextBlock &it)
void deleteObject(QTextObject *object)
bool ensureMaximumBlockCount()
QTextObject * createObject(const QTextFormat &newFormat, int objectIndex=-1)
void changeObjectFormat(QTextObject *group, int format)
void move(int from, int to, int length, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
void clearUndoRedoStacks(QTextDocument::Stacks stacksToClear, bool emitSignals=false)
void enableUndoRedo(bool enable)
FragmentIterator begin() const
void removeFrame(QTextFrame *frame)
FragmentMap::ConstIterator FragmentIterator
void setLayout(QAbstractTextDocumentLayout *layout)
@ SetFormatAndPreserveObjectIndices
void setBlockFormat(const QTextBlock &from, const QTextBlock &to, const QTextBlockFormat &newFormat, FormatChangeMode mode=SetFormat)
FragmentIterator find(int pos) const
bool isUndoAvailable() const
Qt::CursorMoveStyle defaultCursorMoveStyle
void joinPreviousEditBlock()
QTextObject * objectForFormat(int formatIndex) const
uint blockCursorAdjustment
int leftCursorPosition(int position) const
QTextOption defaultTextOption
FragmentIterator end() const
void setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode=SetFormat)
uint needsEnsureMaximumBlockCount
QTextBlock blocksFind(int pos) const
QString plainText() const
void aboutToRemoveCell(int cursorFrom, int cursorEnd)
This method is called from QTextTable when it is about to remove a table-cell to allow cursors to upd...
bool isRedoAvailable() const
int previousCursorPosition(int position, QTextLayout::CursorMode mode) const
void emitRedoAvailable(bool available)
void addCursor(QTextCursorPrivate *c)
\reentrant \inmodule QtGui
Stacks
\value UndoStack The undo stack.
virtual QTextObject * createObject(const QTextFormat &f)
Creates and returns a new document object (a QTextObject), based on the given format.
int objectIndex() const
Returns the index of the format object, or -1 if the format object is invalid.
void setObjectIndex(int object)
Sets the format object's object index.
void clearProperty(int propertyId)
Clears the value of the property given by propertyId.
void merge(const QTextFormat &other)
Merges the other format with this format; where there are conflicts the other format takes precedence...
void setMargin(qreal margin)
Sets the frame's margin in pixels.
QTextFrame * parentFrame() const
Returns the frame's parent frame.
QTextEngine * engine() const
CursorMode
\value SkipCharacters \value SkipWords
QTextFormat format() const
Returns the text object's format.
void setWrapMode(WrapMode wrap)
Sets the option's text wrap mode to the given mode.
@ WrapAtWordBoundaryOrAnywhere
void setTabStopDistance(qreal tabStopDistance)
QAbstractUndoItem * custom
bool tryMerge(const QTextUndoCommand &other)
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLint GLsizei GLsizei GLenum format
GLdouble GLdouble GLdouble GLdouble q
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define QStringLiteral(str)
#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)
QVideoFrameFormat::PixelFormat fmt