From adb47b7f73db45c221743fa6f291d0045dcfc463 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Sun, 3 Dec 2023 22:00:37 +0100 Subject: [PATCH] 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 (cherry picked from commit 8a725084396da5872fa020212b3cb09ee40a91df) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit 3999908ae5ea9e86b1785f7352d31fb11cc8d21e) --- src/gui/text/qtextdocument_p.cpp | 6 +- .../text/qtextdocument/tst_qtextdocument.cpp | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 9e630f37876..03da4a4c100 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -349,8 +349,10 @@ int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blo QTextBlockGroup *group = qobject_cast(objectForFormat(blockFormat)); if (group) { group->blockInserted(QTextBlock(this, b)); - docChangeOldLength--; - docChangeLength--; + if (command != QTextUndoCommand::BlockDeleted) { + docChangeOldLength--; + docChangeLength--; + } } QTextFrame *frame = qobject_cast(objectForFormat(formats.format(format))); diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index f6527fbff6d..a059648eee5 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -181,6 +181,7 @@ private slots: void insertHtmlWithComments(); void delayedLayout(); + void undoContentChangeIndices(); private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); @@ -3981,5 +3982,61 @@ void tst_QTextDocument::delayedLayout() QCOMPARE(layout->lineCount(), 1); // layout happened } +void tst_QTextDocument::undoContentChangeIndices() // QTBUG-113865 +{ + QTextDocument doc; + QTestDocumentLayout *layout = new QTestDocumentLayout(&doc); + QString content = QString("" + "
  • Undo
" + "
  • operation
" + "
  • of
" + "
  • unnumbered
" + "
  • lists
" + "
  • shows
" + "
  • invalid
" + "
  • content
" + "
  • indices
" + ""); + 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) #include "tst_qtextdocument.moc"