summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2022-04-29 08:12:25 +0200
committerMiklos Vajna <vmiklos@collabora.com>2022-04-29 09:03:01 +0200
commitc53d3a1f4b8430507d54f5fac336870df7a700af (patch)
tree4f09f1b412ac6d3e3e0ed57429242279922d12d8
parentd3849255b76e92a42f653c266b88945708984c4f (diff)
sw content controls, checkbox: add DOCX import
My expectation was that <w14:checked w14:val="0"/> would be mapped to a single SPRM where the int value is 0 or 1 depending on if this is a true or false boolean. But the w14 tokenizer rules actually created a NS_ooxml::LN_CT_SdtCheckbox_checked token with a NS_ooxml::LN_CT_OnOff_val token in it, which itself again didn't contain just a bool but dedicated NS_ooxml::LN_ST_OnOff_true, NS_ooxml::LN_ST_OnOff_1, etc values. To make this more complicated, TextEffectsHandler even depends on this weird behavior. Bring the w14 rules closer to the "main" wml rules by folding the NS_ooxml::LN_CT_OnOff_val token into the parent token (NS_ooxml::LN_CT_SdtCheckbox_checked in this case), but leave the NS_ooxml::LN_ST_OnOff_* values unchanged for now. The rest of the changes are more straightforward: we now handle inline/run checkbox SDTs similar to rich text ones, i.e. map them to Writer content controls, rather than just doing a poor mapping to grab-bags. The main benefit here is that the checkbox type of Writer content controls actually change their value on mouse click, so it's possible to fill in such forms. Change-Id: Idbf49a8ff1843d5271f2836e5299c4387bb58e55 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133588 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
-rw-r--r--writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx46
-rw-r--r--writerfilter/qa/cppunittests/dmapper/data/sdt-run-checkbox.docxbin0 -> 4244 bytes
-rw-r--r--writerfilter/source/dmapper/DomainMapper.cxx57
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx21
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.hxx4
-rw-r--r--writerfilter/source/dmapper/SdtHelper.cxx18
-rw-r--r--writerfilter/source/dmapper/SdtHelper.hxx23
-rw-r--r--writerfilter/source/dmapper/TextEffectsHandler.cxx1
-rw-r--r--writerfilter/source/dmapper/TextEffectsHandler.hxx8
-rw-r--r--writerfilter/source/ooxml/model.xml7
10 files changed, 172 insertions, 13 deletions
diff --git a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx
index da2663b93409..c96cb604f91f 100644
--- a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx
+++ b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx
@@ -84,6 +84,52 @@ CPPUNIT_TEST_FIXTURE(Test, testSdtRunRichText)
xContent->getPropertyValue("CharHeight") >>= fCharheight;
CPPUNIT_ASSERT_EQUAL(24.f, fCharheight);
}
+
+CPPUNIT_TEST_FIXTURE(Test, testSdtRunCheckbox)
+{
+ // Given a document with a checkbox inline/run SDT:
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "sdt-run-checkbox.docx";
+
+ // When loading the document:
+ getComponent() = loadFromDesktop(aURL);
+
+ // Then make sure that the doc model has a clickable checkbox content control:
+ uno::Reference<text::XTextDocument> xTextDocument(getComponent(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(),
+ uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
+ uno::Reference<container::XEnumerationAccess> xPara(xParaEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xPortionEnum = xPara->createEnumeration();
+ uno::Reference<beans::XPropertySet> xPortion(xPortionEnum->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xPortion->getPropertyValue("TextPortionType") >>= aTextPortionType;
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: ContentControl
+ // - Actual : Text
+ // i.e. the SDT was imported as plain text, making it hard to fill in checkboxes.
+ CPPUNIT_ASSERT_EQUAL(OUString("ContentControl"), aTextPortionType);
+ uno::Reference<text::XTextContent> xContentControl;
+ xPortion->getPropertyValue("ContentControl") >>= xContentControl;
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ bool bCheckbox{};
+ xContentControlProps->getPropertyValue("Checkbox") >>= bCheckbox;
+ 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<text::XTextRange> xContentControlRange(xContentControl, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xContentControlRange->getText();
+ uno::Reference<container::XEnumerationAccess> xContentEnumAccess(xText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xContentEnum = xContentEnumAccess->createEnumeration();
+ uno::Reference<text::XTextRange> xContent(xContentEnum->nextElement(), uno::UNO_QUERY);
+ CPPUNIT_ASSERT_EQUAL(OUString(u"☒"), xContent->getString());
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-checkbox.docx b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-checkbox.docx
new file mode 100644
index 000000000000..c6718b97c2a0
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-checkbox.docx
Binary files differ
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx
index 61ff5a2c67fb..cd0031f107f6 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -1083,6 +1083,7 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
{
m_pImpl->m_pSdtHelper->setControlType(SdtControlType::richText);
m_pImpl->PushSdt();
+ break;
}
}
m_pImpl->SetSdt(true);
@@ -1094,6 +1095,7 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
switch (m_pImpl->m_pSdtHelper->getControlType())
{
case SdtControlType::richText:
+ case SdtControlType::checkBox:
m_pImpl->PopSdt();
break;
default:
@@ -2771,6 +2773,20 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
case NS_ooxml::LN_CT_SdtPlaceholder_docPart:
case NS_ooxml::LN_CT_SdtPr_color:
{
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ if (nSprmId == NS_ooxml::LN_CT_SdtPr_checkbox)
+ {
+ m_pImpl->m_pSdtHelper->setControlType(SdtControlType::checkBox);
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if (pProperties)
+ {
+ pProperties->resolve(*this);
+ }
+ break;
+ }
+ }
+
// this is an unsupported SDT property, create a grab bag for it
OUString sName;
switch (nSprmId)
@@ -2819,13 +2835,42 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
}
break;
case NS_ooxml::LN_CT_SdtCheckbox_checked:
- m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_checked", sStringValue);
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ // nIntValue is not just 0 or 1, because we're in the w14 namespace's ST_OnOff.
+ if (nIntValue == NS_ooxml::LN_ST_OnOff_true || nIntValue == NS_ooxml::LN_ST_OnOff_1)
+ {
+ m_pImpl->m_pSdtHelper->SetChecked();
+ }
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_checked",
+ TextEffectsHandler::getOnOffString(nIntValue));
+ }
break;
case NS_ooxml::LN_CT_SdtCheckbox_checkedState:
- m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_checkedState", sStringValue);
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ m_pImpl->m_pSdtHelper->SetCheckedState(OUString(sal_Unicode(sStringValue.toInt32(16))));
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_checkedState",
+ sStringValue);
+ }
break;
case NS_ooxml::LN_CT_SdtCheckbox_uncheckedState:
- m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtCheckbox_uncheckedState", sStringValue);
+ if (!m_pImpl->GetSdtStarts().empty())
+ {
+ m_pImpl->m_pSdtHelper->SetUncheckedState(
+ OUString(sal_Unicode(sStringValue.toInt32(16))));
+ }
+ else
+ {
+ m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag,
+ "ooxml:CT_SdtCheckbox_uncheckedState", sStringValue);
+ }
break;
case NS_ooxml::LN_CT_SdtDocPart_docPartGallery:
m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "ooxml:CT_SdtDocPart_docPartGallery", sStringValue);
@@ -2931,6 +2976,12 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext )
rContext->Insert(PROP_CHAR_TRANSPARENCE, uno::makeAny(nTransparency));
}
}
+ else if (nSprmId == NS_ooxml::LN_cntxtAlts_cntxtAlts)
+ {
+ pTextEffectsHandlerPtr->lcl_sprm(rSprm);
+ beans::PropertyValue aGrabBag = pTextEffectsHandlerPtr->getInteropGrabBag();
+ rContext->Insert(*aPropertyId, uno::makeAny(aGrabBag), true, CHAR_GRAB_BAG);
+ }
}
}
break;
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index c95bbf568678..2d956b5ab09c 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -849,6 +849,11 @@ void DomainMapper_Impl::PushSdt()
m_xSdtStarts.push({bStart, OUString(), xCursor->getStart()});
}
+const std::stack<BookmarkInsertPosition>& DomainMapper_Impl::GetSdtStarts() const
+{
+ return m_xSdtStarts;
+}
+
void DomainMapper_Impl::PopSdt()
{
if (m_xSdtStarts.empty())
@@ -886,7 +891,23 @@ void DomainMapper_Impl::PopSdt()
xContentControlProps->setPropertyValue("ShowingPlaceHolder",
uno::makeAny(m_pSdtHelper->GetShowingPlcHdr()));
}
+
+ if (m_pSdtHelper->getControlType() == SdtControlType::checkBox)
+ {
+ xContentControlProps->setPropertyValue("Checkbox", uno::makeAny(true));
+
+ xContentControlProps->setPropertyValue("Checked", uno::makeAny(m_pSdtHelper->GetChecked()));
+
+ xContentControlProps->setPropertyValue("CheckedState",
+ uno::makeAny(m_pSdtHelper->GetCheckedState()));
+
+ xContentControlProps->setPropertyValue("UncheckedState",
+ uno::makeAny(m_pSdtHelper->GetUncheckedState()));
+ }
+
xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ m_pSdtHelper->clear();
}
void DomainMapper_Impl::PushProperties(ContextType eId)
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 32721e170c38..5a48e26fe73b 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -725,8 +725,12 @@ public:
/// Setter method for m_bSdt.
void SetSdt(bool bSdt);
+
void PushSdt();
void PopSdt();
+ /// Gives access to the currently open run/inline SDTs.
+ const std::stack<BookmarkInsertPosition>& GetSdtStarts() const;
+
/// Getter method for m_bSdt.
bool GetSdt() const { return m_bSdt;}
bool GetParaChanged() const { return m_bParaChanged;}
diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx
index 924d70272181..81e503a7f549 100644
--- a/writerfilter/source/dmapper/SdtHelper.cxx
+++ b/writerfilter/source/dmapper/SdtHelper.cxx
@@ -423,6 +423,21 @@ void SdtHelper::SetShowingPlcHdr() { m_bShowingPlcHdr = true; }
bool SdtHelper::GetShowingPlcHdr() const { return m_bShowingPlcHdr; }
+void SdtHelper::SetChecked() { m_bChecked = true; }
+
+bool SdtHelper::GetChecked() const { return m_bChecked; }
+
+void SdtHelper::SetCheckedState(const OUString& rCheckedState) { m_aCheckedState = rCheckedState; }
+
+OUString SdtHelper::GetCheckedState() const { return m_aCheckedState; }
+
+void SdtHelper::SetUncheckedState(const OUString& rUncheckedState)
+{
+ m_aUncheckedState = rUncheckedState;
+}
+
+OUString SdtHelper::GetUncheckedState() const { return m_aUncheckedState; }
+
void SdtHelper::clear()
{
m_aDropDownItems.clear();
@@ -432,6 +447,9 @@ void SdtHelper::clear()
m_sDataBindingStoreItemID.clear();
m_aGrabBag.clear();
m_bShowingPlcHdr = false;
+ m_bChecked = false;
+ m_aCheckedState.clear();
+ m_aUncheckedState.clear();
}
} // namespace writerfilter::dmapper
diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx
index f0515f91c7a3..62d04d140128 100644
--- a/writerfilter/source/dmapper/SdtHelper.hxx
+++ b/writerfilter/source/dmapper/SdtHelper.hxx
@@ -43,6 +43,7 @@ enum class SdtControlType
dropDown,
plainText,
richText,
+ checkBox,
unsupported, // Sdt block is defined, but we still do not support such type of field
unknown
};
@@ -97,6 +98,15 @@ class SdtHelper final : public virtual SvRefBase
/// Current contents are placeholder text.
bool m_bShowingPlcHdr = false;
+ /// If this is a checkbox, is the checkbox checked?
+ bool m_bChecked = false;
+
+ /// If this is a checkbox, the value of a checked checkbox.
+ OUString m_aCheckedState;
+
+ /// If this is a checkbox, the value of an unchecked checkbox.
+ OUString m_aUncheckedState;
+
/// Create and append the drawing::XControlShape, containing the various models.
void createControlShape(css::awt::Size aSize,
css::uno::Reference<css::awt::XControlModel> const& xControlModel,
@@ -106,9 +116,6 @@ class SdtHelper final : public virtual SvRefBase
void loadPropertiesXMLs();
- /// Clear all collected attributes for further reuse
- void clear();
-
public:
explicit SdtHelper(DomainMapper_Impl& rDM_Impl,
css::uno::Reference<css::uno::XComponentContext> const& xContext);
@@ -162,6 +169,16 @@ public:
void SetShowingPlcHdr();
bool GetShowingPlcHdr() const;
+
+ void SetChecked();
+ bool GetChecked() const;
+ void SetCheckedState(const OUString& rCheckedState);
+ OUString GetCheckedState() const;
+ void SetUncheckedState(const OUString& rUncheckedState);
+ OUString GetUncheckedState() const;
+
+ /// Clear all collected attributes for further reuse
+ void clear();
};
} // namespace writerfilter::dmapper
diff --git a/writerfilter/source/dmapper/TextEffectsHandler.cxx b/writerfilter/source/dmapper/TextEffectsHandler.cxx
index 3288556c8979..d145c854fc80 100644
--- a/writerfilter/source/dmapper/TextEffectsHandler.cxx
+++ b/writerfilter/source/dmapper/TextEffectsHandler.cxx
@@ -66,6 +66,7 @@ OUString lclGetNameForElementId(sal_uInt32 aId)
aIdMap[NS_ooxml::LN_CT_Props3D_extrusionClr] = "extrusionClr";
aIdMap[NS_ooxml::LN_CT_Props3D_contourClr] = "contourClr";
aIdMap[NS_ooxml::LN_CT_StylisticSets_styleSet] = "styleSet";
+ aIdMap[NS_ooxml::LN_cntxtAlts_cntxtAlts] = "cntxtAlts";
}
return aIdMap[aId];
}
diff --git a/writerfilter/source/dmapper/TextEffectsHandler.hxx b/writerfilter/source/dmapper/TextEffectsHandler.hxx
index 22127c6c81b2..30a8435b2829 100644
--- a/writerfilter/source/dmapper/TextEffectsHandler.hxx
+++ b/writerfilter/source/dmapper/TextEffectsHandler.hxx
@@ -33,10 +33,6 @@ private:
void convertElementIdToPropertyId(sal_Int32 aElementId);
- // LoggedProperties
- virtual void lcl_attribute(Id aName, Value& aValue) override;
- virtual void lcl_sprm(Sprm& sprm) override;
-
public:
explicit TextEffectsHandler(sal_uInt32 aElementId);
virtual ~TextEffectsHandler() override;
@@ -63,6 +59,10 @@ public:
static OUString getNumSpacingString(sal_Int32 nType);
static sal_uInt8 GetTextFillSolidFillAlpha(const css::beans::PropertyValue& rValue);
+
+ // LoggedProperties
+ virtual void lcl_attribute(Id aName, Value& aValue) override;
+ virtual void lcl_sprm(Sprm& sprm) override;
};
}
diff --git a/writerfilter/source/ooxml/model.xml b/writerfilter/source/ooxml/model.xml
index 439f5e8d8a20..bff00b41a925 100644
--- a/writerfilter/source/ooxml/model.xml
+++ b/writerfilter/source/ooxml/model.xml
@@ -4886,7 +4886,7 @@
</define>
<define name="CT_SdtCheckbox">
<element name="checked">
- <ref name="CT_String"/>
+ <ref name="CT_OnOff"/>
</element>
<element name="checkedState">
<ref name="CT_String"/>
@@ -5260,8 +5260,9 @@
<attribute name="id" tokenid="ooxml:CT_StyleSet_id"/>
<attribute name="val" tokenid="ooxml:CT_StyleSet_val"/>
</resource>
- <resource name="CT_OnOff" resource="Properties">
- <attribute name="val" tokenid="ooxml:CT_OnOff_val"/>
+ <resource name="CT_OnOff" resource="Value">
+ <attribute name="val" tokenid="ooxml:CT_OnOff_val" action="setValue"/>
+ <action name="start" action="setDefaultBooleanValue"/>
</resource>
<!-- Main element content -->