Skip 'off-by-one' adjustment in block length during undo operation

The adjustment of block length with 'off-by-one' affects content
length during undo operation. The issue occurs when we perform undo
operation for a set of group blocks that have same fragment
position. Since their positions are same, group block change
(QTextDocumentPrivate::documentChange) with respect to insertion
doesn't affect document block length and further adjustment to
'off-by-one' without considering this leads to incorrect document
content change information (such as invalid information with regard
to characters removed).

This patch skips adjustment of group block length during undo
operation.

Amends 8fbedf2196a292fe2affcf83ddc846b9c852772a

Fixes: QTBUG-113865
Pick-to: 6.5 6.2 5.15
Change-Id: I315dcf01ba5b2f4ed6d95e9d6910d82848374aef
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
(cherry picked from commit 8a725084396da5872fa020212b3cb09ee40a91df)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 3999908ae5ea9e86b1785f7352d31fb11cc8d21e)
This commit is contained in:
Santhosh Kumar 2023-12-03 22:00:37 +01:00 committed by Qt Cherry-pick Bot
parent 42af812dcd
commit adb47b7f73
2 changed files with 61 additions and 2 deletions

View File

@ -349,9 +349,11 @@ int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blo
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat)); QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
if (group) { if (group) {
group->blockInserted(QTextBlock(this, b)); group->blockInserted(QTextBlock(this, b));
if (command != QTextUndoCommand::BlockDeleted) {
docChangeOldLength--; docChangeOldLength--;
docChangeLength--; docChangeLength--;
} }
}
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format))); QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
if (frame) { if (frame) {

View File

@ -181,6 +181,7 @@ private slots:
void insertHtmlWithComments(); void insertHtmlWithComments();
void delayedLayout(); void delayedLayout();
void undoContentChangeIndices();
private: private:
void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
@ -3981,5 +3982,61 @@ void tst_QTextDocument::delayedLayout()
QCOMPARE(layout->lineCount(), 1); // layout happened QCOMPARE(layout->lineCount(), 1); // layout happened
} }
void tst_QTextDocument::undoContentChangeIndices() // QTBUG-113865
{
QTextDocument doc;
QTestDocumentLayout *layout = new QTestDocumentLayout(&doc);
QString content = QString("<html><body>"
"<ul><li>Undo</li></ul>"
"<ul><li>operation</li></ul>"
"<ul><li>of</li></ul>"
"<ul><li>unnumbered</li></ul>"
"<ul><li>lists</li></ul>"
"<ul><li>shows</li></ul>"
"<ul><li>invalid</li></ul>"
"<ul><li>content</li></ul>"
"<ul><li>indices</li></ul>"
"</body></html>");
doc.setDocumentLayout(layout);
doc.setHtml(content);
// Select the entire document content
QTextCursor cursor(&doc);
cursor.select(QTextCursor::Document);
cursor.removeSelectedText();
// Undo above operation
doc.undo();
// Move the cursor to the end
cursor.movePosition(QTextCursor::End);
cursor.insertHtml(content);
// Select the whole document and remove the content
cursor.select(QTextCursor::Document);
cursor.removeSelectedText();
int documentLength = 0;
int changeRemoved = 0;
int changeAdded = 0;
int changePos = 0;
connect(&doc, &QTextDocument::contentsChange, this, [&](int pos, int removed, int added){
documentLength = doc.characterCount();
changeRemoved = removed;
changeAdded = added;
changePos = pos;
});
// Undo above operation
doc.undo();
const int changeEnd = changeAdded + changeRemoved;
QVERIFY(documentLength > 0);
QCOMPARE(changePos, 0);
QVERIFY(changeRemoved >= 0);
QVERIFY(documentLength >= changeEnd);
}
QTEST_MAIN(tst_QTextDocument) QTEST_MAIN(tst_QTextDocument)
#include "tst_qtextdocument.moc" #include "tst_qtextdocument.moc"