sw/inc/formatcontentcontrol.hxx   |   10 ++++++++++
 sw/qa/core/crsr/crsr.cxx          |   28 ++++++++++++++++++++++++++++
 sw/source/core/crsr/pam.cxx       |   26 ++++++++++++++++++++++++++
 sw/source/uibase/wrtsh/wrtsh3.cxx |    2 ++
 4 files changed, 66 insertions(+)

New commits:
commit bca801438f5208b7fbd4402927267958d95ba1d4
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Jun 10 08:16:48 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Jun 15 08:13:06 2022 +0200

    sw content controls: reject typing inside checkbox or picture content 
controls
    
    Content controls are editable by default (and not only editable, but
    also capable of hosting rich text content), and Writer doesn't limit
    the possibility to edit explicitly, either.
    
    Certain content control types (checkbox and picture for now) limit the
    hosted content though: checkbox overwrites the content on click and
    picture is meant to host a single as-char anchored image. So far the
    simple implementation Writer didn't enforce these limits, leading the
    unexpected behavior when clicking on checkbox content controls (possibly
    not only a checked/non-checked checkmark was toggled, but other content
    was removed).
    
    Fix the problem by making these content control types read-only: this is
    what also Word does and this way you can't loose the content when you
    can't enter it earlier.
    
    We may want to also do this for dropdowns in the future, once combo
    boxes will be supported.
    
    (cherry picked from commit c321498f915f4e8b3f4853232860ce040ab48e46)
    
    Change-Id: I9d44206b3c719a64ec552f2fa0a076901094163e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135821
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index 16335c2e0b63..3bb9f6d64489 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -159,6 +159,12 @@ class SW_DLLPUBLIC SwContentControl : public 
sw::BroadcastingModify
     /// Stores a date timestamp, in case the doc model is not yet updated.
     std::optional<double> m_oSelectedDate;
 
+    /**
+     * E.g. checkbox is read-only by default, but we still update contents on 
interaction
+     * internally. This flag is true for the duration of that interaction.
+     */
+    bool m_bReadWrite = false;
+
 public:
     SwTextContentControl* GetTextAttr() const;
 
@@ -291,6 +297,10 @@ public:
     void SetColor(const OUString& rColor) { m_aColor = rColor; }
 
     OUString GetColor() const { return m_aColor; }
+
+    void SetReadWrite(bool bReadWrite) { m_bReadWrite = bReadWrite; }
+
+    bool GetReadWrite() const { return m_bReadWrite; }
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/crsr/crsr.cxx b/sw/qa/core/crsr/crsr.cxx
index 62cafc6d63a5..7732727cbb3a 100644
--- a/sw/qa/core/crsr/crsr.cxx
+++ b/sw/qa/core/crsr/crsr.cxx
@@ -136,6 +136,34 @@ CPPUNIT_TEST_FIXTURE(SwCoreCrsrTest, 
testContentControlLineBreak)
     CPPUNIT_ASSERT_EQUAL(OUString("t\nest"), 
pTextNode->GetExpandText(pWrtShell->GetLayout()));
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreCrsrTest, testContentControlReadOnly)
+{
+    // Given a document with a checkbox content control:
+    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();
+    uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+    xText->insertString(xCursor, u"☐", /*bAbsorb=*/false);
+    xCursor->gotoStart(/*bExpand=*/false);
+    xCursor->gotoEnd(/*bExpand=*/true);
+    uno::Reference<text::XTextContent> xContentControl(
+        xMSF->createInstance("com.sun.star.text.ContentControl"), 
uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, 
uno::UNO_QUERY);
+    xContentControlProps->setPropertyValue("Checkbox", uno::Any(true));
+    xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+    // When entering the content control:
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/true);
+    pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+
+    // Then make sure that the cursor is read-only:
+    // Without the accompanying fix in place, this test would have failed, it 
was possible to type
+    // into the checkbox content control, just to loose the typed content on 
the next click.
+    CPPUNIT_ASSERT(pWrtShell->HasReadonlySel());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/pam.cxx b/sw/source/core/crsr/pam.cxx
index 4add6f1ee201..e376d5042bbb 100644
--- a/sw/source/core/crsr/pam.cxx
+++ b/sw/source/core/crsr/pam.cxx
@@ -51,6 +51,7 @@
 #include <rtl/ustrbuf.hxx>
 
 #include <editsh.hxx>
+#include <textcontentcontrol.hxx>
 
 // for the dump "MSC-" compiler
 static sal_Int32 GetSttOrEnd( bool bCondition, const SwContentNode& rNd )
@@ -813,6 +814,31 @@ bool SwPaM::HasReadonlySel( bool bFormView ) const
         }
     }
 
+    if (!bRet)
+    {
+        // See if we're inside a read-only content control.
+        const SwPosition* pStart = Start();
+        SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+        if (pTextNode)
+        {
+            sal_Int32 nIndex = pStart->nContent.GetIndex();
+            SwTextAttr* pAttr
+                = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, 
SwTextNode::PARENT);
+            auto pTextContentControl = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+            if (pTextContentControl)
+            {
+                const SwFormatContentControl& rFormatContentControl
+                    = pTextContentControl->GetContentControl();
+                std::shared_ptr<SwContentControl> pContentControl
+                    = rFormatContentControl.GetContentControl();
+                if (pContentControl && !pContentControl->GetReadWrite())
+                {
+                    bRet = pContentControl->GetCheckbox() || 
pContentControl->GetPicture();
+                }
+            }
+        }
+    }
+
     return bRet;
 }
 
diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx 
b/sw/source/uibase/wrtsh/wrtsh3.cxx
index e4b594770282..27ca0353bac1 100644
--- a/sw/source/uibase/wrtsh/wrtsh3.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh3.cxx
@@ -148,9 +148,11 @@ bool SwWrtShell::GotoContentControl(const 
SwFormatContentControl& rContentContro
         GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter);
 
         // Toggle the state.
+        pContentControl->SetReadWrite(true);
         DelLeft();
         pContentControl->SetChecked(!pContentControl->GetChecked());
         Insert(aNewState);
+        pContentControl->SetReadWrite(false);
 
         GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter);
         LockView(/*bViewLocked=*/false);

Reply via email to