schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng | 5 sw/inc/formatcontentcontrol.hxx | 60 ++++ sw/inc/unoprnms.hxx | 6 sw/qa/core/unocore/unocore.cxx | 23 + sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 12 sw/qa/uibase/wrtsh/wrtsh.cxx | 2 sw/source/core/crsr/datecontentcontrolbutton.cxx | 12 sw/source/core/txtnode/attrcontentcontrol.cxx | 73 +++++ sw/source/core/unocore/unocontentcontrol.cxx | 168 ++++++++++++ sw/source/core/unocore/unomap1.cxx | 6 sw/source/filter/ww8/docxattributeoutput.cxx | 32 ++ sw/source/uibase/wrtsh/wrtsh3.cxx | 5 xmloff/qa/unit/data/content-control-date.fodt | 2 xmloff/qa/unit/text.cxx | 6 xmloff/source/text/txtparae.cxx | 6 xmloff/source/text/xmlcontentcontrolcontext.cxx | 9 xmloff/source/text/xmlcontentcontrolcontext.hxx | 1 17 files changed, 424 insertions(+), 4 deletions(-)
New commits: commit 6b33197361d484dea91646949945e258315fa43b Author: Miklos Vajna <[email protected]> AuthorDate: Fri May 27 12:35:20 2022 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Tue May 31 14:40:58 2022 +0200 sw content controls, date: preserve more properties While working on the DOCX import for dates, it turns out there is a need to store more properties for DOCX export purposes. Given that these are potentially useful for full support, add dedicated UNO API and DOCX export for these (i.e. not just grab-bag), but omit UI or ODT filter for now. This includes: - <w:docPart w:val="..."> - <w:dataBinding w:prefixMappings="..." w:xpath="..." w:storeItemID="..."> - <w15:color w:val="..."> And tests for all these. (cherry picked from commit 0f70f4d76b5f68e5b1d81f0e300435ccef893c9a) Change-Id: I18ddec50d40c1c4abd87f7ea947a24dd8a92a755 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135151 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx index 90452dfaeb2b..16335c2e0b63 100644 --- a/sw/inc/formatcontentcontrol.hxx +++ b/sw/inc/formatcontentcontrol.hxx @@ -138,6 +138,21 @@ class SW_DLLPUBLIC SwContentControl : public sw::BroadcastingModify /// Date in YYYY-MM-DDT00:00:00Z format. OUString m_aCurrentDate; + /// The placeholder's doc part: just remembered. + OUString m_aPlaceholderDocPart; + + /// The data bindings's prefix mappings: just remembered. + OUString m_aDataBindingPrefixMappings; + + /// The data bindings's XPath: just remembered. + OUString m_aDataBindingXpath; + + /// The data bindings's store item ID: just remembered. + OUString m_aDataBindingStoreItemID; + + /// The color: just remembered. + OUString m_aColor; + /// Stores a list item index, in case the doc model is not yet updated. std::optional<size_t> m_oSelectedListItem; @@ -232,6 +247,13 @@ public: /// Formats m_oSelectedDate, taking m_aDateFormat and m_aDateLanguage into account. OUString GetDateString() const; + void SetPlaceholderDocPart(const OUString& rPlaceholderDocPart) + { + m_aPlaceholderDocPart = rPlaceholderDocPart; + } + + OUString GetPlaceholderDocPart() const { return m_aPlaceholderDocPart; } + void SetSelectedListItem(std::optional<size_t> oSelectedListItem) { m_oSelectedListItem = oSelectedListItem; @@ -244,6 +266,31 @@ public: std::optional<double> GetSelectedDate() const { return m_oSelectedDate; } virtual void dumpAsXml(xmlTextWriterPtr pWriter) const; + + void SetDataBindingPrefixMappings(const OUString& rDataBindingPrefixMappings) + { + m_aDataBindingPrefixMappings = rDataBindingPrefixMappings; + } + + OUString GetDataBindingPrefixMappings() const { return m_aDataBindingPrefixMappings; } + + void SetDataBindingXpath(const OUString& rDataBindingXpath) + { + m_aDataBindingXpath = rDataBindingXpath; + } + + OUString GetDataBindingXpath() const { return m_aDataBindingXpath; } + + void SetDataBindingStoreItemID(const OUString& rDataBindingStoreItemID) + { + m_aDataBindingStoreItemID = rDataBindingStoreItemID; + } + + OUString GetDataBindingStoreItemID() const { return m_aDataBindingStoreItemID; } + + void SetColor(const OUString& rColor) { m_aColor = rColor; } + + OUString GetColor() const { return m_aColor; } }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 72aaf746cb1e..1ac7b671c695 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -880,6 +880,11 @@ #define UNO_NAME_DATE_FORMAT "DateFormat" #define UNO_NAME_DATE_LANGUAGE "DateLanguage" #define UNO_NAME_CURRENT_DATE "CurrentDate" +#define UNO_NAME_PLACEHOLDER_DOC_PART "PlaceholderDocPart" +#define UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS "DataBindingPrefixMappings" +#define UNO_NAME_DATA_BINDING_XPATH "DataBindingXpath" +#define UNO_NAME_DATA_BINDING_STORE_ITEM_ID "DataBindingStoreItemID" +#define UNO_NAME_COLOR "Color" #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index b5ad497e5dac..e7fd560b4370 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -551,6 +551,17 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); xContentControlProps->setPropertyValue("CurrentDate", uno::Any(OUString("2022-05-25T00:00:00Z"))); + xContentControlProps->setPropertyValue("PlaceholderDocPart", + uno::Any(OUString("DefaultPlaceholder_-1854013437"))); + xContentControlProps->setPropertyValue( + "DataBindingPrefixMappings", + uno::Any(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "))); + xContentControlProps->setPropertyValue( + "DataBindingXpath", + uno::Any(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"))); + xContentControlProps->setPropertyValue( + "DataBindingStoreItemID", uno::Any(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"))); + xContentControlProps->setPropertyValue("Color", uno::Any(OUString("008000"))); xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // Then make sure that the specified properties are set: @@ -565,6 +576,15 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) CPPUNIT_ASSERT_EQUAL(OUString("M/d/yyyy"), pContentControl->GetDateFormat()); CPPUNIT_ASSERT_EQUAL(OUString("en-US"), pContentControl->GetDateLanguage()); CPPUNIT_ASSERT_EQUAL(OUString("2022-05-25T00:00:00Z"), pContentControl->GetCurrentDate()); + CPPUNIT_ASSERT_EQUAL(OUString("DefaultPlaceholder_-1854013437"), + pContentControl->GetPlaceholderDocPart()); + CPPUNIT_ASSERT_EQUAL(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "), + pContentControl->GetDataBindingPrefixMappings()); + CPPUNIT_ASSERT_EQUAL(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"), + pContentControl->GetDataBindingXpath()); + CPPUNIT_ASSERT_EQUAL(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"), + pContentControl->GetDataBindingStoreItemID()); + CPPUNIT_ASSERT_EQUAL(OUString("008000"), pContentControl->GetColor()); } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index c0a0d401da35..2432f6112511 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -291,6 +291,12 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport) xContentControlProps->setPropertyValue("Date", uno::Any(true)); xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("M/d/yyyy"))); xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); + xContentControlProps->setPropertyValue("CurrentDate", uno::Any(OUString("2022-05-26T00:00:00Z"))); + xContentControlProps->setPropertyValue("PlaceholderDocPart", uno::Any(OUString("DefaultPlaceholder_-1854013437"))); + xContentControlProps->setPropertyValue("DataBindingPrefixMappings", uno::Any(OUString("xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "))); + xContentControlProps->setPropertyValue("DataBindingXpath", uno::Any(OUString("/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"))); + xContentControlProps->setPropertyValue("DataBindingStoreItemID", uno::Any(OUString("{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"))); + xContentControlProps->setPropertyValue("Color", uno::Any(OUString("008000"))); xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // When exporting to DOCX: @@ -306,6 +312,12 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport) // i.e. the <w:date> was lost on export. assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date/w:dateFormat", "val", "M/d/yyyy"); assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date/w:lid", "val", "en-US"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:date", "fullDate", "2022-05-26T00:00:00Z"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:placeholder/w:docPart", "val", "DefaultPlaceholder_-1854013437"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dataBinding", "prefixMappings", "xmlns:ns0='http://schemas.microsoft.com/vsto/samples' "); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dataBinding", "xpath", "/ns0:employees[1]/ns0:employee[1]/ns0:hireDate[1]"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:dataBinding", "storeItemID", "{241A8A02-7FFD-488D-8827-63FBE74E8BC9}"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w15:color", "val", "008000"); } DECLARE_OOXMLEXPORT_TEST(testTdf137466, "tdf137466.docx") diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 529a3ea9e331..f70a36b49de2 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -326,6 +326,16 @@ void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const BAD_CAST(m_aDateLanguage.toUtf8().getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("current-date"), BAD_CAST(m_aCurrentDate.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("placeholder-doc-part"), + BAD_CAST(m_aPlaceholderDocPart.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-prefix-mappings"), + BAD_CAST(m_aDataBindingPrefixMappings.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-xpath"), + BAD_CAST(m_aDataBindingXpath.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-store-item-id"), + BAD_CAST(m_aDataBindingStoreItemID.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("color"), + BAD_CAST(m_aColor.toUtf8().getStr())); if (!m_aListItems.empty()) { diff --git a/sw/source/core/unocore/unocontentcontrol.cxx b/sw/source/core/unocore/unocontentcontrol.cxx index 393ab8d7dfb9..c84cff5837d0 100644 --- a/sw/source/core/unocore/unocontentcontrol.cxx +++ b/sw/source/core/unocore/unocontentcontrol.cxx @@ -166,6 +166,11 @@ public: OUString m_aDateFormat; OUString m_aDateLanguage; OUString m_aCurrentDate; + OUString m_aPlaceholderDocPart; + OUString m_aDataBindingPrefixMappings; + OUString m_aDataBindingXpath; + OUString m_aDataBindingStoreItemID; + OUString m_aColor; Impl(SwXContentControl& rThis, SwDoc& rDoc, SwContentControl* pContentControl, const uno::Reference<text::XText>& xParentText, @@ -529,6 +534,11 @@ void SwXContentControl::AttachImpl(const uno::Reference<text::XTextRange>& xText pContentControl->SetDateFormat(m_pImpl->m_aDateFormat); pContentControl->SetDateLanguage(m_pImpl->m_aDateLanguage); pContentControl->SetCurrentDate(m_pImpl->m_aCurrentDate); + pContentControl->SetPlaceholderDocPart(m_pImpl->m_aPlaceholderDocPart); + pContentControl->SetDataBindingPrefixMappings(m_pImpl->m_aDataBindingPrefixMappings); + pContentControl->SetDataBindingXpath(m_pImpl->m_aDataBindingXpath); + pContentControl->SetDataBindingStoreItemID(m_pImpl->m_aDataBindingStoreItemID); + pContentControl->SetColor(m_pImpl->m_aColor); SwFormatContentControl aContentControl(pContentControl, nWhich); bool bSuccess @@ -843,6 +853,81 @@ void SAL_CALL SwXContentControl::setPropertyValue(const OUString& rPropertyName, } } } + else if (rPropertyName == UNO_NAME_PLACEHOLDER_DOC_PART) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aPlaceholderDocPart = aValue; + } + else + { + m_pImpl->m_pContentControl->SetPlaceholderDocPart(aValue); + } + } + } + else if (rPropertyName == UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aDataBindingPrefixMappings = aValue; + } + else + { + m_pImpl->m_pContentControl->SetDataBindingPrefixMappings(aValue); + } + } + } + else if (rPropertyName == UNO_NAME_DATA_BINDING_XPATH) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aDataBindingXpath = aValue; + } + else + { + m_pImpl->m_pContentControl->SetDataBindingXpath(aValue); + } + } + } + else if (rPropertyName == UNO_NAME_DATA_BINDING_STORE_ITEM_ID) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aDataBindingStoreItemID = aValue; + } + else + { + m_pImpl->m_pContentControl->SetDataBindingStoreItemID(aValue); + } + } + } + else if (rPropertyName == UNO_NAME_COLOR) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aColor = aValue; + } + else + { + m_pImpl->m_pContentControl->SetColor(aValue); + } + } + } else { throw beans::UnknownPropertyException(); @@ -977,6 +1062,61 @@ uno::Any SAL_CALL SwXContentControl::getPropertyValue(const OUString& rPropertyN aRet <<= m_pImpl->m_pContentControl->GetCurrentDate(); } } + else if (rPropertyName == UNO_NAME_PLACEHOLDER_DOC_PART) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aPlaceholderDocPart; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetCurrentDate(); + } + } + else if (rPropertyName == UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aDataBindingPrefixMappings; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetDataBindingPrefixMappings(); + } + } + else if (rPropertyName == UNO_NAME_DATA_BINDING_XPATH) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aDataBindingXpath; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetDataBindingXpath(); + } + } + else if (rPropertyName == UNO_NAME_DATA_BINDING_STORE_ITEM_ID) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aDataBindingStoreItemID; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetDataBindingStoreItemID(); + } + } + else if (rPropertyName == UNO_NAME_COLOR) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aColor; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetColor(); + } + } else { throw beans::UnknownPropertyException(); diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx index 3eccea466868..fd5cc2e42fbb 100644 --- a/sw/source/core/unocore/unomap1.cxx +++ b/sw/source/core/unocore/unomap1.cxx @@ -1035,7 +1035,13 @@ const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetContentControlProper { u"" UNO_NAME_PICTURE, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, { u"" UNO_NAME_DATE, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, { u"" UNO_NAME_DATE_FORMAT, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { 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_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 }, + { u"" UNO_NAME_DATA_BINDING_STORE_ITEM_ID, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, + { u"" UNO_NAME_COLOR, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, { u"", 0, css::uno::Type(), 0, 0 } }; diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 95dff36042e2..2787d21ad30a 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -2338,6 +2338,28 @@ void DocxAttributeOutput::WriteContentControlStart() m_pSerializer->startElementNS(XML_w, XML_sdt); m_pSerializer->startElementNS(XML_w, XML_sdtPr); + if (!m_pContentControl->GetPlaceholderDocPart().isEmpty()) + { + m_pSerializer->startElementNS(XML_w, XML_placeholder); + m_pSerializer->singleElementNS(XML_w, XML_docPart, FSNS(XML_w, XML_val), + m_pContentControl->GetPlaceholderDocPart()); + m_pSerializer->endElementNS(XML_w, XML_placeholder); + } + + if (!m_pContentControl->GetDataBindingPrefixMappings().isEmpty() || !m_pContentControl->GetDataBindingXpath().isEmpty() || !m_pContentControl->GetDataBindingStoreItemID().isEmpty()) + { + m_pSerializer->singleElementNS( XML_w, XML_dataBinding, + FSNS(XML_w, XML_prefixMappings), m_pContentControl->GetDataBindingPrefixMappings(), + FSNS(XML_w, XML_xpath), m_pContentControl->GetDataBindingXpath(), + FSNS(XML_w, XML_storeItemID), m_pContentControl->GetDataBindingStoreItemID()); + } + + if (!m_pContentControl->GetColor().isEmpty()) + { + m_pSerializer->singleElementNS(XML_w15, XML_color, FSNS(XML_w, XML_val), + m_pContentControl->GetColor()); + } + if (m_pContentControl->GetShowingPlaceHolder()) { m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); commit d0ebc93087975ce361b618512548bc05b5b95c8b Author: Miklos Vajna <[email protected]> AuthorDate: Fri May 27 11:38:42 2022 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Tue May 31 14:40:45 2022 +0200 sw content controls, date: add current date handling While working on the DOCX import for dates, it turns out there is a need to store the selected date in machine-readable format as well. This is useful, because once the timestamp is formatted, the user is allowed to hand-edit the result, so otherwise the selected date would be lost. This commit adds: - doc model & UNO API - click handler (store the selected date, default to the current date in the date picker if possible) - ODT filter - DOCX export And tests for all these. (cherry picked from commit 79baafccf3d390810f516b2cf9cb3ad2b4e9e63b) Change-Id: I00f4e87ebfe0e8a19486367c32d472ccd2ff16a8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135150 Tested-by: Miklos Vajna <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 514a30c30307..55bedf37b1bb 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2816,6 +2816,11 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. <rng:ref name="language"/> </rng:attribute> </rng:optional> + <rng:optional> + <rng:attribute name="loext:current-date"> + <rng:ref name="string"/> + </rng:attribute> + </rng:optional> <rng:zeroOrMore> <rng:element name="loext:list-item"> <rng:attribute name="loext:display-text"> diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx index 1dba4bce03a6..90452dfaeb2b 100644 --- a/sw/inc/formatcontentcontrol.hxx +++ b/sw/inc/formatcontentcontrol.hxx @@ -135,6 +135,9 @@ class SW_DLLPUBLIC SwContentControl : public sw::BroadcastingModify /// If m_bDate is true, the date's BCP 47 language tag. OUString m_aDateLanguage; + /// Date in YYYY-MM-DDT00:00:00Z format. + OUString m_aCurrentDate; + /// Stores a list item index, in case the doc model is not yet updated. std::optional<size_t> m_oSelectedListItem; @@ -216,6 +219,16 @@ public: OUString GetDateLanguage() const { return m_aDateLanguage; } + void SetCurrentDate(const OUString& rCurrentDate) { m_aCurrentDate = rCurrentDate; } + + OUString GetCurrentDate() const { return m_aCurrentDate; } + + /// Formats fCurrentDate and sets it. + void SetCurrentDateValue(double fCurrentDate); + + /// Parses m_aCurrentDate and returns it. + double GetCurrentDateValue() const; + /// Formats m_oSelectedDate, taking m_aDateFormat and m_aDateLanguage into account. OUString GetDateString() const; diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 4ac4aa7d0dca..72aaf746cb1e 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -879,6 +879,7 @@ #define UNO_NAME_PICTURE "Picture" #define UNO_NAME_DATE_FORMAT "DateFormat" #define UNO_NAME_DATE_LANGUAGE "DateLanguage" +#define UNO_NAME_CURRENT_DATE "CurrentDate" #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index edc11b03b9fb..b5ad497e5dac 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -549,6 +549,8 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) xContentControlProps->setPropertyValue("Date", uno::Any(true)); xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("M/d/yyyy"))); xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); + xContentControlProps->setPropertyValue("CurrentDate", + uno::Any(OUString("2022-05-25T00:00:00Z"))); xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // Then make sure that the specified properties are set: @@ -562,6 +564,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) CPPUNIT_ASSERT(pContentControl->GetDate()); CPPUNIT_ASSERT_EQUAL(OUString("M/d/yyyy"), pContentControl->GetDateFormat()); CPPUNIT_ASSERT_EQUAL(OUString("en-US"), pContentControl->GetDateLanguage()); + CPPUNIT_ASSERT_EQUAL(OUString("2022-05-25T00:00:00Z"), pContentControl->GetCurrentDate()); } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx index 6eb9e51a3f60..6f3478e7f99b 100644 --- a/sw/qa/uibase/wrtsh/wrtsh.cxx +++ b/sw/qa/uibase/wrtsh/wrtsh.cxx @@ -343,6 +343,8 @@ CPPUNIT_TEST_FIXTURE(Test, testSelectDateContentControl) // - Actual : test // i.e. the content control was not updated. CPPUNIT_ASSERT_EQUAL(OUString("2022-05-24"), pTextNode->GetExpandText(pWrtShell->GetLayout())); + CPPUNIT_ASSERT_EQUAL(OUString("2022-05-24T00:00:00Z"), + rFormatContentControl.GetContentControl()->GetCurrentDate()); } } diff --git a/sw/source/core/crsr/datecontentcontrolbutton.cxx b/sw/source/core/crsr/datecontentcontrolbutton.cxx index c52f546e9c6f..9de971b6f9f6 100644 --- a/sw/source/core/crsr/datecontentcontrolbutton.cxx +++ b/sw/source/core/crsr/datecontentcontrolbutton.cxx @@ -45,6 +45,18 @@ void SwDateContentControlButton::LaunchPopup() "modules/swriter/ui/contentcontrolcalendar.ui"); m_xPopup = m_xPopupBuilder->weld_popover("Calendar"); m_xCalendar = m_xPopupBuilder->weld_calendar("date"); + + // Read the doc model. + if (m_pContentControl) + { + const Date& rNullDate = m_pNumberFormatter->GetNullDate(); + double fCurrentDate = m_pContentControl->GetCurrentDateValue(); + if (fCurrentDate != 0) + { + m_xCalendar->set_date(rNullDate + sal_Int32(fCurrentDate)); + } + } + m_xCalendar->connect_activated(LINK(this, SwDateContentControlButton, SelectHandler)); SwContentControlButton::LaunchPopup(); m_xCalendar->grab_focus(); diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 06dc388ee1ef..529a3ea9e331 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -32,6 +32,11 @@ using namespace com::sun::star; +namespace +{ +inline constexpr OUStringLiteral CURRENT_DATE_FORMAT = u"YYYY-MM-DD"; +} + SwFormatContentControl* SwFormatContentControl::CreatePoolDefault(sal_uInt16 nWhich) { return new SwFormatContentControl(nWhich); @@ -218,7 +223,7 @@ OUString SwContentControl::GetDateString() const if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) { - // Try to find a format based on just the language. + // If not found, then create it. sal_Int32 nCheckPos = 0; SvNumFormatType nType; OUString aFormat = m_aDateFormat; @@ -242,6 +247,60 @@ OUString SwContentControl::GetDateString() const return aFormatted; } +void SwContentControl::SetCurrentDateValue(double fCurrentDate) +{ + SwDoc& rDoc = m_pTextNode->GetDoc(); + SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter(); + OUString aFormatted; + sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US); + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + // If not found, then create it. + sal_Int32 nCheckPos = 0; + SvNumFormatType nType; + OUString sFormat = CURRENT_DATE_FORMAT; + pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US); + } + + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + return; + } + + const Color* pColor = nullptr; + pNumberFormatter->GetOutputString(fCurrentDate, nFormat, aFormatted, &pColor, false); + m_aCurrentDate = aFormatted + "T00:00:00Z"; +} + +double SwContentControl::GetCurrentDateValue() const +{ + if (m_aCurrentDate.isEmpty()) + { + return 0; + } + + SwDoc& rDoc = m_pTextNode->GetDoc(); + SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter(); + sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US); + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + sal_Int32 nCheckPos = 0; + SvNumFormatType nType; + OUString sFormat = CURRENT_DATE_FORMAT; + pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US); + } + + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + return 0; + } + + double dCurrentDate = 0; + OUString aCurrentDate = m_aCurrentDate.replaceAll("T00:00:00Z", ""); + pNumberFormatter->IsNumberFormat(aCurrentDate, nFormat, dCurrentDate); + return dCurrentDate; +} + void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl")); @@ -265,6 +324,8 @@ void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const BAD_CAST(m_aDateFormat.toUtf8().getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-language"), BAD_CAST(m_aDateLanguage.toUtf8().getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("current-date"), + BAD_CAST(m_aCurrentDate.toUtf8().getStr())); if (!m_aListItems.empty()) { diff --git a/sw/source/core/unocore/unocontentcontrol.cxx b/sw/source/core/unocore/unocontentcontrol.cxx index ef6959866b4c..393ab8d7dfb9 100644 --- a/sw/source/core/unocore/unocontentcontrol.cxx +++ b/sw/source/core/unocore/unocontentcontrol.cxx @@ -165,6 +165,7 @@ public: bool m_bDate; OUString m_aDateFormat; OUString m_aDateLanguage; + OUString m_aCurrentDate; Impl(SwXContentControl& rThis, SwDoc& rDoc, SwContentControl* pContentControl, const uno::Reference<text::XText>& xParentText, @@ -527,6 +528,7 @@ void SwXContentControl::AttachImpl(const uno::Reference<text::XTextRange>& xText pContentControl->SetDate(m_pImpl->m_bDate); pContentControl->SetDateFormat(m_pImpl->m_aDateFormat); pContentControl->SetDateLanguage(m_pImpl->m_aDateLanguage); + pContentControl->SetCurrentDate(m_pImpl->m_aCurrentDate); SwFormatContentControl aContentControl(pContentControl, nWhich); bool bSuccess @@ -826,6 +828,21 @@ void SAL_CALL SwXContentControl::setPropertyValue(const OUString& rPropertyName, } } } + else if (rPropertyName == UNO_NAME_CURRENT_DATE) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aCurrentDate = aValue; + } + else + { + m_pImpl->m_pContentControl->SetCurrentDate(aValue); + } + } + } else { throw beans::UnknownPropertyException(); @@ -949,6 +966,17 @@ uno::Any SAL_CALL SwXContentControl::getPropertyValue(const OUString& rPropertyN aRet <<= m_pImpl->m_pContentControl->GetDateLanguage(); } } + else if (rPropertyName == UNO_NAME_CURRENT_DATE) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aCurrentDate; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetCurrentDate(); + } + } else { throw beans::UnknownPropertyException(); diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx index 70e6fc0e5fc7..3eccea466868 100644 --- a/sw/source/core/unocore/unomap1.cxx +++ b/sw/source/core/unocore/unomap1.cxx @@ -1035,7 +1035,7 @@ const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetContentControlProper { u"" UNO_NAME_PICTURE, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, { u"" UNO_NAME_DATE, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, { u"" UNO_NAME_DATE_FORMAT, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, - { 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"", 0, css::uno::Type(), 0, 0 } }; diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index f253e77658d7..95dff36042e2 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -2382,7 +2382,15 @@ void DocxAttributeOutput::WriteContentControlStart() if (m_pContentControl->GetDate()) { - m_pSerializer->startElementNS(XML_w, XML_date); + OUString aCurrentDate = m_pContentControl->GetCurrentDate(); + if (aCurrentDate.isEmpty()) + { + m_pSerializer->startElementNS(XML_w, XML_date); + } + else + { + m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), aCurrentDate); + } OUString aDateFormat = m_pContentControl->GetDateFormat(); if (!aDateFormat.isEmpty()) { diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx b/sw/source/uibase/wrtsh/wrtsh3.cxx index f01946e45d17..e4b594770282 100644 --- a/sw/source/uibase/wrtsh/wrtsh3.cxx +++ b/sw/source/uibase/wrtsh/wrtsh3.cxx @@ -190,9 +190,12 @@ bool SwWrtShell::GotoContentControl(const SwFormatContentControl& rContentContro aRewriter.AddRule(UndoArg3, SwResId(STR_START_QUOTE) + aNewState + SwResId(STR_END_QUOTE)); GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter); + // Write the doc model. + pContentControl->SetCurrentDateValue(*pContentControl->GetSelectedDate()); + pContentControl->SetSelectedDate(std::nullopt); + // Update the content. DelLeft(); - pContentControl->SetSelectedDate(std::nullopt); Insert(aNewState); GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter); diff --git a/xmloff/qa/unit/data/content-control-date.fodt b/xmloff/qa/unit/data/content-control-date.fodt index dd3749a02e99..c49e51339c3b 100644 --- a/xmloff/qa/unit/data/content-control-date.fodt +++ b/xmloff/qa/unit/data/content-control-date.fodt @@ -2,7 +2,7 @@ <office:document xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> <office:body> <office:text> - <text:p><loext:content-control loext:date="true" loext:date-format="YYYY-MM-DD" loext:date-rfc-language-tag="en-US">choose a date</loext:content-control></text:p> + <text:p><loext:content-control loext:date="true" loext:date-format="YYYY-MM-DD" loext:date-rfc-language-tag="en-US" loext:current-date="2022-05-25T00:00:00Z">choose a date</loext:content-control></text:p> </office:text> </office:body> </office:document> diff --git a/xmloff/qa/unit/text.cxx b/xmloff/qa/unit/text.cxx index 4f32c23025de..9ac816298b60 100644 --- a/xmloff/qa/unit/text.cxx +++ b/xmloff/qa/unit/text.cxx @@ -665,6 +665,8 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testDateContentControlExport) xContentControlProps->setPropertyValue("Date", uno::Any(true)); xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("YYYY-MM-DD"))); xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); + xContentControlProps->setPropertyValue("CurrentDate", + uno::Any(OUString("2022-05-25T00:00:00Z"))); xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // When exporting to ODT: @@ -685,6 +687,7 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testDateContentControlExport) assertXPath(pXmlDoc, "//loext:content-control", "date", "true"); assertXPath(pXmlDoc, "//loext:content-control", "date-format", "YYYY-MM-DD"); assertXPath(pXmlDoc, "//loext:content-control", "date-rfc-language-tag", "en-US"); + assertXPath(pXmlDoc, "//loext:content-control", "current-date", "2022-05-25T00:00:00Z"); } CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testDateContentControlImport) @@ -721,6 +724,9 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testDateContentControlImport) OUString aDateLanguage; xContentControlProps->getPropertyValue("DateLanguage") >>= aDateLanguage; CPPUNIT_ASSERT_EQUAL(OUString("en-US"), aDateLanguage); + OUString aCurrentDate; + xContentControlProps->getPropertyValue("CurrentDate") >>= aCurrentDate; + CPPUNIT_ASSERT_EQUAL(OUString("2022-05-25T00:00:00Z"), aCurrentDate); } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index e2ec85cb8105..4105772445b2 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -3940,6 +3940,12 @@ void XMLTextParagraphExport::ExportContentControl( { GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATE_RFC_LANGUAGE_TAG, aDateLanguage); } + OUString aCurrentDate; + xPropertySet->getPropertyValue("CurrentDate") >>= aCurrentDate; + if (!aCurrentDate.isEmpty()) + { + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_CURRENT_DATE, aCurrentDate); + } } 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 24b81e04a67c..22af458e43bc 100644 --- a/xmloff/source/text/xmlcontentcontrolcontext.cxx +++ b/xmloff/source/text/xmlcontentcontrolcontext.cxx @@ -112,6 +112,11 @@ void XMLContentControlContext::startFastElement( m_aDateLanguage = rIter.toString(); break; } + case XML_ELEMENT(LO_EXT, XML_CURRENT_DATE): + { + m_aCurrentDate = rIter.toString(); + break; + } default: XMLOFF_WARN_UNKNOWN("xmloff", rIter); } @@ -193,6 +198,10 @@ void XMLContentControlContext::endFastElement(sal_Int32) { xPropertySet->setPropertyValue("DateLanguage", uno::Any(m_aDateLanguage)); } + if (!m_aCurrentDate.isEmpty()) + { + xPropertySet->setPropertyValue("CurrentDate", uno::Any(m_aCurrentDate)); + } } css::uno::Reference<css::xml::sax::XFastContextHandler> diff --git a/xmloff/source/text/xmlcontentcontrolcontext.hxx b/xmloff/source/text/xmlcontentcontrolcontext.hxx index 623ef97e8df3..2c3ecfb9cafb 100644 --- a/xmloff/source/text/xmlcontentcontrolcontext.hxx +++ b/xmloff/source/text/xmlcontentcontrolcontext.hxx @@ -47,6 +47,7 @@ class XMLContentControlContext : public SvXMLImportContext bool m_bDate = false; OUString m_aDateFormat; OUString m_aDateLanguage; + OUString m_aCurrentDate; public: XMLContentControlContext(SvXMLImport& rImport, sal_Int32 nElement, XMLHints_Impl& rHints,
