sw/inc/txtrfmrk.hxx                |    3 +
 sw/qa/core/txtnode/txtnode.cxx     |   41 ++++++++++++++++++
 sw/source/core/txtnode/atrref.cxx  |   52 +++++++++++++++++++++++
 sw/source/uibase/shells/basesh.cxx |   81 -------------------------------------
 4 files changed, 98 insertions(+), 79 deletions(-)

New commits:
commit 384a80fc56e75e3d1ee18ffc303db85490716829
Author:     Matti Tyrväinen <ma...@uef.fi>
AuthorDate: Mon Feb 13 21:04:56 2023 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri May 19 12:28:35 2023 +0200

    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 <ma...@uef.fi>
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

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);
 }
 }
 

Reply via email to