offapi/com/sun/star/text/ContentControl.idl   |    6 ++
 sw/inc/formatcontentcontrol.hxx               |    8 +++
 sw/inc/unoprnms.hxx                           |    1 
 sw/qa/core/crsr/crsr.cxx                      |   19 +++++++++
 sw/qa/core/unocore/unocore.cxx                |   53 ++++++++++++++++++++++++++
 sw/source/core/crsr/pam.cxx                   |   11 ++++-
 sw/source/core/txtnode/attrcontentcontrol.cxx |    7 +++
 sw/source/core/unocore/unocontentcontrol.cxx  |   29 ++++++++++++++
 sw/source/core/unocore/unomap1.cxx            |    1 
 sw/source/uibase/wrtsh/wrtsh1.cxx             |    5 ++
 sw/source/uibase/wrtsh/wrtsh3.cxx             |    2 
 11 files changed, 141 insertions(+), 1 deletion(-)

New commits:
commit 5f3b8f632fa71891cb8e5b1b6641035c781a554c
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Sep 21 12:09:34 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Sep 22 08:32:55 2022 +0200

    sw content controls, combo box: make the dropdown case read-only
    
    Dropdown should be read-only (select from existing list items only),
    combo box should be read-write (accepting free-form user input as well).
    
    Now that we have a combo box, allow editing a combo box's content, but
    turn dropdown into a read-only control.
    
    (cherry picked from commit 37656a47d8797d45d706a17ab8843dcb8db90bbc)
    
    Conflicts:
            sw/qa/core/crsr/crsr.cxx
    
    Change-Id: Ie9434eaf2580be8ce154ec1126f7768eb9401254
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140348
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/qa/core/crsr/crsr.cxx b/sw/qa/core/crsr/crsr.cxx
index 7732727cbb3a..5f06658b528e 100644
--- a/sw/qa/core/crsr/crsr.cxx
+++ b/sw/qa/core/crsr/crsr.cxx
@@ -19,11 +19,13 @@
 #include <comphelper/propertysequence.hxx>
 #include <svl/srchitem.hxx>
 #include <vcl/scheduler.hxx>
+#include <comphelper/propertyvalue.hxx>
 
 #include <docsh.hxx>
 #include <unotxdoc.hxx>
 #include <wrtsh.hxx>
 #include <ndtxt.hxx>
+#include <formatcontentcontrol.hxx>
 
 constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/core/crsr/data/";
 
@@ -164,6 +166,23 @@ CPPUNIT_TEST_FIXTURE(SwCoreCrsrTest, 
testContentControlReadOnly)
     CPPUNIT_ASSERT(pWrtShell->HasReadonlySel());
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreCrsrTest, testDropdownContentControl)
+{
+    // Given a document with a dropdown content control:
+    SwDoc* pDoc = createSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST);
+
+    // When entering the content control:
+    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 drop-down content control, providing content that is not one 
of the list items.
+    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 e376d5042bbb..277af8905a07 100644
--- a/sw/source/core/crsr/pam.cxx
+++ b/sw/source/core/crsr/pam.cxx
@@ -833,7 +833,16 @@ bool SwPaM::HasReadonlySel( bool bFormView ) const
                     = rFormatContentControl.GetContentControl();
                 if (pContentControl && !pContentControl->GetReadWrite())
                 {
-                    bRet = pContentControl->GetCheckbox() || 
pContentControl->GetPicture();
+                    switch (pContentControl->GetType())
+                    {
+                        case SwContentControlType::CHECKBOX:
+                        case SwContentControlType::PICTURE:
+                        case SwContentControlType::DROP_DOWN_LIST:
+                            bRet = true;
+                            break;
+                        default:
+                            break;
+                    }
                 }
             }
         }
diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx 
b/sw/source/uibase/wrtsh/wrtsh3.cxx
index 27ca0353bac1..58204886933e 100644
--- a/sw/source/uibase/wrtsh/wrtsh3.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh3.cxx
@@ -172,9 +172,11 @@ bool SwWrtShell::GotoContentControl(const 
SwFormatContentControl& rContentContro
         GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter);
 
         // Update the content.
+        pContentControl->SetReadWrite(true);
         DelLeft();
         pContentControl->SetSelectedListItem(std::nullopt);
         Insert(aNewState);
+        pContentControl->SetReadWrite(false);
 
         GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter);
         LockView(/*bViewLocked=*/false);
commit 84a30b7cb43cb90c2b8aa81862f09c5f143c34d6
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Sep 21 08:24:17 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Sep 22 08:32:41 2022 +0200

    sw content controls, combo box: add doc model & UNO API
    
    This is similar to dropdowns, but combo box allow free-form user input,
    while dropdown is meant to enforce that the content is one of the list
    items.
    
    (cherry picked from commit 276f3a3ce52ca422bf5ebccfa2c926d3e87d5eab)
    
    Change-Id: I4ae226c55f70b2b3237021348e21b7d184e8a5ab
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140347
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/offapi/com/sun/star/text/ContentControl.idl 
b/offapi/com/sun/star/text/ContentControl.idl
index 641ffd9cebd4..af5be9ac251e 100644
--- a/offapi/com/sun/star/text/ContentControl.idl
+++ b/offapi/com/sun/star/text/ContentControl.idl
@@ -93,6 +93,12 @@ service ContentControl
     /** The color: just remembered.
     */
     [optional, property] string Color;
+
+    /** Combo box that allows free-form text as well, i.e. not dropdown.
+
+        @since LibreOffice 7.5
+    */
+    [optional, property] boolean ComboBox;
 };
 
 
diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index d0c56b6ae326..e5e1cf0de1e9 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -45,6 +45,7 @@ enum class SwContentControlType
     PICTURE,
     DATE,
     PLAIN_TEXT,
+    COMBO_BOX,
 };
 
 /// SfxPoolItem subclass that wraps an SwContentControl.
@@ -146,6 +147,9 @@ class SW_DLLPUBLIC SwContentControl : public 
sw::BroadcastingModify
     /// Plain text, i.e. not rich text.
     bool m_bPlainText = false;
 
+    /// Same as drop-down, but free-form input is also accepted.
+    bool m_bComboBox = false;
+
     /// The placeholder's doc part: just remembered.
     OUString m_aPlaceholderDocPart;
 
@@ -265,6 +269,10 @@ public:
 
     bool GetPlainText() const { return m_bPlainText; }
 
