sw/qa/core/unocore/unocore.cxx                |   14 ++++++++++++--
 sw/qa/uibase/wrtsh/wrtsh.cxx                  |   10 ++++------
 sw/source/core/crsr/crstrvl.cxx               |    4 +++-
 sw/source/core/crsr/viscrs.cxx                |    6 +++---
 sw/source/core/txtnode/attrcontentcontrol.cxx |    4 ----
 sw/source/core/txtnode/ndtxt.cxx              |    1 +
 sw/source/core/txtnode/thints.cxx             |   21 ++++++++++++++++++++-
 sw/source/core/unocore/unocontentcontrol.cxx  |    3 ++-
 sw/source/filter/ascii/ascatr.cxx             |    3 ++-
 sw/source/uibase/wrtsh/wrtsh3.cxx             |    4 ----
 10 files changed, 47 insertions(+), 23 deletions(-)

New commits:
commit 1e9416fac497a58be04011cfa0ffce9c74a9e397
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed May 11 09:29:05 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed May 11 12:22:48 2022 +0200

    sw content controls: introduce a word breaking dummy char at the end
    
    - this way double-clicking on the last word of a content control or the
      first word after the content control selects the correct text range,
      similar to how the same at the start of content control already worked
    
    - this allows not touching the expand flag in the SwTextContentControl
      ctor, which was overwritten by the ODT import (when the content
      control was at the end of the paragraph) anyway
    
    - hide this dummy character when accessing the paragraph content via the
      UNO API or the text export
    
    - still need to audit a few more places in follow-up commits to maintain
      the invariant that content controls have the same dummy char at the
      attribute start and end -- somwhat similar to how SwTextInputField
      does it with CH_TXT_ATR_INPUTFIELDSTART/END
    
    Change-Id: I88763d6db84afedbb865b680f040994c4d6ab7d5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134151
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 405c279f315f..ee058645c951 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -373,7 +373,7 @@ CPPUNIT_TEST_FIXTURE(SwModelTestBase, testImageTooltip)
 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlTextPortionEnum)
 {
     // Given a document with a content control around one or more text 
portions:
-    createSwDoc();
+    SwDoc* pDoc = createSwDoc();
     uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, 
uno::UNO_QUERY);
     uno::Reference<text::XTextDocument> xTextDocument(mxComponent, 
uno::UNO_QUERY);
     uno::Reference<text::XText> xText = xTextDocument->getText();
@@ -414,7 +414,17 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, 
testContentControlTextPortionEnum)
     // portion.
     assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion", "type",
                 "PortionType::ContentControl");
-    assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion", 
"portion", "test");
+    assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion", 
"portion", "test*");
+
+    // Also test the doc model, make sure that there is a dummy character at 
the start and end, so
+    // the user can explicitly decide if they want to expand the content 
control or not:
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    OUString aText = 
pWrtShell->GetCursor()->GetNode().GetTextNode()->GetText();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: ^Atest^A
+    // - Actual  : ^Atest
+    // i.e. there was no dummy character at the end.
+    CPPUNIT_ASSERT_EQUAL(OUString("\x0001test\x0001"), aText);
 }
 
 CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlCheckbox)
diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx
index 1bce34fb8a53..72d8e7060de6 100644
--- a/sw/qa/uibase/wrtsh/wrtsh.cxx
+++ b/sw/qa/uibase/wrtsh/wrtsh.cxx
@@ -125,9 +125,8 @@ CPPUNIT_TEST_FIXTURE(Test, testTickCheckboxContentControl)
     // Without the accompanying fix in place, this test would have failed:
     // - Expected: ☐
     // - Actual  : ☒
