diff options
author | Matti Tyrväinen <matty@uef.fi> | 2023-02-13 21:04:56 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2023-05-19 12:28:35 +0200 |
commit | 384a80fc56e75e3d1ee18ffc303db85490716829 (patch) | |
tree | ba0f7b3caa753cc6cbbf8970b7894ce9f136c50b | |
parent | 595b42b686b33bbba0eeba1740277b5f6e0f5433 (diff) |
tdf#81720 Don't expand Reference Marks
Make Reference Marks behave like nesting attributes such as
Hyperlinks. This is described as the ideal behavior in
<https://bugs.documentfoundation.org/show_bug.cgi?id=81720#c24>
Change-Id: I34ddfb3a6aa0147ba4bc281ebbb6342089b7bd08
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146941
Reviewed-by: Matti <matty@uef.fi>
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
-rw-r--r-- | sw/inc/txtrfmrk.hxx | 3 | ||||
-rw-r--r-- | sw/qa/core/txtnode/txtnode.cxx | 41 | ||||
-rw-r--r-- | sw/source/core/txtnode/atrref.cxx | 52 | ||||
-rw-r--r-- | sw/source/uibase/shells/basesh.cxx | 81 |
4 files changed, 98 insertions, 79 deletions
diff --git a/sw/inc/txtrfmrk.hxx b/sw/inc/txtrfmrk.hxx index f73f8cf71019..c01387998392 100644 --- a/sw/inc/txtrfmrk.hxx +++ b/sw/inc/txtrfmrk.hxx @@ -21,6 +21,8 @@ #include "txatbase.hxx" +class SwDoc; +class SwWrtShell; class SwTextNode; // Attribute for content-/position references in text. @@ -37,6 +39,7 @@ public: virtual const sal_Int32* GetEnd() const override; // SwTextAttr virtual void SetEnd(sal_Int32) override; // SwTextAttr + void UpdateFieldContent(SwDoc* pDoc, SwWrtShell& rWrtSh, OUString aContent); // get and set TextNode pointer inline const SwTextNode& GetTextNode() const; diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx index f99cc3dd54dd..640df69ed211 100644 --- a/sw/qa/core/txtnode/txtnode.cxx +++ b/sw/qa/core/txtnode/txtnode.cxx @@ -11,6 +11,7 @@ #include <LibreOfficeKit/LibreOfficeKitEnums.h> #include <comphelper/lok.hxx> +#include <comphelper/propertyvalue.hxx> #include <sfx2/viewsh.hxx> #include <vcl/gdimtf.hxx> #include <vcl/scheduler.hxx> @@ -31,6 +32,7 @@ #include <ndtxt.hxx> #include <textcontentcontrol.hxx> #include <swdtflvr.hxx> +#include <txtrfmrk.hxx> /// Covers sw/source/core/txtnode/ fixes. class SwCoreTxtnodeTest : public SwModelTestBase @@ -219,6 +221,45 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testSplitNodeSuperscriptCopy) CPPUNIT_ASSERT(!aSet.HasItem(RES_CHRATR_ESCAPEMENT)); } +CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testDontExpandRefmark) +{ + // Given a document with a refmark: + createSwDoc(); + + uno::Sequence<css::beans::PropertyValue> aArgs = { + comphelper::makePropertyValue("TypeName", uno::Any(OUString("SetRef"))), + comphelper::makePropertyValue( + "Name", uno::Any(OUString("ZOTERO_ITEM CSL_CITATION {} RNDpyJknp173F"))), + comphelper::makePropertyValue("Content", uno::Any(OUString("foo"))), + }; + dispatchCommand(mxComponent, ".uno:InsertField", aArgs); + + SwDoc* pDoc = getSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwPosition& rCursor = *pWrtShell->GetCursor()->GetPoint(); + SwTextNode* pTextNode = rCursor.GetNode().GetTextNode(); + std::vector<SwTextAttr*> aAttrs + = pTextNode->GetTextAttrsAt(rCursor.GetContentIndex(), RES_TXTATR_REFMARK); + + auto& rRefmark = const_cast<SwFormatRefMark&>(aAttrs[0]->GetRefMark()); + auto pTextRefMark = const_cast<SwTextRefMark*>(rRefmark.GetTextRefMark()); + + // When typing after the refmark... + pWrtShell->SttEndDoc(/*bStt=*/true); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 3, /*bBasicCall=*/false); + pWrtShell->Insert(" bar"); + + // and skipping back to insert a comma after the refmark + pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/false, 4, /*bBasicCall=*/false); + pWrtShell->Insert(","); + + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 3 + // - Actual : 4 + // i.e. the reference mark expanded + CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(*pTextRefMark->End())); +} + CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testInsertDropDownContentControlTwice) { // Given an already selected dropdown content control: diff --git a/sw/source/core/txtnode/atrref.cxx b/sw/source/core/txtnode/atrref.cxx index b93b62e7433a..ffb4509aee70 100644 --- a/sw/source/core/txtnode/atrref.cxx +++ b/sw/source/core/txtnode/atrref.cxx @@ -32,6 +32,9 @@ #include <comphelper/lok.hxx> #include <doc.hxx> #include <ndtxt.hxx> +#include <pam.hxx> +#include <translatehelper.hxx> +#include <wrtsh.hxx> SwFormatRefMark::~SwFormatRefMark( ) { @@ -118,6 +121,8 @@ SwTextRefMark::SwTextRefMark( SwFormatRefMark& rAttr, } SetDontMoveAttr( true ); SetOverlapAllowedAttr( true ); + SetDontExpand( true ); // like hyperlinks, reference markers shouldn't expand + SetLockExpandFlag( true ); // protect the flag } SwTextRefMark::~SwTextRefMark() @@ -153,6 +158,53 @@ void SwTextRefMark::SetEnd(sal_Int32 n) m_pHints->EndPosChanged(); } +void SwTextRefMark::UpdateFieldContent(SwDoc* pDoc, SwWrtShell& rWrtSh, OUString aContent) +{ + if (!this->End()) + { + return; + } + + // Insert markers to remember where the paste positions are. + const SwTextNode& rTextNode = this->GetTextNode(); + SwPaM aMarkers(SwPosition(rTextNode, *this->End())); + IDocumentContentOperations& rIDCO = pDoc->getIDocumentContentOperations(); + this->SetLockExpandFlag(false); + this->SetDontExpand(false); + if (rIDCO.InsertString(aMarkers, "XY")) + { + SwPaM aPasteEnd(SwPosition(rTextNode, *this->End())); + aPasteEnd.Move(fnMoveBackward, GoInContent); + + // Paste HTML content. + SwPaM* pCursorPos = rWrtSh.GetCursor(); + *pCursorPos = aPasteEnd; + SwTranslateHelper::PasteHTMLToPaM(rWrtSh, pCursorPos, aContent.toUtf8(), true); + + // Update the refmark to point to the new content. + sal_Int32 nOldStart = this->GetStart(); + sal_Int32 nNewStart = *this->End(); + // First grow it to include text till the end of the paste position. + this->SetEnd(aPasteEnd.GetPoint()->GetContentIndex()); + // Then shrink it to only start at the paste start: we know that the refmark was + // truncated to the paste start, as the refmark has to stay inside a single text node + this->SetStart(nNewStart); + rTextNode.GetSwpHints().SortIfNeedBe(); + SwPaM aEndMarker(*aPasteEnd.GetPoint()); + aEndMarker.SetMark(); + aEndMarker.GetMark()->AdjustContent(1); + SwPaM aStartMarker(SwPosition(rTextNode, nOldStart), SwPosition(rTextNode, nNewStart)); + + // Remove markers. The start marker includes the old content as well. + rIDCO.DeleteAndJoin(aStartMarker); + rIDCO.DeleteAndJoin(aEndMarker); + } + // Restore flags. + this->SetDontExpand(true); + this->SetLockExpandFlag(true); +} + + void SwTextRefMark::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextRefMark")); diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx index 4add92e90068..9b917005bfed 100644 --- a/sw/source/uibase/shells/basesh.cxx +++ b/sw/source/uibase/shells/basesh.cxx @@ -840,45 +840,7 @@ bool UpdateFieldContents(SfxRequest& rReq, SwWrtShell& rWrtSh) OUString aContent = aMap["Content"].get<OUString>(); auto pTextRefMark = const_cast<SwTextRefMark*>(pRefMark->GetTextRefMark()); - if (!pTextRefMark->End()) - { - continue; - } - - // Insert markers to remember where the paste positions are. - const SwTextNode& rTextNode = pTextRefMark->GetTextNode(); - SwPaM aMarkers(SwPosition(rTextNode, *pTextRefMark->End())); - IDocumentContentOperations& rIDCO = pDoc->getIDocumentContentOperations(); - pTextRefMark->SetDontExpand(false); - bool bSuccess = rIDCO.InsertString(aMarkers, "XY"); - if (bSuccess) - { - SwPaM aPasteEnd(SwPosition(rTextNode, *pTextRefMark->End())); - aPasteEnd.Move(fnMoveBackward, GoInContent); - - // Paste HTML content. - SwPaM* pCursorPos = rWrtSh.GetCursor(); - *pCursorPos = aPasteEnd; - SwTranslateHelper::PasteHTMLToPaM(rWrtSh, pCursorPos, aContent.toUtf8(), true); - - // Update the refmark to point to the new content. - sal_Int32 nOldStart = pTextRefMark->GetStart(); - sal_Int32 nNewStart = *pTextRefMark->End(); - // First grow it to include text till the end of the paste position. - pTextRefMark->SetEnd(aPasteEnd.GetPoint()->GetContentIndex()); - // Then shrink it to only start at the paste start: we know that the refmark was - // truncated to the paste start, as the refmark has to stay inside a single text node - pTextRefMark->SetStart(nNewStart); - rTextNode.GetSwpHints().SortIfNeedBe(); - SwPaM aEndMarker(*aPasteEnd.GetPoint()); - aEndMarker.SetMark(); - aEndMarker.GetMark()->AdjustContent(1); - SwPaM aStartMarker(SwPosition(rTextNode, nOldStart), SwPosition(rTextNode, nNewStart)); - - // Remove markers. The start marker includes the old content as well. - rIDCO.DeleteAndJoin(aStartMarker); - rIDCO.DeleteAndJoin(aEndMarker); - } + pTextRefMark->UpdateFieldContent(pDoc, rWrtSh, aContent); } rWrtSh.EndAction(); @@ -945,46 +907,7 @@ void UpdateFieldContent(SfxRequest& rReq, SwWrtShell& rWrtSh) OUString aContent = aMap["Content"].get<OUString>(); auto pTextRefMark = const_cast<SwTextRefMark*>(rRefmark.GetTextRefMark()); - if (!pTextRefMark->End()) - { - return; - } - - // Insert markers to remember where the paste positions are. - const SwTextNode& rTextNode = pTextRefMark->GetTextNode(); - SwPaM aMarkers(SwPosition(rTextNode, *pTextRefMark->End())); - IDocumentContentOperations& rIDCO = pDoc->getIDocumentContentOperations(); - pTextRefMark->SetDontExpand(false); - if (!rIDCO.InsertString(aMarkers, "XY")) - { - return; - } - - SwPaM aPasteEnd(SwPosition(rTextNode, *pTextRefMark->End())); - aPasteEnd.Move(fnMoveBackward, GoInContent); - - // Paste HTML content. - SwPaM* pCursorPos = rWrtSh.GetCursor(); - *pCursorPos = aPasteEnd; - SwTranslateHelper::PasteHTMLToPaM(rWrtSh, pCursorPos, aContent.toUtf8(), true); - - // Update the refmark to point to the new content. - sal_Int32 nOldStart = pTextRefMark->GetStart(); - sal_Int32 nNewStart = *pTextRefMark->End(); - // First grow it to include text till the end of the paste position. - pTextRefMark->SetEnd(aPasteEnd.GetPoint()->GetContentIndex()); - // Then shrink it to only start at the paste start: we know that the refmark was - // truncated to the paste start, as the refmark has to stay inside a single text node - pTextRefMark->SetStart(nNewStart); - rTextNode.GetSwpHints().SortIfNeedBe(); - SwPaM aEndMarker(*aPasteEnd.GetPoint()); - aEndMarker.SetMark(); - aEndMarker.GetMark()->AdjustContent(1); - SwPaM aStartMarker(SwPosition(rTextNode, nOldStart), SwPosition(rTextNode, nNewStart)); - - // Remove markers. The start marker includes the old content as well. - rIDCO.DeleteAndJoin(aStartMarker); - rIDCO.DeleteAndJoin(aEndMarker); + pTextRefMark->UpdateFieldContent(pDoc, rWrtSh, aContent); } } |