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