sw/qa/filter/ww8/ww8.cxx | 27 +++++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 18 +++++- writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx | 30 ++++++++++ writerfilter/qa/cppunittests/dmapper/data/sdt-run-combobox.docx |binary writerfilter/source/dmapper/DomainMapper.cxx | 23 ++++++- writerfilter/source/dmapper/DomainMapper_Impl.cxx | 7 ++ writerfilter/source/dmapper/SdtHelper.cxx | 3 - writerfilter/source/dmapper/SdtHelper.hxx | 1 8 files changed, 101 insertions(+), 8 deletions(-)
New commits: commit 321aa90f483d4c458e20c96da4f8d70943184283 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Thu Sep 22 10:01:26 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Sep 23 09:35:25 2022 +0200 sw content controls, combo box: add DOCX filter Map the ComboBox UNO property to: <w:sdtPr> <w:comboBox> ... </w:comboBox> </w:sdtPr> and the opposite on import. (cherry picked from commit 01b1f57a90172a76faa1489b3b72250ee76169a6) Change-Id: I50e0b961bca99f4ecca86d6784d2e6a13f469314 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140432 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/filter/ww8/ww8.cxx b/sw/qa/filter/ww8/ww8.cxx index d98ff7dae151..d537cee04188 100644 --- a/sw/qa/filter/ww8/ww8.cxx +++ b/sw/qa/filter/ww8/ww8.cxx @@ -11,6 +11,11 @@ #include <com/sun/star/text/XTextDocument.hpp> +#include <docsh.hxx> +#include <formatcontentcontrol.hxx> +#include <wrtsh.hxx> +#include <unotxdoc.hxx> + namespace { /** @@ -56,6 +61,28 @@ CPPUNIT_TEST_FIXTURE(Test, testPlainTextContentControlExport) // i.e. the plain text content control was turned into a rich text one on export. assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:text", 1); } + +CPPUNIT_TEST_FIXTURE(Test, testDocxComboBoxContentControlExport) +{ + // Given a document with a combo box content control around a text portion: + mxComponent = loadFromDesktop("private:factory/swriter"); + SwDocShell* pDocShell = dynamic_cast<SwXTextDocument*>(mxComponent.get())->GetDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->InsertContentControl(SwContentControlType::COMBO_BOX); + + // When exporting to DOCX: + save("Office Open XML Text", maTempFile); + mbExported = true; + + // Then make sure the expected markup is used: + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '//w:sdt/w:sdtPr/w:comboBox' number of nodes is incorrect + // i.e. the combo box content control was turned into a drop-down one on export. + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:comboBox", 1); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 67e87965e5f0..42c5c0cdef15 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -2395,14 +2395,28 @@ void DocxAttributeOutput::WriteContentControlStart() if (m_pContentControl->HasListItems()) { - m_pSerializer->startElementNS(XML_w, XML_dropDownList); + if (m_pContentControl->GetComboBox()) + { + m_pSerializer->startElementNS(XML_w, XML_comboBox); + } + else + { + m_pSerializer->startElementNS(XML_w, XML_dropDownList); + } for (const auto& rItem : m_pContentControl->GetListItems()) { m_pSerializer->singleElementNS(XML_w, XML_listItem, FSNS(XML_w, XML_displayText), rItem.m_aDisplayText, FSNS(XML_w, XML_value), rItem.m_aValue); } - m_pSerializer->endElementNS(XML_w, XML_dropDownList); + if (m_pContentControl->GetComboBox()) + { + m_pSerializer->endElementNS(XML_w, XML_comboBox); + } + else + { + m_pSerializer->endElementNS(XML_w, XML_dropDownList); + } } if (m_pContentControl->GetDate()) diff --git a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx index 6b568619785e..7b842e667104 100644 --- a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx +++ b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx @@ -212,6 +212,36 @@ CPPUNIT_TEST_FIXTURE(Test, testSdtRunDropdown) CPPUNIT_ASSERT_EQUAL(OUString("choose a color"), xContent->getString()); } +CPPUNIT_TEST_FIXTURE(Test, testSdtRunComboBox) +{ + // Given a document with a combo box inline/run SDT: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "sdt-run-combobox.docx"; + + // When loading the document: + getComponent() = loadFromDesktop(aURL); + + // Then make sure that the doc model has a clickable combo box content control: + uno::Reference<text::XTextDocument> xTextDocument(getComponent(), uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xParagraphsAccess(xTextDocument->getText(), + uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xParagraphs = xParagraphsAccess->createEnumeration(); + uno::Reference<container::XEnumerationAccess> xParagraph(xParagraphs->nextElement(), + uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xPortions = xParagraph->createEnumeration(); + uno::Reference<beans::XPropertySet> xTextPortion(xPortions->nextElement(), uno::UNO_QUERY); + OUString aPortionType; + xTextPortion->getPropertyValue("TextPortionType") >>= aPortionType; + CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType); + uno::Reference<text::XTextContent> xContentControl; + xTextPortion->getPropertyValue("ContentControl") >>= xContentControl; + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + bool bComboBox{}; + xContentControlProps->getPropertyValue("ComboBox") >>= bComboBox; + // Without the accompanying fix in place, this failed as the content control was a drop-down, + // not a combo box. + CPPUNIT_ASSERT(bComboBox); +} + CPPUNIT_TEST_FIXTURE(Test, testSdtRunPicture) { // Given a document with a dropdown inline/run SDT: diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-combobox.docx b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-combobox.docx new file mode 100644 index 000000000000..2ae80047cc1b Binary files /dev/null and b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-combobox.docx differ diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index e79a46784dab..20165d7a7bcf 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -1104,6 +1104,7 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) case SdtControlType::richText: case SdtControlType::checkBox: case SdtControlType::dropDown: + case SdtControlType::comboBox: case SdtControlType::picture: case SdtControlType::datePicker: m_pImpl->PopSdt(); @@ -1130,6 +1131,7 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) switch (m_pImpl->m_pSdtHelper->getControlType()) { case SdtControlType::dropDown: + case SdtControlType::comboBox: m_pImpl->m_pSdtHelper->createDropDownControl(); break; case SdtControlType::plainText: @@ -2705,8 +2707,15 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } } break; - case NS_ooxml::LN_CT_SdtPr_dropDownList: case NS_ooxml::LN_CT_SdtPr_comboBox: + { + m_pImpl->m_pSdtHelper->setControlType(SdtControlType::comboBox); + writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); + if (pProperties) + pProperties->resolve(*this); + } + break; + case NS_ooxml::LN_CT_SdtPr_dropDownList: { m_pImpl->m_pSdtHelper->setControlType(SdtControlType::dropDown); writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); @@ -3729,7 +3738,9 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) } bool bNewLine = len == 1 && (sText[0] == 0x0d || sText[0] == 0x07); - if (m_pImpl->GetSdtStarts().empty() && m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::dropDown) + if (m_pImpl->GetSdtStarts().empty() + && (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::dropDown + || m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::comboBox)) { // Block, cell or row SDT. if (bNewLine) @@ -3784,7 +3795,9 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) pContext = m_pImpl->GetTopFieldContext()->getProperties(); uno::Sequence<beans::PropertyValue> aGrabBag = m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear(); - if (m_pImpl->GetSdtStarts().empty() || m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::dropDown) + if (m_pImpl->GetSdtStarts().empty() + || (m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::dropDown + && m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::comboBox)) { pContext->Insert(PROP_SDTPR, uno::makeAny(aGrabBag), true, CHAR_GRAB_BAG); } @@ -3793,7 +3806,9 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) { uno::Sequence<beans::PropertyValue> aGrabBag = m_pImpl->m_pSdtHelper->getInteropGrabBagAndClear(); if (m_pImpl->GetSdtStarts().empty() - || (m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::dropDown && m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::richText)) + || (m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::dropDown + && m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::comboBox + && m_pImpl->m_pSdtHelper->getControlType() != SdtControlType::richText)) { m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH)->Insert(PROP_SDTPR, uno::makeAny(aGrabBag), true, PARA_GRAB_BAG); diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 25498306b737..5ddb03d7c405 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -944,7 +944,8 @@ void DomainMapper_Impl::PopSdt() uno::makeAny(m_pSdtHelper->GetUncheckedState())); } - if (m_pSdtHelper->getControlType() == SdtControlType::dropDown) + if (m_pSdtHelper->getControlType() == SdtControlType::dropDown + || m_pSdtHelper->getControlType() == SdtControlType::comboBox) { std::vector<OUString>& rDisplayTexts = m_pSdtHelper->getDropDownDisplayTexts(); std::vector<OUString>& rValues = m_pSdtHelper->getDropDownItems(); @@ -961,6 +962,10 @@ void DomainMapper_Impl::PopSdt() pItems[i] = aItem; } xContentControlProps->setPropertyValue("ListItems", uno::Any(aItems)); + if (m_pSdtHelper->getControlType() == SdtControlType::comboBox) + { + xContentControlProps->setPropertyValue("ComboBox", uno::Any(true)); + } } } diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx index 013581a038c8..53359a4e69e2 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -210,7 +210,8 @@ std::optional<OUString> SdtHelper::getValueFromDataBinding() void SdtHelper::createDropDownControl() { - assert(getControlType() == SdtControlType::dropDown); + assert(getControlType() == SdtControlType::dropDown + || getControlType() == SdtControlType::comboBox); const bool bDropDown = officecfg::Office::Writer::Filter::Import::DOCX::ImportComboBoxAsDropDown::get(); diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx index e8b8ab34ed38..b1ec0dd6c77f 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -45,6 +45,7 @@ enum class SdtControlType richText, checkBox, picture, + comboBox, unsupported, // Sdt block is defined, but we still do not support such type of field unknown };