sw/inc/undobj.hxx | 3 sw/qa/extras/uiwriter/uiwriter2.cxx | 148 ++++++++++++++++++++++++++++++++++++ sw/source/core/edit/edlingu.cxx | 3 sw/source/core/undo/undobj.cxx | 6 - sw/source/core/undo/unins.cxx | 41 ++++----- 5 files changed, 174 insertions(+), 27 deletions(-)
New commits: commit 9e746242a5dc4fd79ed8214c5508bade60d325ca Author: Michael Stahl <michael.st...@cib.de> AuthorDate: Fri Jun 12 14:17:20 2020 +0200 Commit: Michael Stahl <michael.st...@cib.de> CommitDate: Fri Jun 12 18:09:03 2020 +0200 tdf#131912 sw: fix spell check correct deleting flys * SwEditShell::ApplyChangedSentence() should not call DeleteAndJoin() + InsertString() but ReplaceRange() * ReplaceRange() and SwUndoReplace need to set a new flag DelContentType::Replace to tell SwUndoSaveContent::DelContentIndex() not to delete flys but instead record the previous anchor positions * SwUndoReplace::UndoImpl() should also not call DeleteAndJoin() + InsertString(); instead call ReplaceRange() for the start node and then DeleteAndJoin() for any regex "\n" that were inserted (regression from 28b77c89dfcafae82cf2a6d85731b643ff9290e5) Change-Id: I485d79510ae233213cb4b208533871934c5e5ec6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96201 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@cib.de> (cherry picked from commit e1629c210ad78310e3d48c0756723134a27b89df) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/96186 diff --git a/sw/inc/undobj.hxx b/sw/inc/undobj.hxx index 8da9b70980b8..91394446ccf7 100644 --- a/sw/inc/undobj.hxx +++ b/sw/inc/undobj.hxx @@ -135,12 +135,13 @@ enum class DelContentType : sal_uInt16 Fly = 0x02, Bkm = 0x08, AllMask = 0x0b, + Replace = 0x10, WriterfilterHack = 0x20, ExcludeFlyAtStartEnd = 0x40, CheckNoCntnt = 0x80, }; namespace o3tl { - template<> struct typed_flags<DelContentType> : is_typed_flags<DelContentType, 0xeb> {}; + template<> struct typed_flags<DelContentType> : is_typed_flags<DelContentType, 0xfb> {}; } /// will DelContentIndex destroy a frame anchored at character at rAnchorPos? diff --git a/sw/qa/extras/uiwriter/uiwriter2.cxx b/sw/qa/extras/uiwriter/uiwriter2.cxx index 74f6c5e30c22..99f573b139e6 100644 --- a/sw/qa/extras/uiwriter/uiwriter2.cxx +++ b/sw/qa/extras/uiwriter/uiwriter2.cxx @@ -313,6 +313,154 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf132236) assertXPath(pXmlDoc, "/root/page[1]/body/txt", 1); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf131912) +{ + SwDoc* const pDoc = createDoc(); + SwWrtShell* const pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + + sw::UndoManager& rUndoManager = pDoc->GetUndoManager(); + + sw::UnoCursorPointer pCursor( + pDoc->CreateUnoCursor(SwPosition(SwNodeIndex(pDoc->GetNodes().GetEndOfContent(), -1)))); + + pDoc->getIDocumentContentOperations().InsertString(*pCursor, "foo"); + + { + SfxItemSet flySet(pDoc->GetAttrPool(), + svl::Items<RES_FRM_SIZE, RES_FRM_SIZE, RES_ANCHOR, RES_ANCHOR>{}); + SwFormatAnchor anchor(RndStdIds::FLY_AT_CHAR); + pWrtShell->StartOfSection(false); + pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 2, /*bBasicCall=*/false); + anchor.SetAnchor(pWrtShell->GetCursor()->GetPoint()); + flySet.Put(anchor); + SwFormatFrameSize size(SwFrameSize::Minimum, 1000, 1000); + flySet.Put(size); // set a size, else we get 1 char per line... + SwFrameFormat const* pFly = pWrtShell->NewFlyFrame(flySet, /*bAnchValid=*/true); + CPPUNIT_ASSERT(pFly != nullptr); + } + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + + pCursor->SetMark(); + pCursor->GetMark()->nContent.Assign(pCursor->GetContentNode(), 0); + pCursor->GetPoint()->nContent.Assign(pCursor->GetContentNode(), 3); + + // replace with more text + pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, "blahblah", false); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("blahblah"), pCursor->GetNode().GetTextNode()->GetText()); + + rUndoManager.Undo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText()); + + rUndoManager.Redo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("blahblah"), pCursor->GetNode().GetTextNode()->GetText()); + + rUndoManager.Undo(); + + pCursor->GetMark()->nContent.Assign(pCursor->GetContentNode(), 0); + pCursor->GetPoint()->nContent.Assign(pCursor->GetContentNode(), 3); + + // replace with less text + pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, "x", false); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("x"), pCursor->GetNode().GetTextNode()->GetText()); + + rUndoManager.Undo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText()); + + rUndoManager.Redo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("x"), pCursor->GetNode().GetTextNode()->GetText()); + + rUndoManager.Undo(); + + pCursor->GetMark()->nContent.Assign(pCursor->GetContentNode(), 0); + pCursor->GetPoint()->nContent.Assign(pCursor->GetContentNode(), 3); + + // regex replace with paragraph breaks + pDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, "xyz\\n\\nquux\\n", true); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + pWrtShell->StartOfSection(false); + CPPUNIT_ASSERT_EQUAL(OUString("xyz"), + pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText()); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText()); + + rUndoManager.Undo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText()); + pWrtShell->StartOfSection(false); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("foo"), pWrtShell->GetCursor()->GetText()); + + rUndoManager.Redo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + pWrtShell->StartOfSection(false); + CPPUNIT_ASSERT_EQUAL(OUString("xyz"), + pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText()); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText()); + + // regex replace with paragraph join + pWrtShell->StartOfSection(false); + pWrtShell->Down(true); + pDoc->getIDocumentContentOperations().ReplaceRange(*pWrtShell->GetCursor(), "bar", true); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + pWrtShell->StartOfSection(false); + CPPUNIT_ASSERT_EQUAL(OUString("bar"), + pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText()); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("bar\nquux\n"), pWrtShell->GetCursor()->GetText()); + + rUndoManager.Undo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + pWrtShell->StartOfSection(false); + CPPUNIT_ASSERT_EQUAL(OUString("xyz"), + pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText()); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText()); + + rUndoManager.Redo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + pWrtShell->StartOfSection(false); + CPPUNIT_ASSERT_EQUAL(OUString("bar"), + pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText()); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("bar\nquux\n"), pWrtShell->GetCursor()->GetText()); + + rUndoManager.Undo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + pWrtShell->StartOfSection(false); + CPPUNIT_ASSERT_EQUAL(OUString("xyz"), + pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText()); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("xyz\n\nquux\n"), pWrtShell->GetCursor()->GetText()); + + rUndoManager.Undo(); + + CPPUNIT_ASSERT_EQUAL(size_t(1), pDoc->GetFlyCount(FLYCNTTYPE_FRM)); + CPPUNIT_ASSERT_EQUAL(OUString("foo"), pCursor->GetNode().GetTextNode()->GetText()); + pWrtShell->StartOfSection(false); + pWrtShell->EndOfSection(true); + CPPUNIT_ASSERT_EQUAL(OUString("foo"), pWrtShell->GetCursor()->GetText()); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest2, testTdf54819) { load(DATA_DIRECTORY, "tdf54819.fodt"); diff --git a/sw/source/core/edit/edlingu.cxx b/sw/source/core/edit/edlingu.cxx index a2a4adf3b249..15e8532f0ee3 100644 --- a/sw/source/core/edit/edlingu.cxx +++ b/sw/source/core/edit/edlingu.cxx @@ -1141,11 +1141,10 @@ void SwEditShell::ApplyChangedSentence(const svx::SpellPortions& rNewPortions, b if(aCurrentNewPortion->sText != aCurrentOldPortion->sText) { // change text ... - mxDoc->getIDocumentContentOperations().DeleteAndJoin(*pCursor); // ... and apply language if necessary if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage) SetAttrItem( SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId) ); - mxDoc->getIDocumentContentOperations().InsertString(*pCursor, aCurrentNewPortion->sText); + mxDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, aCurrentNewPortion->sText, false); } else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage) { diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx index 1da78ab1d9a4..3a3546c61d55 100644 --- a/sw/source/core/undo/undobj.cxx +++ b/sw/source/core/undo/undobj.cxx @@ -971,7 +971,8 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, if (!m_pHistory) m_pHistory.reset( new SwHistory ); - if (IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType)) + if (!(DelContentType::Replace & nDelContentType) + && IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType)) { m_pHistory->AddDeleteFly(*pFormat, nChainInsPos); // reset n so that no Format is skipped @@ -1002,7 +1003,8 @@ void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark, { if( !m_pHistory ) m_pHistory.reset( new SwHistory ); - if (IsDestroyFrameAnchoredAtChar( + if (!(DelContentType::Replace & nDelContentType) + && IsDestroyFrameAnchoredAtChar( *pAPos, *pStt, *pEnd, nDelContentType)) { m_pHistory->AddDeleteFly(*pFormat, nChainInsPos); diff --git a/sw/source/core/undo/unins.cxx b/sw/source/core/undo/unins.cxx index 6fb4e82fae56..6e08fb5027e5 100644 --- a/sw/source/core/undo/unins.cxx +++ b/sw/source/core/undo/unins.cxx @@ -597,7 +597,7 @@ SwUndoReplace::Impl::Impl( OSL_ENSURE( pNd, "Dude, where's my TextNode?" ); m_pHistory.reset( new SwHistory ); - DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace); m_nSetPos = m_pHistory->Count(); @@ -658,42 +658,39 @@ void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext) pDoc->SetAutoCorrExceptWord( nullptr ); } - SwIndex aIdx( pNd, m_nSttCnt ); // don't look at m_sIns for deletion, maybe it was not completely inserted { rPam.GetPoint()->nNode = *pNd; rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt ); rPam.SetMark(); - rPam.GetPoint()->nNode = m_nEndNd - m_nOffset; - rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), m_nEndCnt ); - // move it out of the way so it is not registered at deleted node - aIdx.Assign(nullptr, 0); - - pDoc->getIDocumentContentOperations().DeleteAndJoin( rPam ); + rPam.GetPoint()->nNode = m_nSttNd - m_nOffset; + rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(), m_nSttNd == m_nEndNd ? m_nEndCnt : pNd->Len()); + + // replace only in start node, without regex + bool const ret = pDoc->getIDocumentContentOperations().ReplaceRange(rPam, m_sOld, false); + assert(ret); (void)ret; + if (m_nSttNd != m_nEndNd) + { // in case of regex inserting paragraph breaks, join nodes... + assert(rPam.GetMark()->nContent == rPam.GetMark()->nNode.GetNode().GetTextNode()->Len()); + rPam.GetPoint()->nNode = m_nEndNd - m_nOffset; + rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(true), m_nEndCnt); + pDoc->getIDocumentContentOperations().DeleteAndJoin(rPam); + } rPam.DeleteMark(); - pNd = rPam.GetNode().GetTextNode(); + pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode(); OSL_ENSURE( pNd, "Dude, where's my TextNode?" ); - aIdx.Assign( pNd, m_nSttCnt ); } if( m_bSplitNext ) { - SwPosition aPos( *pNd, aIdx ); + SwPosition aPos(*pNd, pNd->Len()); pDoc->getIDocumentContentOperations().SplitNode( aPos, false ); pNd->RestoreMetadata(m_pMetadataUndoEnd); pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode(); - aIdx.Assign( pNd, m_nSttCnt ); // METADATA: restore pNd->RestoreMetadata(m_pMetadataUndoStart); } - if (!m_sOld.isEmpty()) - { - OUString const ins( pNd->InsertText( m_sOld, aIdx ) ); - assert(ins.getLength() == m_sOld.getLength()); // must succeed - (void) ins; - } - if( m_pHistory ) { if( pNd->GetpSwpHints() ) @@ -720,7 +717,7 @@ void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext) } rPam.GetPoint()->nNode = m_nSttNd; - rPam.GetPoint()->nContent = aIdx; + rPam.GetPoint()->nContent = m_nSttCnt; } void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext) @@ -746,7 +743,7 @@ void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext) auto xSave = std::make_unique<SwHistory>(); std::swap(m_pHistory, xSave); - DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace); m_nSetPos = m_pHistory->Count(); std::swap(xSave, m_pHistory); @@ -755,7 +752,7 @@ void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext) else { m_pHistory.reset( new SwHistory ); - DelContentIndex( *rPam.GetMark(), *rPam.GetPoint() ); + DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace); m_nSetPos = m_pHistory->Count(); if( !m_nSetPos ) { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits