sw/inc/ndtxt.hxx | 6 +++++ sw/qa/core/txtnode/data/fly-anchor-undo.odt |binary sw/qa/core/txtnode/txtnode.cxx | 25 +++++++++++++++++++++ sw/source/core/txtnode/ndtxt.cxx | 33 +++++++++++++++++----------- sw/source/core/undo/undel.cxx | 2 + 5 files changed, 54 insertions(+), 12 deletions(-)
New commits: commit 1734e97222324c137ecd084ad2464abdff2698d1 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Mon Sep 27 19:59:54 2021 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Sep 28 08:10:40 2021 +0200 tdf#143819 sw: fix undo not restoring adjusted anchor Regression from commit 0a4d77f35e96e4dfdf69cc5ceb788ddaa849084c (SwTxtNode::Update: don't move anchors at insert position, 2014-10-06), the problem is that in case backspace adjusts the anchor point of a fly and undo does an insert (which now doesn't adjust the anchor point), then the user action and its undo is not in sync. Fix this by informing SwTextNode::Update() if an undo is in progress and doing the old behavior in that case. Change-Id: I0b3f3954be11420846f84287e486b993b2dc39e8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/122727 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx index 6b64fdef8b19..d3320d995d06 100644 --- a/sw/inc/ndtxt.hxx +++ b/sw/inc/ndtxt.hxx @@ -117,6 +117,9 @@ class SW_DLLPUBLIC SwTextNode final Needed to avoid duplicate handling of attribute change actions. */ bool mbInSetOrResetAttr; + /// Is an undo operation in progress? + bool m_bInUndo; + std::optional< OUString > m_oNumStringCache; css::uno::WeakReference<css::text::XTextContent> m_wXParagraph; @@ -790,6 +793,7 @@ public: /// sfx2::Metadatable virtual ::sfx2::IXmlIdRegistry& GetRegistry() override; virtual bool IsInClipboard() const override; + /// Is this node in the undo array? virtual bool IsInUndo() const override; virtual bool IsInContent() const override; virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override; @@ -811,6 +815,8 @@ public: static bool IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich, bool bIsCharStyle = false); void FormatDropNotify(const SwFormatDrop& rDrop) override { TriggerNodeUpdate(sw::LegacyModifyHint(&rDrop, &rDrop)); }; + + void SetInSwUndo(bool bInUndo); }; inline SwpHints & SwTextNode::GetSwpHints() diff --git a/sw/qa/core/txtnode/data/fly-anchor-undo.odt b/sw/qa/core/txtnode/data/fly-anchor-undo.odt new file mode 100644 index 000000000000..dd2093161fa5 Binary files /dev/null and b/sw/qa/core/txtnode/data/fly-anchor-undo.odt differ diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx index f4bb070ac972..5b43ee8591f0 100644 --- a/sw/qa/core/txtnode/txtnode.cxx +++ b/sw/qa/core/txtnode/txtnode.cxx @@ -154,6 +154,31 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testTitleFieldInvalidate) comphelper::LibreOfficeKit::setActive(false); } +CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testFlyAnchorUndo) +{ + // Given a document with a fly frame, anchored after the last char of the document: + load(DATA_DIRECTORY, "fly-anchor-undo.odt"); + SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + SwDocShell* pShell = pTextDoc->GetDocShell(); + SwDoc* pDoc = pShell->GetDoc(); + const SwFrameFormats& rSpz = *pDoc->GetSpzFrameFormats(); + sal_Int32 nExpected = rSpz[0]->GetAnchor().GetContentAnchor()->nContent.GetIndex(); + + // When deleting that last character and undoing it: + SwWrtShell* pWrtShell = pShell->GetWrtShell(); + pWrtShell->SttEndDoc(/*bStt=*/false); + pWrtShell->DelLeft(); + pWrtShell->Undo(); + + // Then make sure the anchor position after the undo is the same as the original: + sal_Int32 nActual = rSpz[0]->GetAnchor().GetContentAnchor()->nContent.GetIndex(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 3 + // - Actual : 2 + // i.e. the anchor position was left unchanged by the undo. + CPPUNIT_ASSERT_EQUAL(nExpected, nActual); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index 05548bb3e69b..8d5a58eccb25 100644 --- a/sw/source/core/txtnode/ndtxt.cxx +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -194,7 +194,8 @@ SwTextNode::SwTextNode( const SwNodeIndex &rWhere, SwTextFormatColl *pTextColl, m_bLastOutlineState( false ), m_bNotifiable( false ), mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ), - mbInSetOrResetAttr( false ) + mbInSetOrResetAttr( false ), + m_bInUndo(false) { InitSwParaStatistics( true ); @@ -1426,20 +1427,23 @@ void SwTextNode::Update( } // at-char anchored flys shouldn't be moved, either. - std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys()); - for (size_t i = 0; i != rFlys.size(); ++i) + if (!m_bInUndo) { - SwFrameFormat const*const pFormat = rFlys[i]; - const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); - const SwPosition* pContentAnchor = rAnchor.GetContentAnchor(); - if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pContentAnchor) + std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys()); + for (size_t i = 0; i != rFlys.size(); ++i) { - // The fly is at-char anchored and has an anchor position. - SwIndex& rEndIdx = const_cast<SwIndex&>(pContentAnchor->nContent); - if (&pContentAnchor->nNode.GetNode() == this && rEndIdx.GetIndex() == rPos.GetIndex()) + SwFrameFormat const*const pFormat = rFlys[i]; + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + const SwPosition* pContentAnchor = rAnchor.GetContentAnchor(); + if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pContentAnchor) { - // The anchor position is exactly our insert position. - rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex()); + // The fly is at-char anchored and has an anchor position. + SwIndex& rEndIdx = const_cast<SwIndex&>(pContentAnchor->nContent); + if (&pContentAnchor->nNode.GetNode() == this && rEndIdx.GetIndex() == rPos.GetIndex()) + { + // The anchor position is exactly our insert position. + rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex()); + } } } } @@ -4859,6 +4863,11 @@ bool SwTextNode::SetAttr( const SfxItemSet& rSet ) return bRet; } +void SwTextNode::SetInSwUndo(bool bInUndo) +{ + m_bInUndo = bInUndo; +} + namespace { // Helper class for special handling of resetting attributes at text node: // In constructor an instance of the helper class recognize whose attributes diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx index f3fbf5eac553..790dd2e3ea67 100644 --- a/sw/source/core/undo/undel.cxx +++ b/sw/source/core/undo/undel.cxx @@ -1015,8 +1015,10 @@ void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext) // SectionNode mode and selection from top to bottom: // -> in StartNode is still the rest of the Join => delete aPos.nContent.Assign( pTextNd, m_nSttContent ); + pTextNd->SetInSwUndo(true); OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent, SwInsertFlags::NOHINTEXPAND) ); + pTextNd->SetInSwUndo(false); assert(ins.getLength() == m_aSttStr->getLength()); // must succeed (void) ins; // METADATA: restore