-    // i.e. the text node's text was CH_TXTATR_BREAKWORD + "Ballot Box with 
X", not just
-    // CH_TXTATR_BREAKWORD + "Ballot Box".
-    CPPUNIT_ASSERT_EQUAL(OUString(u"\x0001☐"), pTextNode->GetText());
+    // i.e. the text node's text was "Ballot Box with X", not just "Ballot 
Box".
+    CPPUNIT_ASSERT_EQUAL(OUString(u"☐"), 
pTextNode->GetExpandText(pWrtShell->GetLayout()));
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testInsertContentControl)
@@ -214,9 +213,8 @@ CPPUNIT_TEST_FIXTURE(Test, testSelectDropdownContentControl)
     // Without the accompanying fix in place, this test would have failed:
     // - Expected: red
     // - Actual  : choose an item
-    // i.e. the document text was unchanged instead of CH_TXTATR_BREAKWORD + 
display text of the
-    // first list item.
-    CPPUNIT_ASSERT_EQUAL(OUString(u"\x0001red"), pTextNode->GetText());
+    // i.e. the document text was unchanged instead of display text of the 
first list item.
+    CPPUNIT_ASSERT_EQUAL(OUString("red"), 
pTextNode->GetExpandText(pWrtShell->GetLayout()));
 }
 }
 
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index 14ed1026f4d7..ac48ef29b9c6 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -879,7 +879,9 @@ bool SwCursorShell::GotoFormatContentControl(const 
SwFormatContentControl& rCont
         sal_Int32 nStart = pTextContentControl->GetStart() + 1;
         pCursor->GetPoint()->nContent.Assign(pTextNode, nStart);
         pCursor->GetMark()->nNode = *pTextNode;
-        pCursor->GetMark()->nContent.Assign(pTextNode, 
*(pTextContentControl->End()));
+        // Don't select the CH_TXTATR_BREAKWORD itself at the end.
+        sal_Int32 nEnd = *pTextContentControl->End() - 1;
+        pCursor->GetMark()->nContent.Assign(pTextNode, nEnd);
 
         // Assume that once the placeholder is selected, the content is no 
longer the placeholder.
         pContentControl->SetShowingPlaceHolder(false);
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 12dae9ceda13..2cf7052553ac 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -649,10 +649,10 @@ void SwSelPaintRects::HighlightContentControl()
         SwTextContentControl* pCurContentControlAtCursor = nullptr;
         if (pTextNode)
         {
-            // SwTextNode::EXPAND because the LHS of the dummy character 
doesn't count, the RHS of
-            // the start of the LHS of the end counts.
+            // SwTextNode::PARENT because this way we highlight when the user 
will type inside the
+            // content control, not outside of it.
             SwTextAttr* pAttr = pTextNode->GetTextAttrAt(
-                pStart->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, 
SwTextNode::EXPAND);
+                pStart->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, 
SwTextNode::PARENT);
             if (pAttr)
             {
                 pCurContentControlAtCursor = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx 
b/sw/source/core/txtnode/attrcontentcontrol.cxx
index c28e686ce6fb..8704221a9a3b 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -318,10 +318,6 @@ 
SwTextContentControl::SwTextContentControl(SwFormatContentControl& rAttr, sal_In
 {
     rAttr.SetTextAttr(this);
     SetHasDummyChar(true);
-
-    // If the user types at the end of the content control, expand the text 
attr to the right.
-    SetLockExpandFlag(false);
-    SetDontExpand(false);
 }
 
 SwTextContentControl::~SwTextContentControl()
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index d9905fce33cc..1a3d53c4b4aa 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -3412,6 +3412,7 @@ OUString SwTextNode::GetExpandText(SwRootFrame 
const*const pLayout,
     // remove dummy characters of Input Fields
     comphelper::string::remove(aText, CH_TXT_ATR_INPUTFIELDSTART);
     comphelper::string::remove(aText, CH_TXT_ATR_INPUTFIELDEND);
+    comphelper::string::remove(aText, CH_TXTATR_BREAKWORD);
 
     if( bWithNum )
     {
diff --git a/sw/source/core/txtnode/thints.cxx 
b/sw/source/core/txtnode/thints.cxx
index b15667ec6fcc..f71db22d3fb7 100644
--- a/sw/source/core/txtnode/thints.cxx
+++ b/sw/source/core/txtnode/thints.cxx
@@ -1566,11 +1566,30 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, 
const SetAttrMode nMode )
             }
 
             // adjust end of hint to account for inserted CH_TXTATR
-            const sal_Int32 * const pEnd(pAttr->GetEnd());
+            const sal_Int32* pEnd(pAttr->GetEnd());
             if (pEnd)
             {
                 pAttr->SetEnd(*pEnd + 1);
             }
+
+            if (pAttr->Which() == RES_TXTATR_CONTENTCONTROL)
+            {
+                // Content controls have a dummy character at their end as 
well.
+                SwIndex aEndIdx(this, *pAttr->GetEnd());
+                OUString aEnd
+                    = InsertText(OUString(GetCharOfTextAttr(*pAttr)), aEndIdx, 
nInsertFlags);
+                if (aEnd.isEmpty())
+                {
+                    DestroyAttr(pAttr);
+                    return false;
+                }
+
+                pEnd = pAttr->GetEnd();
+                if (pEnd)
+                {
+                    pAttr->SetEnd(*pEnd + 1);
+                }
+            }
         }
     }
 
diff --git a/sw/source/core/unocore/unocontentcontrol.cxx 
b/sw/source/core/unocore/unocontentcontrol.cxx
index 19e5e60176d3..5c01ae826acf 100644
--- a/sw/source/core/unocore/unocontentcontrol.cxx
+++ b/sw/source/core/unocore/unocontentcontrol.cxx
@@ -316,7 +316,8 @@ bool SwXContentControl::SetContentRange(SwTextNode*& 
rpNode, sal_Int32& rStart,
             {
                 // rStart points at the first position within the content 
control.
                 rStart = pTextAttr->GetStart() + 1;
-                rEnd = *pTextAttr->End();
+                // rEnd points at the last position within the content control.
+                rEnd = *pTextAttr->End() - 1;
                 return true;
             }
         }
diff --git a/sw/source/filter/ascii/ascatr.cxx 
b/sw/source/filter/ascii/ascatr.cxx
index 97e98784afc8..42d16c7eed1a 100644
--- a/sw/source/filter/ascii/ascatr.cxx
+++ b/sw/source/filter/ascii/ascatr.cxx
@@ -342,7 +342,7 @@ static Writer& OutASC_SwTextNode( Writer& rWrt, 
SwContentNode& rNode )
             if ( !bExportSoftHyphens )
                 aOutStr = aOutStr.replaceAll(OUStringChar(CHAR_SOFTHYPHEN), 
"");
 
-            // all INWORD/BREAKWORD should be already removed by OutAttr
+            // all INWORD should be already removed by OutAttr
             // but the field-marks are not attributes so filter those
             static sal_Unicode const forbidden [] = {
                     CH_TXT_ATR_INPUTFIELDSTART,
@@ -351,6 +351,7 @@ static Writer& OutASC_SwTextNode( Writer& rWrt, 
SwContentNode& rNode )
                     CH_TXT_ATR_FIELDSTART,
                     CH_TXT_ATR_FIELDSEP,
                     CH_TXT_ATR_FIELDEND,
+                    CH_TXTATR_BREAKWORD,
                     0
                 };
             aOutStr = comphelper::string::removeAny(aOutStr, forbidden);
diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx 
b/sw/source/uibase/wrtsh/wrtsh3.cxx
index aa0badb54e61..573ea4a9752d 100644
--- a/sw/source/uibase/wrtsh/wrtsh3.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh3.cxx
@@ -140,10 +140,6 @@ bool SwWrtShell::GotoContentControl(const 
SwFormatContentControl& rContentContro
         GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter);
 
         // Update the content.
-        SwTextContentControl* pTextContentControl
-            = 
const_cast<SwFormatContentControl&>(rContentControl).GetTextAttr();
-        // If the content control is at the end of the line, then expand is 
false by default.
-        pTextContentControl->SetDontExpand(false);
         DelLeft();
         pContentControl->SetSelectedListItem(std::nullopt);
         Insert(aNewState);

Reply via email to