From c2fab664a887b16cb78570851ceffcacd26815f7 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 27 Apr 2022 08:53:13 +0200 Subject: sw content controls, checkbox: add ODT filter Map the 4 new UNO properties to XML attributes: - Checkbox <-> loext:checkbox="..." - Checked <-> loext:checked="..." - CheckedState <-> loext:checked-state="..." - UncheckedState <-> loext:unchecked-state="..." Change-Id: Ia4623004ee39c77f5f242c2d720bc188e4dd9433 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133467 Reviewed-by: Miklos Vajna Tested-by: Jenkins --- include/xmloff/xmltoken.hxx | 2 + .../OpenDocument-v1.3+libreoffice-schema.rng | 22 ++++++ xmloff/qa/unit/data/content-control-checkbox.fodt | 8 +++ xmloff/qa/unit/text.cxx | 84 ++++++++++++++++++++++ xmloff/source/core/xmltoken.cxx | 2 + xmloff/source/text/txtparae.cxx | 32 +++++++++ xmloff/source/text/xmlcontentcontrolcontext.cxx | 43 +++++++++++ xmloff/source/text/xmlcontentcontrolcontext.hxx | 5 ++ xmloff/source/token/tokens.txt | 2 + 9 files changed, 200 insertions(+) create mode 100644 xmloff/qa/unit/data/content-control-checkbox.fodt diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index cdfb259f59ce..4db16a783e2d 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -3489,6 +3489,8 @@ namespace xmloff::token { XML_CONTENT_CONTROL, XML_SHOWING_PLACE_HOLDER, + XML_CHECKED_STATE, + XML_UNCHECKED_STATE, XML_TOKEN_END }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 68ac5c155d6a..511f31629eb4 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2812,6 +2812,28 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. + + + + + + + + + + + + + + + + + + + + + + diff --git a/xmloff/qa/unit/data/content-control-checkbox.fodt b/xmloff/qa/unit/data/content-control-checkbox.fodt new file mode 100644 index 000000000000..59c333ab9d57 --- /dev/null +++ b/xmloff/qa/unit/data/content-control-checkbox.fodt @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx index 5efe9cd91a2d..cd9afdf08654 100644 --- a/xmloff/qa/unit/text.cxx +++ b/xmloff/qa/unit/text.cxx @@ -453,6 +453,90 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testContentControlImport) CPPUNIT_ASSERT_EQUAL(OUString("test"), xContent->getString()); } +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testCheckboxContentControlExport) +{ + // Given a document with a checkbox content control around a text portion: + getComponent() = loadFromDesktop("private:factory/swriter"); + uno::Reference xMSF(getComponent(), uno::UNO_QUERY); + uno::Reference xTextDocument(getComponent(), uno::UNO_QUERY); + uno::Reference xText = xTextDocument->getText(); + uno::Reference xCursor = xText->createTextCursor(); + xText->insertString(xCursor, OUString(u"☐"), /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Checkbox", uno::makeAny(true)); + xContentControlProps->setPropertyValue("Checked", uno::makeAny(true)); + xContentControlProps->setPropertyValue("CheckedState", uno::makeAny(OUString(u"☒"))); + xContentControlProps->setPropertyValue("UncheckedState", uno::makeAny(OUString(u"☐"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When exporting to ODT: + uno::Reference xStorable(getComponent(), uno::UNO_QUERY); + uno::Sequence aStoreProps = comphelper::InitPropertySequence({ + { "FilterName", uno::makeAny(OUString("writer8")) }, + }); + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + xStorable->storeToURL(aTempFile.GetURL(), aStoreProps); + validate(aTempFile.GetFileName(), test::ODF); + + // Then make sure the expected markup is used: + std::unique_ptr pStream = parseExportStream(aTempFile, "content.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + assertXPath(pXmlDoc, "//loext:content-control", "checkbox", "true"); + assertXPath(pXmlDoc, "//loext:content-control", "checked", "true"); + assertXPath(pXmlDoc, "//loext:content-control", "checked-state", u"☒"); + assertXPath(pXmlDoc, "//loext:content-control", "unchecked-state", u"☐"); +} + +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testCheckboxContentControlImport) +{ + // Given an ODF document with a checkbox content control: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "content-control-checkbox.fodt"; + + // When loading that document: + getComponent() = loadFromDesktop(aURL); + + // Then make sure that the content control is not lost on import: + uno::Reference xTextDocument(getComponent(), uno::UNO_QUERY); + uno::Reference xParagraphsAccess(xTextDocument->getText(), + uno::UNO_QUERY); + uno::Reference xParagraphs = xParagraphsAccess->createEnumeration(); + uno::Reference xParagraph(xParagraphs->nextElement(), + uno::UNO_QUERY); + uno::Reference xPortions = xParagraph->createEnumeration(); + uno::Reference xTextPortion(xPortions->nextElement(), uno::UNO_QUERY); + OUString aPortionType; + xTextPortion->getPropertyValue("TextPortionType") >>= aPortionType; + CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aPortionType); + uno::Reference xContentControl; + xTextPortion->getPropertyValue("ContentControl") >>= xContentControl; + uno::Reference xContentControlProps(xContentControl, uno::UNO_QUERY); + bool bCheckbox{}; + xContentControlProps->getPropertyValue("Checkbox") >>= bCheckbox; + // Without the accompanying fix in place, this failed, as the checkbox-related attributes were + // ignored on import. + CPPUNIT_ASSERT(bCheckbox); + bool bChecked{}; + xContentControlProps->getPropertyValue("Checked") >>= bChecked; + CPPUNIT_ASSERT(bChecked); + OUString aCheckedState; + xContentControlProps->getPropertyValue("CheckedState") >>= aCheckedState; + CPPUNIT_ASSERT_EQUAL(OUString(u"☒"), aCheckedState); + OUString aUncheckedState; + xContentControlProps->getPropertyValue("UncheckedState") >>= aUncheckedState; + CPPUNIT_ASSERT_EQUAL(OUString(u"☐"), aUncheckedState); + uno::Reference xContentControlRange(xContentControl, uno::UNO_QUERY); + uno::Reference xText = xContentControlRange->getText(); + uno::Reference xContentEnumAccess(xText, uno::UNO_QUERY); + uno::Reference xContentEnum = xContentEnumAccess->createEnumeration(); + uno::Reference xContent(xContentEnum->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString(u"☒"), xContent->getString()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 2839bd0d1832..b84451538ebd 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3492,6 +3492,8 @@ namespace xmloff::token { TOKEN("content-control", XML_CONTENT_CONTROL ), TOKEN("showing-place-holder", XML_SHOWING_PLACE_HOLDER ), + TOKEN("checked-state", XML_CHECKED_STATE), + TOKEN("unchecked-state", XML_UNCHECKED_STATE), #if OSL_DEBUG_LEVEL > 0 diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index 5b8c07b70919..9b82dad50ee9 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -3900,6 +3900,38 @@ void XMLTextParagraphExport::ExportContentControl( GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_SHOWING_PLACE_HOLDER, aBuffer.makeStringAndClear()); } + + bool bCheckbox = false; + xPropertySet->getPropertyValue("Checkbox") >>= bCheckbox; + if (bCheckbox) + { + OUStringBuffer aBuffer; + sax::Converter::convertBool(aBuffer, bCheckbox); + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CHECKBOX, aBuffer.makeStringAndClear()); + } + + bool bChecked = false; + xPropertySet->getPropertyValue("Checked") >>= bChecked; + if (bChecked) + { + OUStringBuffer aBuffer; + sax::Converter::convertBool(aBuffer, bChecked); + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CHECKED, aBuffer.makeStringAndClear()); + } + + OUString aCheckedState; + xPropertySet->getPropertyValue("CheckedState") >>= aCheckedState; + if (!aCheckedState.isEmpty()) + { + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CHECKED_STATE, aCheckedState); + } + + OUString aUncheckedState; + xPropertySet->getPropertyValue("UncheckedState") >>= aUncheckedState; + if (!aUncheckedState.isEmpty()) + { + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_UNCHECKED_STATE, aUncheckedState); + } } SvXMLElementExport aElem(GetExport(), bExport, XML_NAMESPACE_LO_EXT, XML_CONTENT_CONTROL, false, diff --git a/xmloff/source/text/xmlcontentcontrolcontext.cxx b/xmloff/source/text/xmlcontentcontrolcontext.cxx index fb7869b6e8a8..61fa609e0185 100644 --- a/xmloff/source/text/xmlcontentcontrolcontext.cxx +++ b/xmloff/source/text/xmlcontentcontrolcontext.cxx @@ -58,6 +58,32 @@ void XMLContentControlContext::startFastElement( } break; } + case XML_ELEMENT(LO_EXT, XML_CHECKBOX): + { + if (sax::Converter::convertBool(bTmp, rIter.toView())) + { + m_bCheckbox = bTmp; + } + break; + } + case XML_ELEMENT(LO_EXT, XML_CHECKED): + { + if (sax::Converter::convertBool(bTmp, rIter.toView())) + { + m_bChecked = bTmp; + } + break; + } + case XML_ELEMENT(LO_EXT, XML_CHECKED_STATE): + { + m_aCheckedState = rIter.toString(); + break; + } + case XML_ELEMENT(LO_EXT, XML_UNCHECKED_STATE): + { + m_aUncheckedState = rIter.toString(); + break; + } default: XMLOFF_WARN_UNKNOWN("xmloff", rIter); } @@ -99,6 +125,23 @@ void XMLContentControlContext::endFastElement(sal_Int32) { xPropertySet->setPropertyValue("ShowingPlaceHolder", uno::makeAny(m_bShowingPlaceHolder)); } + + if (m_bCheckbox) + { + xPropertySet->setPropertyValue("Checkbox", uno::makeAny(m_bCheckbox)); + } + if (m_bChecked) + { + xPropertySet->setPropertyValue("Checked", uno::makeAny(m_bChecked)); + } + if (!m_aCheckedState.isEmpty()) + { + xPropertySet->setPropertyValue("CheckedState", uno::makeAny(m_aCheckedState)); + } + if (!m_aUncheckedState.isEmpty()) + { + xPropertySet->setPropertyValue("UncheckedState", uno::makeAny(m_aUncheckedState)); + } } css::uno::Reference diff --git a/xmloff/source/text/xmlcontentcontrolcontext.hxx b/xmloff/source/text/xmlcontentcontrolcontext.hxx index 2658fa76972b..3d3e44d76445 100644 --- a/xmloff/source/text/xmlcontentcontrolcontext.hxx +++ b/xmloff/source/text/xmlcontentcontrolcontext.hxx @@ -35,6 +35,11 @@ class XMLContentControlContext : public SvXMLImportContext bool m_bShowingPlaceHolder = false; + bool m_bCheckbox = false; + bool m_bChecked = false; + OUString m_aCheckedState; + OUString m_aUncheckedState; + public: XMLContentControlContext(SvXMLImport& rImport, sal_Int32 nElement, XMLHints_Impl& rHints, bool& rIgnoreLeadingSpace); diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 1a8d60b4ec1b..accdd853e9e1 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3235,4 +3235,6 @@ color-lum-mod color-lum-off content-control showing-place-holder +checked-state +unchecked-state TOKEN_END_DUMMY -- cgit v1.2.3