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. Change-Id: I00f4e87ebfe0e8a19486367c32d472ccd2ff16a8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135035 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
This commit is contained in:
@@ -2887,6 +2887,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">
|
||||
|
||||
@@ -136,6 +136,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;
|
||||
|
||||
@@ -217,6 +220,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;
|
||||
|
||||
|
||||
@@ -882,6 +882,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: */
|
||||
|
||||
@@ -601,6 +601,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:
|
||||
@@ -614,6 +616,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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
@@ -529,6 +530,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
|
||||
@@ -828,6 +830,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();
|
||||
@@ -951,6 +968,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();
|
||||
|
||||
@@ -1031,7 +1031,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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -2374,7 +2374,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())
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -732,6 +732,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:
|
||||
@@ -752,6 +754,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)
|
||||
@@ -788,6 +791,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();
|
||||
|
||||
@@ -3966,6 +3966,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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user