summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qtextdocument_p.cpp
blob: 227cbae2952a21e6ba9237966cdca2bc1a7a09d6 (plain)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724
// Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include <private/qtools_p.h>#include <qdebug.h>#include <qscopedvaluerollback.h>#include"qtextdocument_p.h"#include"qtextdocument.h"#include <qtextformat.h>#include"qtextformat_p.h"#include"qtextobject_p.h"#include"qtextcursor.h"#include"qtextimagehandler_p.h"#include"qtextcursor_p.h"#include"qtextdocumentlayout_p.h"#include"qtexttable.h"#include"qtextengine_p.h"#include <QtCore/q20utility.h>#include <stdlib.h> QT_BEGIN_NAMESPACE #define PMDEBUG if(0) qDebug// The VxWorks DIAB compiler crashes when initializing the anonymous union with { a7 }#if !defined(Q_CC_DIAB)# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ QTextUndoCommand c = { a1, a2, 0, 0, quint8(a3), a4, quint32(a5), quint32(a6), { int(a7) }, quint32(a8) }#else# define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \ QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8#endif/* Structure of a document: DOCUMENT :== FRAME_CONTENTS FRAME :== START_OF_FRAME FRAME_CONTENTS END_OF_FRAME FRAME_CONTENTS = LIST_OF_BLOCKS ((FRAME | TABLE) LIST_OF_BLOCKS)* TABLE :== (START_OF_FRAME TABLE_CELL)+ END_OF_FRAME TABLE_CELL = FRAME_CONTENTS LIST_OF_BLOCKS :== (BLOCK END_OF_PARA)* BLOCK BLOCK :== (FRAGMENT)* FRAGMENT :== String of characters END_OF_PARA :== 0x2029 # Paragraph separator in Unicode START_OF_FRAME :== 0xfdd0 END_OF_FRAME := 0xfdd1 Note also that LIST_OF_BLOCKS can be empty. Nevertheless, there is at least one valid cursor position there where you could start typing. The block format is in this case determined by the last END_OF_PARA/START_OF_FRAME/END_OF_FRAME (see below). Lists are not in here, as they are treated specially. A list is just a collection of (not necessarily connected) blocks, that share the same objectIndex() in the format that refers to the list format and object. The above does not clearly note where formats are. Here's how it looks currently: FRAGMENT: one charFormat associated END_OF_PARA: one charFormat, and a blockFormat for the _next_ block. START_OF_FRAME: one char format, and a blockFormat (for the next block). The format associated with the objectIndex() of the charFormat decides whether this is a frame or table and its properties END_OF_FRAME: one charFormat and a blockFormat (for the next block). The object() of the charFormat is the same as for the corresponding START_OF_BLOCK. The document is independent of the layout with certain restrictions: * Cursor movement (esp. up and down) depend on the layout. * You cannot have more than one layout, as the layout data of QTextObjects is stored in the text object itself.*/voidQTextBlockData::invalidate()const{if(layout) layout->engine()->invalidate();}static boolisValidBlockSeparator(QChar ch){return ch ==QChar::ParagraphSeparator || ch == QTextBeginningOfFrame || ch == QTextEndOfFrame;}static boolnoBlockInString(QStringView str){return!str.contains(QChar::ParagraphSeparator)&& !str.contains(QTextBeginningOfFrame)&& !str.contains(QTextEndOfFrame);}boolQTextUndoCommand::tryMerge(const QTextUndoCommand &other){if(command != other.command)return false;if(command == Inserted && (pos + length == other.pos)&& (strPos + length == other.strPos)&& format == other.format) { length += other.length;return true;}// removal to the 'right' using 'Delete' keyif(command == Removed && pos == other.pos && (strPos + length == other.strPos)&& format == other.format) { length += other.length;return true;}// removal to the 'left' using 'Backspace'if(command == Removed && (other.pos + other.length == pos)&& (other.strPos + other.length == strPos)&& (format == other.format)) {int l = length;(*this) = other; length += l;return true;}return false;}QTextDocumentPrivate::QTextDocumentPrivate():wasUndoAvailable(false),wasRedoAvailable(false),docChangeOldLength(0),docChangeLength(0),framesDirty(true),rtFrame(nullptr),initialBlockCharFormatIndex(-1),// set correctly later in init()resourceProvider(nullptr),cssMedia(QStringLiteral("screen")){ editBlock =0; editBlockCursorPosition = -1; docChangeFrom = -1; undoState =0; revision = -1;// init() inserts a block, bringing it to 0 lout =nullptr; modified =false; modifiedState =0; undoEnabled =true; inContentsChange =false; blockCursorAdjustment =false; defaultTextOption.setTabStopDistance(80);// same as in qtextengine.cpp defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); defaultCursorMoveStyle =Qt::LogicalMoveStyle; indentWidth =40; documentMargin =4; maximumBlockCount =0; needsEnsureMaximumBlockCount =false; unreachableCharacterCount =0; lastBlockCount =0;}voidQTextDocumentPrivate::init(){ framesDirty =false;bool undoState = undoEnabled; undoEnabled =false; initialBlockCharFormatIndex = formats.indexForFormat(QTextCharFormat());insertBlock(0, formats.indexForFormat(QTextBlockFormat()), formats.indexForFormat(QTextCharFormat())); undoEnabled = undoState; modified =false; modifiedState =0; qRegisterMetaType<QTextDocument *>();}voidQTextDocumentPrivate::clear(){Q_Q(QTextDocument);for(QTextCursorPrivate *curs :std::as_const(cursors)) { curs->setPosition(0); curs->currentCharFormat = -1; curs->anchor =0; curs->adjusted_anchor =0;} QSet<QTextCursorPrivate *> oldCursors = cursors; QT_TRY{ cursors.clear(); QMap<int, QTextObject *>::Iterator objectIt = objects.begin();while(objectIt != objects.end()) {if(*objectIt != rtFrame) {delete*objectIt; objectIt = objects.erase(objectIt);}else{++objectIt;}}// also clear out the remaining root frame pointer// (we're going to delete the object further down) objects.clear(); title.clear();clearUndoRedoStacks(QTextDocument::UndoAndRedoStacks); text =QString(); unreachableCharacterCount =0; modifiedState =0; modified =false; formats.clear();int len = fragments.length(); fragments.clear(); blocks.clear(); cachedResources.clear();delete rtFrame; rtFrame =nullptr;init(); cursors = oldCursors;{ QScopedValueRollback<bool>bg(inContentsChange,true); emit q->contentsChange(0, len,0);}if(lout) lout->documentChanged(0, len,0);}QT_CATCH(...) { cursors = oldCursors;// at least recover the cursors QT_RETHROW;}}QTextDocumentPrivate::~QTextDocumentPrivate(){for(QTextCursorPrivate *curs :std::as_const(cursors)) curs->priv =nullptr; cursors.clear(); undoState =0; undoEnabled =true;clearUndoRedoStacks(QTextDocument::RedoStack);}voidQTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout){Q_Q(QTextDocument);if(lout == layout)return;const bool firstLayout = !lout;delete lout; lout = layout;if(!firstLayout)for(BlockMap::Iterator it = blocks.begin(); !it.atEnd(); ++it) it->free(); emit q->documentLayoutChanged();{ QScopedValueRollback<bool>bg(inContentsChange,true); emit q->contentsChange(0,0,length());}if(lout) lout->documentChanged(0,0,length());}voidQTextDocumentPrivate::insert_string(int pos, uint strPos, uint length,int format,QTextUndoCommand::Operation op){// ##### optimize when only appending to the fragment!Q_ASSERT(noBlockInString(QStringView{text}.mid(strPos, length)));split(pos); uint x = fragments.insert_single(pos, length); QTextFragmentData *X = fragments.fragment(x); X->format = format; X->stringPosition = strPos; uint w = fragments.previous(x);if(w)unite(w);int b = blocks.findNode(pos); blocks.setSize(b, blocks.size(b)+length);Q_ASSERT(blocks.length() == fragments.length()); QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));if(frame) { frame->d_func()->fragmentAdded(text.at(strPos), x); framesDirty =true;}adjustDocumentChangesAndCursors(pos, length, op);}intQTextDocumentPrivate::insert_block(int pos, uint strPos,int format,int blockFormat,QTextUndoCommand::Operation op,int command){split(pos); uint x = fragments.insert_single(pos,1); QTextFragmentData *X = fragments.fragment(x); X->format = format; X->stringPosition = strPos;// no need trying to unite, since paragraph separators are always in a fragment of their ownQ_ASSERT(isValidBlockSeparator(text.at(strPos)));Q_ASSERT(blocks.length()+1== fragments.length());int block_pos = pos;if(blocks.length() && command ==QTextUndoCommand::BlockRemoved)++block_pos;int size =1;int n = blocks.findNode(block_pos);int key = n ? blocks.position(n) : blocks.length();Q_ASSERT(n || block_pos == blocks.length());if(key != block_pos) {Q_ASSERT(key < block_pos);int oldSize = blocks.size(n); blocks.setSize(n, block_pos-key); size += oldSize - (block_pos-key);}int b = blocks.insert_single(block_pos, size); QTextBlockData *B = blocks.fragment(b); B->format = blockFormat;Q_ASSERT(blocks.length() == fragments.length()); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));if(group) { group->blockInserted(QTextBlock(this, b));if(command !=QTextUndoCommand::BlockDeleted) { docChangeOldLength--; docChangeLength--;}} QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));if(frame) { frame->d_func()->fragmentAdded(text.at(strPos), x); framesDirty =true;}adjustDocumentChangesAndCursors(pos,1, op);return x;}intQTextDocumentPrivate::insertBlock(QChar blockSeparator,int pos,int blockFormat,int charFormat,QTextUndoCommand::Operation op){Q_ASSERT(formats.format(blockFormat).isBlockFormat());Q_ASSERT(formats.format(charFormat).isCharFormat());Q_ASSERT(pos >=0&& (pos < fragments.length() || (pos ==0&& fragments.length() ==0)));Q_ASSERT(isValidBlockSeparator(blockSeparator));beginEditBlock();int strPos = text.size(); text.append(blockSeparator);int ob = blocks.findNode(pos);bool atBlockEnd =true;bool atBlockStart =true;int oldRevision =0;if(ob) { atBlockEnd = (pos - blocks.position(ob) == blocks.size(ob)-1); atBlockStart = ((int)blocks.position(ob) == pos); oldRevision = blocks.fragment(ob)->revision;}const int fragment =insert_block(pos, strPos, charFormat, blockFormat, op,QTextUndoCommand::BlockRemoved);Q_ASSERT(blocks.length() == fragments.length());int b = blocks.findNode(pos); QTextBlockData *B = blocks.fragment(b);QT_INIT_TEXTUNDOCOMMAND(c,QTextUndoCommand::BlockInserted, (editBlock !=0), op, charFormat, strPos, pos, blockFormat, B->revision);appendUndoItem(c);Q_ASSERT(undoState == undoStack.size());// update revision numbers of the modified blocks. B->revision = (atBlockEnd && !atBlockStart)? oldRevision : revision; b = blocks.next(b);if(b) { B = blocks.fragment(b); B->revision = atBlockStart ? oldRevision : revision;}if(formats.charFormat(charFormat).objectIndex() == -1) needsEnsureMaximumBlockCount =true;endEditBlock();return fragment;}intQTextDocumentPrivate::insertBlock(int pos,int blockFormat,int charFormat,QTextUndoCommand::Operation op){returninsertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);}voidQTextDocumentPrivate::insert(int pos,int strPos,int strLength,int format){if(strLength <=0)return;Q_ASSERT(pos >=0&& pos < fragments.length());Q_ASSERT(formats.format(format).isCharFormat());insert_string(pos, strPos, strLength, format,QTextUndoCommand::MoveCursor);if(undoEnabled) {int b = blocks.findNode(pos); QTextBlockData *B = blocks.fragment(b);QT_INIT_TEXTUNDOCOMMAND(c,QTextUndoCommand::Inserted, (editBlock !=0),QTextUndoCommand::MoveCursor, format, strPos, pos, strLength, B->revision);appendUndoItem(c); B->revision = revision;Q_ASSERT(undoState == undoStack.size());}finishEdit();}voidQTextDocumentPrivate::insert(int pos, QStringView str,int format){if(str.size() ==0)return;Q_ASSERT(noBlockInString(str));int strPos = text.size(); text.append(str);insert(pos, strPos, str.size(), format);}intQTextDocumentPrivate::remove_string(int pos, uint length,QTextUndoCommand::Operation op){Q_ASSERT(pos >=0);Q_ASSERT(blocks.length() == fragments.length());Q_ASSERT(q20::cmp_greater_equal(blocks.length(), pos+length));int b = blocks.findNode(pos); uint x = fragments.findNode(pos);Q_ASSERT(blocks.size(b) > length);Q_ASSERT(x &&q20::cmp_equal(fragments.position(x), pos) && fragments.size(x) == length);Q_ASSERT(noBlockInString(QStringView{text}.mid(fragments.fragment(x)->stringPosition, length))); blocks.setSize(b, blocks.size(b)-length); QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));if(frame) { frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x); framesDirty =true;}const int w = fragments.erase_single(x);if(!undoEnabled) unreachableCharacterCount += length;adjustDocumentChangesAndCursors(pos, -int(length), op);return w;}intQTextDocumentPrivate::remove_block(int pos,int*blockFormat,int command,QTextUndoCommand::Operation op){Q_ASSERT(pos >=0);Q_ASSERT(blocks.length() == fragments.length());Q_ASSERT(blocks.length() > pos);int b = blocks.findNode(pos); uint x = fragments.findNode(pos);Q_ASSERT(x && (int)fragments.position(x) == pos);Q_ASSERT(fragments.size(x) ==1);Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));Q_ASSERT(b);if(blocks.size(b) ==1&& command ==QTextUndoCommand::BlockAdded) {Q_ASSERT((int)blocks.position(b) == pos);// qDebug("removing empty block");// empty block remove the block itself}else{// non empty block, merge with next one into this block// qDebug("merging block with next");int n = blocks.next(b);Q_ASSERT((int)blocks.position(n) == pos +1); blocks.setSize(b, blocks.size(b) + blocks.size(n) -1); blocks.fragment(b)->userState = blocks.fragment(n)->userState; b = n;}*blockFormat = blocks.fragment(b)->format; QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));if(group) group->blockRemoved(QTextBlock(this, b)); QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));if(frame) { frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x); framesDirty =true;} blocks.erase_single(b);const int w = fragments.erase_single(x);adjustDocumentChangesAndCursors(pos, -1, op);return w;}#if !defined(QT_NO_DEBUG)static boolisAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child){while(child) {if(child == possibleAncestor)return true; child = child->parentFrame();}return false;}#endifvoidQTextDocumentPrivate::move(int pos,int to,int length,QTextUndoCommand::Operation op){Q_ASSERT(to <= fragments.length() && to <= pos);Q_ASSERT(pos >=0&& pos+length <= fragments.length());Q_ASSERT(blocks.length() == fragments.length());if(pos == to)return;const bool needsInsert = to != -1;#if !defined(QT_NO_DEBUG)const bool startAndEndInSameFrame = (frameAt(pos) ==frameAt(pos + length -1));const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos),frameAt(pos + length -1))&& text.at(find(pos + length -1)->stringPosition) == QTextEndOfFrame);const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame && text.at(find(pos + length -1)->stringPosition) == QTextEndOfFrame &&frameAt(pos)->parentFrame() ==frameAt(pos + length -1)->parentFrame());const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length -1))&&frameAt(pos + length -1)->parentFrame() ==frameAt(pos));Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);#endifsplit(pos);split(pos+length); uint dst = needsInsert ? fragments.findNode(to) :0; uint dstKey = needsInsert ? fragments.position(dst) :0; uint x = fragments.findNode(pos); uint end = fragments.findNode(pos+length); uint w =0;while(x != end) { uint n = fragments.next(x); uint key = fragments.position(x); uint b = blocks.findNode(key+1); QTextBlockData *B = blocks.fragment(b);int blockRevision = B->revision; QTextFragmentData *X = fragments.fragment(x);QT_INIT_TEXTUNDOCOMMAND(c,QTextUndoCommand::Removed, (editBlock !=0), op, X->format, X->stringPosition, key, X->size_array[0], blockRevision);QT_INIT_TEXTUNDOCOMMAND(cInsert,QTextUndoCommand::Inserted, (editBlock !=0), op, X->format, X->stringPosition, dstKey, X->size_array[0], blockRevision);if(key+1!= blocks.position(b)) {// qDebug("remove_string from %d length %d", key, X->size_array[0]);Q_ASSERT(noBlockInString(QStringView{text}.mid(X->stringPosition, X->size_array[0]))); w =remove_string(key, X->size_array[0], op);if(needsInsert) {insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op); dstKey += X->size_array[0];}}else{// qDebug("remove_block at %d", key);Q_ASSERT(X->size_array[0] ==1&&isValidBlockSeparator(text.at(X->stringPosition))); b = blocks.previous(b); B =nullptr; c.command = blocks.size(b) ==1?QTextUndoCommand::BlockDeleted :QTextUndoCommand::BlockRemoved; w =remove_block(key, &c.blockFormat,QTextUndoCommand::BlockAdded, op);if(needsInsert) {insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op,QTextUndoCommand::BlockRemoved); cInsert.command = blocks.size(b) ==1?QTextUndoCommand::BlockAdded :QTextUndoCommand::BlockInserted; cInsert.blockFormat = c.blockFormat;}}appendUndoItem(c);if(B) B->revision = revision; x = n;if(needsInsert)appendUndoItem(cInsert);}if(w)unite(w);Q_ASSERT(blocks.length() == fragments.length());if(!blockCursorAdjustment)finishEdit();}voidQTextDocumentPrivate::remove(int pos,int length,QTextUndoCommand::Operation op){if(length ==0)return; blockCursorAdjustment =true;move(pos, -1, length, op); blockCursorAdjustment =false;for(QTextCursorPrivate *curs :std::as_const(cursors)) {if(curs->adjustPosition(pos, -length, op) ==QTextCursorPrivate::CursorMoved) { curs->changed =true;}}finishEdit();}voidQTextDocumentPrivate::setCharFormat(int pos,int length,const QTextCharFormat &newFormat, FormatChangeMode mode){beginEditBlock();Q_ASSERT(newFormat.isValid());int newFormatIdx = -1;if(mode == SetFormatAndPreserveObjectIndices) { QTextCharFormat cleanFormat = newFormat; cleanFormat.clearProperty(QTextFormat::ObjectIndex); newFormatIdx = formats.indexForFormat(cleanFormat);}else if(mode == SetFormat) { newFormatIdx = formats.indexForFormat(newFormat);}if(pos == -1) {if(mode == MergeFormat) { QTextFormat format = formats.format(initialBlockCharFormatIndex); format.merge(newFormat); initialBlockCharFormatIndex = formats.indexForFormat(format);}else if(mode == SetFormatAndPreserveObjectIndices && formats.format(initialBlockCharFormatIndex).objectIndex() != -1) { QTextCharFormat f = newFormat; f.setObjectIndex(formats.format(initialBlockCharFormatIndex).objectIndex()); initialBlockCharFormatIndex = formats.indexForFormat(f);}else{ initialBlockCharFormatIndex = newFormatIdx;}++pos;--length;}const int startPos = pos;const int endPos = pos + length;split(startPos);split(endPos);while(pos < endPos) {FragmentMap::Iterator it = fragments.find(pos);Q_ASSERT(!it.atEnd()); QTextFragmentData *fragment = it.value();Q_ASSERT(formats.format(fragment->format).type() ==QTextFormat::CharFormat);int offset = pos - it.position();int length =qMin(endPos - pos,int(fragment->size_array[0] - offset));int oldFormat = fragment->format;if(mode == MergeFormat) { QTextFormat format = formats.format(fragment->format); format.merge(newFormat); fragment->format = formats.indexForFormat(format);}else if(mode == SetFormatAndPreserveObjectIndices && formats.format(oldFormat).objectIndex() != -1) { QTextCharFormat f = newFormat; f.setObjectIndex(formats.format(oldFormat).objectIndex()); fragment->format = formats.indexForFormat(f);}else{ fragment->format = newFormatIdx;}QT_INIT_TEXTUNDOCOMMAND(c,QTextUndoCommand::CharFormatChanged,true,QTextUndoCommand::MoveCursor, oldFormat,0, pos, length,0);appendUndoItem(c); pos += length;Q_ASSERT(q20::cmp_equal(pos, (it.position() + fragment->size_array[0])) || pos >= endPos);}int n = fragments.findNode(startPos -1);if(n)unite(n); n = fragments.findNode(endPos);if(n)unite(n); QTextBlock blockIt =blocksFind(startPos); QTextBlock endIt =blocksFind(endPos);if(endIt.isValid()) endIt = endIt.next();for(; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())QTextDocumentPrivate::block(blockIt)->invalidate();documentChange(startPos, length);endEditBlock();}voidQTextDocumentPrivate::setBlockFormat(const QTextBlock &from,const QTextBlock &to,const QTextBlockFormat &newFormat, FormatChangeMode mode){beginEditBlock();Q_ASSERT(mode != SetFormatAndPreserveObjectIndices);// only implemented for setCharFormatQ_ASSERT(newFormat.isValid());int newFormatIdx = -1;if(mode == SetFormat) newFormatIdx = formats.indexForFormat(newFormat); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat)); QTextBlock it = from; QTextBlock end = to;if(end.isValid()) end = end.next();for(; it != end; it = it.next()) {int oldFormat =block(it)->format; QTextBlockFormat format = formats.blockFormat(oldFormat); QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));if(mode == MergeFormat) { format.merge(newFormat); newFormatIdx = formats.indexForFormat(format); group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));}block(it)->format = newFormatIdx;block(it)->invalidate();QT_INIT_TEXTUNDOCOMMAND(c,QTextUndoCommand::BlockFormatChanged,true,QTextUndoCommand::MoveCursor, oldFormat,0, it.position(),1,0);appendUndoItem(c);if(group != oldGroup) {if(oldGroup) oldGroup->blockRemoved(it);if(group) group->blockInserted(it);}else if(group) { group->blockFormatChanged(it);}}documentChange(from.position(), to.position() + to.length() - from.position());endEditBlock();}boolQTextDocumentPrivate::split(int pos){ uint x = fragments.findNode(pos);if(x) {int k = fragments.position(x);// qDebug("found fragment with key %d, size_left=%d, size=%d to split at %d",// k, (*it)->size_left[0], (*it)->size_array[0], pos);if(k != pos) {Q_ASSERT(k <= pos);// need to resize the first fragment and add a new one QTextFragmentData *X = fragments.fragment(x);int oldsize = X->size_array[0]; fragments.setSize(x, pos-k); uint n = fragments.insert_single(pos, oldsize-(pos-k)); X = fragments.fragment(x); QTextFragmentData *N = fragments.fragment(n); N->stringPosition = X->stringPosition + pos-k; N->format = X->format;return true;}}return false;}boolQTextDocumentPrivate::unite(uint f){ uint n = fragments.next(f);if(!n)return false; QTextFragmentData *ff = fragments.fragment(f); QTextFragmentData *nf = fragments.fragment(n);if(nf->format == ff->format && (ff->stringPosition + (int)ff->size_array[0] == nf->stringPosition)) {if(isValidBlockSeparator(text.at(ff->stringPosition))||isValidBlockSeparator(text.at(nf->stringPosition)))return false; fragments.setSize(f, ff->size_array[0] + nf->size_array[0]); fragments.erase_single(n);return true;}return false;}intQTextDocumentPrivate::undoRedo(bool undo){PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ?"undo:":"redo:", undoState,int(undoStack.size()));if(!undoEnabled || (undo && undoState ==0) || (!undo && undoState == undoStack.size()))return-1; undoEnabled =false;beginEditBlock();int editPos = -1;int editLength = -1;while(1) {if(undo)--undoState; QTextUndoCommand &c = undoStack[undoState];int resetBlockRevision = c.pos;switch(c.command) {caseQTextUndoCommand::Inserted:remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);PMDEBUG(" erase: from %d, length %d", c.pos, c.length); c.command =QTextUndoCommand::Removed; editPos = c.pos; editLength =0;break;caseQTextUndoCommand::Removed:PMDEBUG(" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation); c.command =QTextUndoCommand::Inserted;if(editPos != (int)c.pos) editLength =0; editPos = c.pos; editLength += c.length;break;caseQTextUndoCommand::BlockInserted:caseQTextUndoCommand::BlockAdded:remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);PMDEBUG(" blockremove: from %d", c.pos);if(c.command ==QTextUndoCommand::BlockInserted) c.command =QTextUndoCommand::BlockRemoved;else c.command =QTextUndoCommand::BlockDeleted; editPos = c.pos; editLength =0;break;caseQTextUndoCommand::BlockRemoved:caseQTextUndoCommand::BlockDeleted:PMDEBUG(" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command); resetBlockRevision +=1;if(c.command ==QTextUndoCommand::BlockRemoved) c.command =QTextUndoCommand::BlockInserted;else c.command =QTextUndoCommand::BlockAdded;if(editPos != (int)c.pos) editLength =0; editPos = c.pos; editLength +=1;break;caseQTextUndoCommand::CharFormatChanged: { resetBlockRevision = -1;// ## TODOPMDEBUG(" charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length); FragmentIterator it =find(c.pos);Q_ASSERT(!it.atEnd());int oldFormat = it.value()->format;setCharFormat(c.pos, c.length, formats.charFormat(c.format)); c.format = oldFormat;if(editPos != (int)c.pos) editLength =0; editPos = c.pos; editLength += c.length;break;}caseQTextUndoCommand::BlockFormatChanged: { resetBlockRevision = -1;// ## TODOPMDEBUG(" blockformat: format %d pos %d", c.format, c.pos); QTextBlock it =blocksFind(c.pos);Q_ASSERT(it.isValid());int oldFormat =block(it)->format;block(it)->format = c.format; QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat))); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format))); c.format = oldFormat;if(group != oldGroup) {if(oldGroup) oldGroup->blockRemoved(it);if(group) group->blockInserted(it);}else if(group) { group->blockFormatChanged(it);}documentChange(it.position(), it.length()); editPos = -1;break;}caseQTextUndoCommand::GroupFormatChange: { resetBlockRevision = -1;// ## TODOPMDEBUG(" group format change"); QTextObject *object =objectForIndex(c.objectIndex);int oldFormat = formats.objectFormatIndex(c.objectIndex);changeObjectFormat(object, c.format); c.format = oldFormat; editPos = -1;break;}caseQTextUndoCommand::CursorMoved: editPos = c.pos; editLength =0;break;caseQTextUndoCommand::Custom: resetBlockRevision = -1;// ## TODOif(undo) c.custom->undo();else c.custom->redo(); editPos = -1;break;default:Q_ASSERT(false);}if(resetBlockRevision >=0) {int b = blocks.findNode(resetBlockRevision); QTextBlockData *B = blocks.fragment(b); B->revision = c.revision;}if(!undo)++undoState;bool inBlock = ( undoState >0&& undoState < undoStack.size()&& undoStack.at(undoState).block_part && undoStack.at(undoState -1).block_part && !undoStack.at(undoState -1).block_end );if(!inBlock)break;} undoEnabled =true;int newCursorPos = -1;if(editPos >=0) newCursorPos = editPos + editLength;else if(docChangeFrom >=0) newCursorPos=qMin(docChangeFrom + docChangeLength,length() -1);endEditBlock();emitUndoAvailable(isUndoAvailable());emitRedoAvailable(isRedoAvailable());return newCursorPos;}/*! Appends a custom undo \a item to the undo stack.*/voidQTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item){if(!undoEnabled) {delete item;return;} QTextUndoCommand c; c.command =QTextUndoCommand::Custom; c.block_part = editBlock !=0; c.block_end =0; c.operation =QTextUndoCommand::MoveCursor; c.format =0; c.strPos =0; c.pos =0; c.blockFormat =0; c.custom = item;appendUndoItem(c);}voidQTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c){PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);if(!undoEnabled)return;if(undoState < undoStack.size())clearUndoRedoStacks(QTextDocument::RedoStack);if(editBlock !=0&& editBlockCursorPosition >=0) {// we had a beginEditBlock() with a cursor positionif(q20::cmp_not_equal(c.pos, editBlockCursorPosition)) {// and that cursor position is different from the command// generate a CursorMoved undo itemQT_INIT_TEXTUNDOCOMMAND(cc,QTextUndoCommand::CursorMoved,true,QTextUndoCommand::MoveCursor,0,0, editBlockCursorPosition,0,0); undoStack.append(cc); undoState++; editBlockCursorPosition = -1;}}if(!undoStack.isEmpty() && modified) {const int lastIdx = undoState -1;const QTextUndoCommand &last = undoStack.at(lastIdx);if( (last.block_part && c.block_part && !last.block_end)// part of the same block => can merge|| (!c.block_part && !last.block_part)// two single undo items => can merge|| (c.command ==QTextUndoCommand::Inserted && last.command == c.command && (last.block_part && !c.block_part))) {// two sequential inserts that are not part of the same block => can mergeif(undoStack[lastIdx].tryMerge(c))return;}}if(modifiedState > undoState) modifiedState = -1; undoStack.append(c); undoState++;emitUndoAvailable(true);emitRedoAvailable(false);if(!c.block_part) emit document()->undoCommandAdded();}voidQTextDocumentPrivate::clearUndoRedoStacks(QTextDocument::Stacks stacksToClear,bool emitSignals){bool undoCommandsAvailable = undoState !=0;bool redoCommandsAvailable = undoState != undoStack.size();if(stacksToClear ==QTextDocument::UndoStack && undoCommandsAvailable) {for(int i =0; i < undoState; ++i) { QTextUndoCommand c = undoStack.at(i);if(c.command &QTextUndoCommand::Custom)delete c.custom;} undoStack.remove(0, undoState); undoState =0;if(emitSignals)emitUndoAvailable(false);}else if(stacksToClear ==QTextDocument::RedoStack && redoCommandsAvailable) {for(int i = undoState; i < undoStack.size(); ++i) { QTextUndoCommand c = undoStack.at(i);if(c.command &QTextUndoCommand::Custom)delete c.custom;} undoStack.resize(undoState);if(emitSignals)emitRedoAvailable(false);}else if(stacksToClear ==QTextDocument::UndoAndRedoStacks && !undoStack.isEmpty()) {for(int i =0; i < undoStack.size(); ++i) { QTextUndoCommand c = undoStack.at(i);if(c.command &QTextUndoCommand::Custom)delete c.custom;} undoState =0; undoStack.clear();if(emitSignals && undoCommandsAvailable)emitUndoAvailable(false);if(emitSignals && redoCommandsAvailable)emitRedoAvailable(false);}}voidQTextDocumentPrivate::emitUndoAvailable(bool available){if(available != wasUndoAvailable) {Q_Q(QTextDocument); emit q->undoAvailable(available); wasUndoAvailable = available;}}voidQTextDocumentPrivate::emitRedoAvailable(bool available){if(available != wasRedoAvailable) {Q_Q(QTextDocument); emit q->redoAvailable(available); wasRedoAvailable = available;}}voidQTextDocumentPrivate::enableUndoRedo(bool enable){if(enable && maximumBlockCount >0)return;if(!enable) { undoState =0;clearUndoRedoStacks(QTextDocument::RedoStack);emitUndoAvailable(false);emitRedoAvailable(false);} modifiedState = modified ? -1: undoState; undoEnabled = enable;if(!undoEnabled)compressPieceTable();}voidQTextDocumentPrivate::joinPreviousEditBlock(){beginEditBlock();if(undoEnabled && undoState) undoStack[undoState -1].block_end =false;}voidQTextDocumentPrivate::endEditBlock(){Q_ASSERT(editBlock >0);if(--editBlock)return;if(undoEnabled && undoState >0) {const bool wasBlocking = !undoStack.at(undoState -1).block_end;if(undoStack.at(undoState -1).block_part) { undoStack[undoState -1].block_end =true;if(wasBlocking) emit document()->undoCommandAdded();}} editBlockCursorPosition = -1;finishEdit();}voidQTextDocumentPrivate::finishEdit(){Q_Q(QTextDocument);if(editBlock)return;if(framesDirty)scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);if(lout && docChangeFrom >=0) {if(!inContentsChange) { QScopedValueRollback<bool>bg(inContentsChange,true); emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);} lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);} docChangeFrom = -1;if(needsEnsureMaximumBlockCount) { needsEnsureMaximumBlockCount =false;if(ensureMaximumBlockCount()) {// if ensureMaximumBlockCount() returns true// it will have called endEditBlock() and// compressPieceTable() itself, so we return here// to prevent getting two contentsChanged emitsreturn;}} QList<QTextCursor> changedCursors;for(QTextCursorPrivate *curs :std::as_const(cursors)) {if(curs->changed) { curs->changed =false; changedCursors.append(QTextCursor(curs));}}for(const QTextCursor &cursor :std::as_const(changedCursors)) emit q->cursorPositionChanged(cursor);contentsChanged();if(blocks.numNodes() != lastBlockCount) { lastBlockCount = blocks.numNodes(); emit q->blockCountChanged(lastBlockCount);}if(!undoEnabled && unreachableCharacterCount)compressPieceTable();}voidQTextDocumentPrivate::documentChange(int from,int length){// qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);if(docChangeFrom <0) { docChangeFrom = from; docChangeOldLength = length; docChangeLength = length;return;}int start =qMin(from, docChangeFrom);int end =qMax(from + length, docChangeFrom + docChangeLength);int diff =qMax(0, end - start - docChangeLength); docChangeFrom = start; docChangeOldLength += diff; docChangeLength += diff;}/* adjustDocumentChangesAndCursors is called whenever there is an insert or remove of characters. param from is the cursor position in the document param addedOrRemoved is the amount of characters added or removed. A negative number means characters are removed. The function stores information to be emitted when finishEdit() is called.*/voidQTextDocumentPrivate::adjustDocumentChangesAndCursors(int from,int addedOrRemoved,QTextUndoCommand::Operation op){if(!editBlock)++revision;if(blockCursorAdjustment) {;// postpone, will be called again from QTextDocumentPrivate::remove()}else{for(QTextCursorPrivate *curs :std::as_const(cursors)) {if(curs->adjustPosition(from, addedOrRemoved, op) ==QTextCursorPrivate::CursorMoved) { curs->changed =true;}}}// qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);if(docChangeFrom <0) { docChangeFrom = from;if(addedOrRemoved >0) { docChangeOldLength =0; docChangeLength = addedOrRemoved;}else{ docChangeOldLength = -addedOrRemoved; docChangeLength =0;}// qDebug("adjustDocumentChanges:");// qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);return;}// have to merge the new change with the already existing one.int added =qMax(0, addedOrRemoved);int removed =qMax(0, -addedOrRemoved);int diff =0;if(from + removed < docChangeFrom) diff = docChangeFrom - from - removed;else if(from > docChangeFrom + docChangeLength) diff = from - (docChangeFrom + docChangeLength);int overlap_start =qMax(from, docChangeFrom);int overlap_end =qMin(from + removed, docChangeFrom + docChangeLength);int removedInside =qMax(0, overlap_end - overlap_start); removed -= removedInside;// qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside); docChangeFrom =qMin(docChangeFrom, from); docChangeOldLength += removed + diff; docChangeLength += added - removedInside + diff;// qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);} QString QTextDocumentPrivate::plainText()const{ QString result; result.resize(length());const QChar *text_unicode = text.unicode(); QChar *data = result.data();for(QTextDocumentPrivate::FragmentIterator it =begin(); it !=end(); ++it) {const QTextFragmentData *f = *it;::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] *sizeof(QChar)); data += f->size_array[0];}// remove trailing block separator result.chop(1);return result;}intQTextDocumentPrivate::blockCharFormatIndex(int node)const{int pos = blocks.position(node);if(pos ==0)return initialBlockCharFormatIndex;return fragments.find(pos -1)->format;}intQTextDocumentPrivate::nextCursorPosition(int position,QTextLayout::CursorMode mode)const{if(position ==length()-1)return position; QTextBlock it =blocksFind(position);int start = it.position();int end = start + it.length() -1;if(position == end)return end +1;return it.layout()->nextCursorPosition(position-start, mode) + start;}intQTextDocumentPrivate::previousCursorPosition(int position,QTextLayout::CursorMode mode)const{if(position ==0)return position; QTextBlock it =blocksFind(position);int start = it.position();if(position == start)return start -1;return it.layout()->previousCursorPosition(position-start, mode) + start;}intQTextDocumentPrivate::leftCursorPosition(int position)const{ QTextBlock it =blocksFind(position);int start = it.position();return it.layout()->leftCursorPosition(position-start) + start;}intQTextDocumentPrivate::rightCursorPosition(int position)const{ QTextBlock it =blocksFind(position);int start = it.position();return it.layout()->rightCursorPosition(position-start) + start;}voidQTextDocumentPrivate::changeObjectFormat(QTextObject *obj,int format){beginEditBlock();int objectIndex = obj->objectIndex();int oldFormatIndex = formats.objectFormatIndex(objectIndex); formats.setObjectFormatIndex(objectIndex, format); QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);if(b) { b->d_func()->markBlocksDirty();} QTextFrame *f = qobject_cast<QTextFrame *>(obj);if(f)documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());QT_INIT_TEXTUNDOCOMMAND(c,QTextUndoCommand::GroupFormatChange, (editBlock !=0),QTextUndoCommand::MoveCursor, oldFormatIndex,0,0, obj->d_func()->objectIndex,0);appendUndoItem(c);endEditBlock();}static QTextFrame *findChildFrame(QTextFrame *f,int pos){/* Binary search for frame at pos */const QList<QTextFrame *> children = f->childFrames();int first =0;int last = children.size() -1;while(first <= last) {int mid = (first + last) /2; QTextFrame *c = children.at(mid);if(pos > c->lastPosition()) first = mid +1;else if(pos < c->firstPosition()) last = mid -1;elsereturn c;}returnnullptr;} QTextFrame *QTextDocumentPrivate::rootFrame()const{if(!rtFrame) { QTextFrameFormat defaultRootFrameFormat; defaultRootFrameFormat.setMargin(documentMargin); rtFrame = qobject_cast<QTextFrame *>(const_cast<QTextDocumentPrivate *>(this)->createObject(defaultRootFrameFormat));}return rtFrame;}voidQTextDocumentPrivate::addCursor(QTextCursorPrivate *c){ cursors.insert(c);}voidQTextDocumentPrivate::removeCursor(QTextCursorPrivate *c){ cursors.remove(c);} QTextFrame *QTextDocumentPrivate::frameAt(int pos)const{ QTextFrame *f =rootFrame();while(1) { QTextFrame *c =findChildFrame(f, pos);if(!c)return f; f = c;}}voidQTextDocumentPrivate::clearFrame(QTextFrame *f){for(int i =0; i < f->d_func()->childFrames.size(); ++i)clearFrame(f->d_func()->childFrames.at(i)); f->d_func()->childFrames.clear(); f->d_func()->parentFrame =nullptr;}voidQTextDocumentPrivate::scan_frames(int pos,int charsRemoved,int charsAdded){// ###### optimizeQ_UNUSED(pos);Q_UNUSED(charsRemoved);Q_UNUSED(charsAdded); QTextFrame *f =rootFrame();clearFrame(f);for(FragmentIterator it =begin(); it !=end(); ++it) {// QTextFormat fmt = formats.format(it->format); QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));if(!frame)continue;Q_ASSERT(it.size() ==1); QChar ch = text.at(it->stringPosition);if(ch == QTextBeginningOfFrame) {if(f != frame) {// f == frame happens for tablesQ_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start ==0); frame->d_func()->parentFrame = f; f->d_func()->childFrames.append(frame); f = frame;}}else if(ch == QTextEndOfFrame) {Q_ASSERT(f == frame);Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end ==0); f = frame->d_func()->parentFrame;}else if(ch ==QChar::ObjectReplacementCharacter) {Q_ASSERT(f != frame);Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start ==0);Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end ==0); frame->d_func()->parentFrame = f; f->d_func()->childFrames.append(frame);}else{Q_ASSERT(false);}}Q_ASSERT(f == rtFrame); framesDirty =false;}voidQTextDocumentPrivate::insert_frame(QTextFrame *f){int start = f->firstPosition();int end = f->lastPosition(); QTextFrame *parent =frameAt(start-1);Q_ASSERT(parent ==frameAt(end+1));if(start != end) {// iterator over the parent and move all children contained in my frame to myselffor(int i =0; i < parent->d_func()->childFrames.size(); ++i) { QTextFrame *c = parent->d_func()->childFrames.at(i);if(start < c->firstPosition() && end > c->lastPosition()) { parent->d_func()->childFrames.removeAt(i); f->d_func()->childFrames.append(c); c->d_func()->parentFrame = f;}}}// insert at the correct positionint i =0;for(; i < parent->d_func()->childFrames.size(); ++i) { QTextFrame *c = parent->d_func()->childFrames.at(i);if(c->firstPosition() > end)break;} parent->d_func()->childFrames.insert(i, f); f->d_func()->parentFrame = parent;} QTextFrame *QTextDocumentPrivate::insertFrame(int start,int end,const QTextFrameFormat &format){Q_ASSERT(start >=0&& start <length());Q_ASSERT(end >=0&& end <length());Q_ASSERT(start <= end || end == -1);if(start != end &&frameAt(start) !=frameAt(end))returnnullptr;beginEditBlock(); QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));Q_ASSERT(frame);// #### using the default block and char format below might be wrongint idx = formats.indexForFormat(QTextBlockFormat()); QTextCharFormat cfmt; cfmt.setObjectIndex(frame->objectIndex());int charIdx = formats.indexForFormat(cfmt);insertBlock(QTextBeginningOfFrame, start, idx, charIdx,QTextUndoCommand::MoveCursor);insertBlock(QTextEndOfFrame, ++end, idx, charIdx,QTextUndoCommand::KeepCursor); frame->d_func()->fragment_start =find(start).n; frame->d_func()->fragment_end =find(end).n;insert_frame(frame);endEditBlock();return frame;}voidQTextDocumentPrivate::removeFrame(QTextFrame *frame){ QTextFrame *parent = frame->d_func()->parentFrame;if(!parent)return;int start = frame->firstPosition();int end = frame->lastPosition();Q_ASSERT(end >= start);beginEditBlock();// remove already removes the frames from the treeremove(end,1);remove(start-1,1);endEditBlock();} QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex)const{if(objectIndex <0)returnnullptr; QTextObject *object = objects.value(objectIndex,nullptr);if(!object) { QTextDocumentPrivate *that =const_cast<QTextDocumentPrivate *>(this); QTextFormat fmt = formats.objectFormat(objectIndex); object = that->createObject(fmt, objectIndex);}return object;} QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex)const{int objectIndex = formats.format(formatIndex).objectIndex();returnobjectForIndex(objectIndex);} QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f)const{returnobjectForIndex(f.objectIndex());} QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f,int objectIndex){ QTextObject *obj =document()->createObject(f);if(obj) { obj->d_func()->objectIndex = objectIndex == -1? formats.createObjectIndex(f) : objectIndex; objects[obj->d_func()->objectIndex] = obj;}return obj;}voidQTextDocumentPrivate::deleteObject(QTextObject *object){const int objIdx = object->d_func()->objectIndex; objects.remove(objIdx);delete object;}voidQTextDocumentPrivate::contentsChanged(){Q_Q(QTextDocument);if(editBlock)return;bool m = undoEnabled ? (modifiedState != undoState) :true;if(modified != m) { modified = m; emit q->modificationChanged(modified);} emit q->contentsChanged();}voidQTextDocumentPrivate::compressPieceTable(){if(undoEnabled)return;const uint garbageCollectionThreshold =96*1024;// bytes//qDebug() << "unreachable bytes:" << unreachableCharacterCount * sizeof(QChar) << " -- limit" << garbageCollectionThreshold << "text size =" << text.size() << "capacity:" << text.capacity();bool compressTable = unreachableCharacterCount *sizeof(QChar) > garbageCollectionThreshold && text.size() >= text.capacity() *0.9;if(!compressTable)return; QString newText; newText.resize(text.size()); QChar *newTextPtr = newText.data();int newLen =0;for(FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {memcpy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] *sizeof(QChar)); it->stringPosition = newLen; newTextPtr += it->size_array[0]; newLen += it->size_array[0];} newText.resize(newLen); newText.squeeze();//qDebug() << "removed" << text.size() - newText.size() << "characters"; text = newText; unreachableCharacterCount =0;}voidQTextDocumentPrivate::setModified(bool m){Q_Q(QTextDocument);if(m == modified)return; modified = m;if(!modified) modifiedState = undoState;else modifiedState = -1; emit q->modificationChanged(modified);}boolQTextDocumentPrivate::ensureMaximumBlockCount(){if(maximumBlockCount <=0)return false;if(blocks.numNodes() <= maximumBlockCount)return false;beginEditBlock();const int blocksToRemove = blocks.numNodes() - maximumBlockCount; QTextCursor cursor(this,0); cursor.movePosition(QTextCursor::NextBlock,QTextCursor::KeepAnchor, blocksToRemove); unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();// preserve the char format of the paragraph that is to become the new first one QTextCharFormat charFmt = cursor.blockCharFormat(); cursor.removeSelectedText(); cursor.setBlockCharFormat(charFmt);endEditBlock();compressPieceTable();return true;}/// This method is called from QTextTable when it is about to remove a table-cell to allow cursors to update their selection.voidQTextDocumentPrivate::aboutToRemoveCell(int from,int to){Q_ASSERT(from <= to);for(QTextCursorPrivate *curs :std::as_const(cursors)) curs->aboutToRemoveCell(from, to);} QT_END_NAMESPACE 
close