+    void SetComboBox(bool bComboBox) { m_bComboBox = bComboBox; }
+
+    bool GetComboBox() const { return m_bComboBox; }
+
     void SetPlaceholderDocPart(const OUString& rPlaceholderDocPart)
     {
         m_aPlaceholderDocPart = rPlaceholderDocPart;
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index f31523e73d3e..115638ae4e3a 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -881,6 +881,7 @@
 #define UNO_NAME_DATE_LANGUAGE "DateLanguage"
 #define UNO_NAME_CURRENT_DATE "CurrentDate"
 #define UNO_NAME_PLAIN_TEXT "PlainText"
+#define UNO_NAME_COMBO_BOX "ComboBox"
 #define UNO_NAME_PLACEHOLDER_DOC_PART "PlaceholderDocPart"
 #define UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS "DataBindingPrefixMappings"
 #define UNO_NAME_DATA_BINDING_XPATH "DataBindingXpath"
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 55c04614b209..658d4f9bfa3f 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -649,6 +649,59 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, 
testContentControlPlainText)
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(6), *pAttr->End());
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlComboBox)
+{
+    // Given an empty document:
+    SwDoc* pDoc = createSwDoc();
+
+    // When inserting a combobox content control:
+    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, "test", /*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);
+    {
+        uno::Sequence<beans::PropertyValues> aListItems = {
+            {
+                comphelper::makePropertyValue("DisplayText", 
uno::Any(OUString("red"))),
+                comphelper::makePropertyValue("Value", 
uno::Any(OUString("R"))),
+            },
+            {
+                comphelper::makePropertyValue("DisplayText", 
uno::Any(OUString("green"))),
+                comphelper::makePropertyValue("Value", 
uno::Any(OUString("G"))),
+            },
+            {
+                comphelper::makePropertyValue("DisplayText", 
uno::Any(OUString("blue"))),
+                comphelper::makePropertyValue("Value", 
uno::Any(OUString("B"))),
+            },
+        };
+        xContentControlProps->setPropertyValue("ListItems", 
uno::Any(aListItems));
+        // Without the accompanying fix in place, this test would have failed 
with:
+        // An uncaught exception of type 
com.sun.star.beans.UnknownPropertyException
+        xContentControlProps->setPropertyValue("ComboBox", uno::Any(true));
+    }
+    xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+    // Then make sure that the specified properties are set:
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    SwTextNode* pTextNode = 
pWrtShell->GetCursor()->GetPoint()->nNode.GetNode().GetTextNode();
+    SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, 
RES_TXTATR_CONTENTCONTROL);
+    auto pTextContentControl = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+    auto& rFormatContentControl
+        = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+    std::shared_ptr<SwContentControl> pContentControl = 
rFormatContentControl.GetContentControl();
+    std::vector<SwContentControlListItem> aListItems = 
pContentControl->GetListItems();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems.size());
+    CPPUNIT_ASSERT_EQUAL(OUString("red"), aListItems[0].m_aDisplayText);
+    CPPUNIT_ASSERT_EQUAL(OUString("R"), aListItems[0].m_aValue);
+    CPPUNIT_ASSERT(pContentControl->GetComboBox());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx 
b/sw/source/core/txtnode/attrcontentcontrol.cxx
index 39b048fdf59e..9d0bf82b1a4c 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -335,6 +335,11 @@ SwContentControlType SwContentControl::GetType() const
         return SwContentControlType::CHECKBOX;
     }
 
+    if (m_bComboBox)
+    {
+        return SwContentControlType::COMBO_BOX;
+    }
+
     if (!m_aListItems.empty())
     {
         return SwContentControlType::DROP_DOWN_LIST;
@@ -385,6 +390,8 @@ void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) 
const
                                       
BAD_CAST(m_aCurrentDate.toUtf8().getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("plain-text"),
                                       
BAD_CAST(OString::boolean(m_bPlainText).getStr()));
+    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("combo-box"),
+                                      
BAD_CAST(OString::boolean(m_bComboBox).getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, 
BAD_CAST("placeholder-doc-part"),
                                       
BAD_CAST(m_aPlaceholderDocPart.toUtf8().getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, 
BAD_CAST("data-binding-prefix-mappings"),
diff --git a/sw/source/core/unocore/unocontentcontrol.cxx 
b/sw/source/core/unocore/unocontentcontrol.cxx
index 110665cdc058..ed4e6d854776 100644
--- a/sw/source/core/unocore/unocontentcontrol.cxx
+++ b/sw/source/core/unocore/unocontentcontrol.cxx
@@ -167,6 +167,7 @@ public:
     OUString m_aDateLanguage;
     OUString m_aCurrentDate;
     bool m_bPlainText;
+    bool m_bComboBox;
     OUString m_aPlaceholderDocPart;
     OUString m_aDataBindingPrefixMappings;
     OUString m_aDataBindingXpath;
@@ -188,6 +189,7 @@ public:
         , m_bPicture(false)
         , m_bDate(false)
         , m_bPlainText(false)
+        , m_bComboBox(false)
     {
         if (m_pContentControl)
         {
@@ -537,6 +539,7 @@ void SwXContentControl::AttachImpl(const 
uno::Reference<text::XTextRange>& xText
     pContentControl->SetDateLanguage(m_pImpl->m_aDateLanguage);
     pContentControl->SetCurrentDate(m_pImpl->m_aCurrentDate);
     pContentControl->SetPlainText(m_pImpl->m_bPlainText);
+    pContentControl->SetComboBox(m_pImpl->m_bComboBox);
     pContentControl->SetPlaceholderDocPart(m_pImpl->m_aPlaceholderDocPart);
     
pContentControl->SetDataBindingPrefixMappings(m_pImpl->m_aDataBindingPrefixMappings);
     pContentControl->SetDataBindingXpath(m_pImpl->m_aDataBindingXpath);
@@ -871,6 +874,21 @@ void SAL_CALL SwXContentControl::setPropertyValue(const 
OUString& rPropertyName,
             }
         }
     }
+    else if (rPropertyName == UNO_NAME_COMBO_BOX)
+    {
+        bool bValue;
+        if (rValue >>= bValue)
+        {
+            if (m_pImpl->m_bIsDescriptor)
+            {
+                m_pImpl->m_bComboBox = bValue;
+            }
+            else
+            {
+                m_pImpl->m_pContentControl->SetComboBox(bValue);
+            }
+        }
+    }
     else if (rPropertyName == UNO_NAME_PLACEHOLDER_DOC_PART)
     {
         OUString aValue;
@@ -1091,6 +1109,17 @@ uno::Any SAL_CALL 
SwXContentControl::getPropertyValue(const OUString& rPropertyN
             aRet <<= m_pImpl->m_pContentControl->GetPlainText();
         }
     }
+    else if (rPropertyName == UNO_NAME_COMBO_BOX)
+    {
+        if (m_pImpl->m_bIsDescriptor)
+        {
+            aRet <<= m_pImpl->m_bComboBox;
+        }
+        else
+        {
+            aRet <<= m_pImpl->m_pContentControl->GetComboBox();
+        }
+    }
     else if (rPropertyName == UNO_NAME_PLACEHOLDER_DOC_PART)
     {
         if (m_pImpl->m_bIsDescriptor)
diff --git a/sw/source/core/unocore/unomap1.cxx 
b/sw/source/core/unocore/unomap1.cxx
index d082fc4f7202..4235fd2ebc1c 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -1038,6 +1038,7 @@ const SfxItemPropertyMapEntry* 
SwUnoPropertyMapProvider::GetContentControlProper
         { u"" UNO_NAME_DATE_LANGUAGE, 0, cppu::UnoType<OUString>::get(), 
PROPERTY_NONE, 0 },
         { u"" UNO_NAME_CURRENT_DATE, 0, cppu::UnoType<OUString>::get(), 
PROPERTY_NONE, 0 },
         { u"" UNO_NAME_PLAIN_TEXT, 0, cppu::UnoType<bool>::get(), 
PROPERTY_NONE, 0 },
+        { u"" UNO_NAME_COMBO_BOX, 0, cppu::UnoType<bool>::get(), 
PROPERTY_NONE, 0 },
         { u"" UNO_NAME_PLACEHOLDER_DOC_PART, 0, 
cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
         { u"" UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS, 0, 
cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
         { u"" UNO_NAME_DATA_BINDING_XPATH, 0, cppu::UnoType<OUString>::get(), 
PROPERTY_NONE, 0 },
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx 
b/sw/source/uibase/wrtsh/wrtsh1.cxx
index 3ca2d158c021..52558d1963a6 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -1057,6 +1057,11 @@ void 
SwWrtShell::InsertContentControl(SwContentControlType eType)
             aPlaceholder = u"\u2610";
             break;
         }
+        case SwContentControlType::COMBO_BOX:
+        {
+            pContentControl->SetComboBox(true);
+            [[fallthrough]];
+        }
         case SwContentControlType::DROP_DOWN_LIST:
         {
             pContentControl->SetShowingPlaceHolder(true);

Reply via email to