diff options
28 files changed, 205 insertions, 11 deletions
diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index bd650daea6e7..34bcf3099048 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -1221,6 +1221,7 @@ namespace xmloff::token { XML_LIST_STYLE, XML_LIST_STYLE_NAME, XML_LN, + XML_LOCK, XML_LOCKED, XML_LOG, XML_LOGARITHMIC, diff --git a/offapi/com/sun/star/text/ContentControl.idl b/offapi/com/sun/star/text/ContentControl.idl index 6abcc79fd204..59894741de2b 100644 --- a/offapi/com/sun/star/text/ContentControl.idl +++ b/offapi/com/sun/star/text/ContentControl.idl @@ -127,6 +127,12 @@ service ContentControl @since LibreOffice 7.5 */ [optional, property] long Id; + + /** Describes whether the control itself and/or its data can be modified or deleted by the user. + + @since LibreOffice 7.6 + */ + [optional, property] string Lock; }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 6a993840e5e4..613ded76689c 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -3008,6 +3008,11 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. <rng:ref name="string"/> </rng:attribute> </rng:optional> + <rng:optional> + <rng:attribute name="loext:lock"> + <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 94399d6818e8..e561fa6e23fb 100644 --- a/sw/inc/formatcontentcontrol.hxx +++ b/sw/inc/formatcontentcontrol.hxx @@ -179,6 +179,9 @@ class SW_DLLPUBLIC SwContentControl : public sw::BroadcastingModify /// The id: just remembered. sal_Int32 m_nId = 0; + /// The control and content locks: mostly just remembered. + OUString m_aLock; + /// Stores a list item index, in case the doc model is not yet updated. // i.e. temporarily store the selected item until the text is inserted by GotoContentControl. std::optional<size_t> m_oSelectedListItem; @@ -359,8 +362,17 @@ public: sal_Int32 GetId() const { return m_nId; } + // At the design level, define how the control should be locked. No effect at implementation lvl + void SetLock(bool bLockContent, bool bLockControl); + void SetLock(const OUString& rLock) { m_aLock = rLock; } + + // At the design level, get how the control is locked. Does not reflect actual implementation. + std::optional<bool> GetLock(bool bControl) const; + const OUString& GetLock() const { return m_aLock; } + void SetReadWrite(bool bReadWrite) { m_bReadWrite = bReadWrite; } + // At the implementation level, define whether the user can directly modify the contents. bool GetReadWrite() const { return m_bReadWrite; } SwContentControlType GetType() const; diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx index 199f4847c930..2e7158fac97e 100644 --- a/sw/inc/unoprnms.hxx +++ b/sw/inc/unoprnms.hxx @@ -917,6 +917,7 @@ inline constexpr OUStringLiteral UNO_NAME_COLOR = u"Color"; inline constexpr OUStringLiteral UNO_NAME_ALIAS = u"Alias"; inline constexpr OUStringLiteral UNO_NAME_TAG = u"Tag"; inline constexpr OUStringLiteral UNO_NAME_ID = u"Id"; +inline constexpr OUStringLiteral UNO_NAME_LOCK = u"Lock"; inline constexpr OUStringLiteral UNO_NAME_DATE_STRING = u"DateString"; #endif diff --git a/sw/qa/core/data/docm/testModernVBA.docm b/sw/qa/core/data/docm/testModernVBA.docm Binary files differindex 49e53b615622..c08d738c8adb 100644 --- a/sw/qa/core/data/docm/testModernVBA.docm +++ b/sw/qa/core/data/docm/testModernVBA.docm diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index a521cdebca8d..985353db3e80 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -633,6 +633,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) xContentControlProps->setPropertyValue("Color", uno::Any(OUString("008000"))); xContentControlProps->setPropertyValue("Alias", uno::Any(OUString("myalias"))); xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("mytag"))); + xContentControlProps->setPropertyValue("Lock", uno::Any(OUString("sdtContentLocked"))); xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // Then make sure that the specified properties are set: @@ -658,6 +659,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) CPPUNIT_ASSERT_EQUAL(OUString("008000"), pContentControl->GetColor()); CPPUNIT_ASSERT_EQUAL(OUString("myalias"), pContentControl->GetAlias()); CPPUNIT_ASSERT_EQUAL(OUString("mytag"), pContentControl->GetTag()); + CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), pContentControl->GetLock()); } CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testListIdState) diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index d9d5802e0606..7a9a14c1cb1a 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -423,6 +423,8 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport) xContentControlProps->setPropertyValue("Color", uno::Any(OUString("008000"))); xContentControlProps->setPropertyValue("Alias", uno::Any(OUString("myalias"))); xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("mytag"))); + xContentControlProps->setPropertyValue("Lock", uno::Any(OUString("sdtLocked"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // When exporting to DOCX: @@ -445,6 +447,7 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport) assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w15:color", "val", "008000"); assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:alias", "val", "myalias"); assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:tag", "val", "mytag"); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:lock", "val", "sdtLocked"); } CPPUNIT_TEST_FIXTURE(Test, testNegativePageBorder) diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx index 3f08067bf1a3..a89fc773ae34 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx @@ -1016,6 +1016,7 @@ CPPUNIT_TEST_FIXTURE(Test, testSimpleSdts) assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:sdt/w:sdtPr/w:text", 1); assertXPath(pXmlDoc, "//*/w:sdt/w:sdtPr/w:id", 5); + assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:lock", 1); assertXPath(pXmlDoc, "/w:document/w:body/w:sdt[1]/w:sdtPr/w:picture", 1); assertXPath(pXmlDoc, "/w:document/w:body/w:sdt[2]/w:sdtPr/w:group", 1); assertXPath(pXmlDoc, "/w:document/w:body/w:p[4]/w:sdt/w:sdtPr/w:citation", 1); diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 1db39849fbe6..ffbe23589f99 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -445,6 +445,42 @@ bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& rKeyCode) return false; } +// NOTE: call SetReadWrite separately to implement true (un)locking. +// This is mostly a theoretical function; the lock state is mainly kept for round-tripping purposes. +// It is implemented here primarily for pointless VBA control, but with the intention that it +// could be made functionally useful as well for checkboxes/dropdowns/pictures. +// Returns whether the content (bControl=false) cannot be modified, +// or if the control cannot be deleted. +std::optional<bool> SwContentControl::GetLock(bool bControl) const +{ + std::optional<bool> oLock; + if (m_aLock.isEmpty()) + return oLock; + else if (m_aLock.equalsIgnoreAsciiCase("sdtContentLocked")) + oLock = true; + else if (m_aLock.equalsIgnoreAsciiCase("unlocked")) + oLock = false; + else if (m_aLock.equalsIgnoreAsciiCase("sdtLocked")) + oLock = bControl; + else if (m_aLock.equalsIgnoreAsciiCase("contentLocked")) + oLock = !bControl; + + assert(oLock && "invalid or unknown lock state"); + return oLock; +} + +void SwContentControl::SetLock(bool bLockContent, bool bLockControl) +{ + if (!bLockContent && !bLockControl) + m_aLock = "unlocked"; + else if (bLockContent && bLockControl) + m_aLock = "sdtContentLocked"; + else if (bLockContent) + m_aLock = "contentLocked"; + else + m_aLock = "sdtLocked"; +} + SwContentControlType SwContentControl::GetType() const { if (m_bCheckbox) @@ -526,6 +562,8 @@ void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("tag"), BAD_CAST(m_aTag.toUtf8().getStr())); (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(m_nId).getStr())); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("lock"), + BAD_CAST(m_aLock.toUtf8().getStr())); if (!m_aListItems.empty()) { diff --git a/sw/source/core/unocore/unocontentcontrol.cxx b/sw/source/core/unocore/unocontentcontrol.cxx index 1a3092e21c52..8b6c4e798f2c 100644 --- a/sw/source/core/unocore/unocontentcontrol.cxx +++ b/sw/source/core/unocore/unocontentcontrol.cxx @@ -179,6 +179,7 @@ public: OUString m_aAlias; OUString m_aTag; sal_Int32 m_nId; + OUString m_aLock; Impl(SwXContentControl& rThis, SwDoc& rDoc, SwContentControl* pContentControl, uno::Reference<text::XText> xParentText, std::unique_ptr<const TextRangeList_t> pPortions) @@ -493,6 +494,7 @@ void SwXContentControl::AttachImpl(const uno::Reference<text::XTextRange>& xText pContentControl->SetAlias(m_pImpl->m_aAlias); pContentControl->SetTag(m_pImpl->m_aTag); pContentControl->SetId(m_pImpl->m_nId); + pContentControl->SetLock(m_pImpl->m_aLock); SwFormatContentControl aContentControl(pContentControl, nWhich); bool bSuccess @@ -983,6 +985,21 @@ void SAL_CALL SwXContentControl::setPropertyValue(const OUString& rPropertyName, } } } + else if (rPropertyName == UNO_NAME_LOCK) + { + OUString aValue; + if (rValue >>= aValue) + { + if (m_pImpl->m_bIsDescriptor) + { + m_pImpl->m_aLock = aValue; + } + else + { + m_pImpl->m_pContentControl->SetLock(aValue); + } + } + } else { throw beans::UnknownPropertyException(); @@ -1245,6 +1262,17 @@ uno::Any SAL_CALL SwXContentControl::getPropertyValue(const OUString& rPropertyN aRet <<= m_pImpl->m_pContentControl->GetId(); } } + else if (rPropertyName == UNO_NAME_LOCK) + { + if (m_pImpl->m_bIsDescriptor) + { + aRet <<= m_pImpl->m_aLock; + } + else + { + aRet <<= m_pImpl->m_pContentControl->GetLock(); + } + } else { throw beans::UnknownPropertyException(); diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx index a37843b1538a..437f293a9f78 100644 --- a/sw/source/core/unocore/unomap1.cxx +++ b/sw/source/core/unocore/unomap1.cxx @@ -1013,6 +1013,7 @@ o3tl::span<const SfxItemPropertyMapEntry> SwUnoPropertyMapProvider::GetContentCo { UNO_NAME_ALIAS, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, { UNO_NAME_TAG, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, { UNO_NAME_ID, 0, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 }, + { UNO_NAME_LOCK, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 }, { UNO_NAME_DATE_STRING, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 }, }; diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 76d608ddc01e..11bf0e1f9b82 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -617,6 +617,8 @@ void SdtBlockHelper::DeleteAndResetTheLists() m_aAlias.clear(); if (!m_aTag.isEmpty()) m_aTag.clear(); + if (!m_aLock.isEmpty()) + m_aLock.clear(); if (!m_aPlaceHolderDocPart.isEmpty()) m_aPlaceHolderDocPart.clear(); if (!m_aColor.isEmpty()) @@ -724,6 +726,9 @@ void SdtBlockHelper::WriteExtraParams(const ::sax_fastparser::FSHelperPtr& pSeri if (!m_aTag.isEmpty()) pSerializer->singleElementNS(XML_w, XML_tag, FSNS(XML_w, XML_val), m_aTag); + + if (!m_aLock.isEmpty()) + pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val), m_aLock); } void SdtBlockHelper::EndSdtBlock(const ::sax_fastparser::FSHelperPtr& pSerializer) @@ -838,6 +843,11 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const uno::Sequence<beans::Property if (!(aPropertyValue.Value >>= m_aTag)) SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt tag value"); } + else if (aPropertyValue.Name == "ooxml:CT_SdtPr_lock" && m_aLock.isEmpty()) + { + if (!(aPropertyValue.Value >>= m_aLock)) + SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected sdt lock value"); + } else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id") m_bHasId = true; else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation") @@ -2398,6 +2408,12 @@ void DocxAttributeOutput::WriteContentControlStart() OString::number(m_pContentControl->GetId())); } + if (!m_pContentControl->GetLock().isEmpty()) + { + m_pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val), + m_pContentControl->GetLock()); + } + if (m_pContentControl->GetShowingPlaceHolder()) { m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr); diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index f8da5a4bd32d..055d277c7827 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -145,6 +145,7 @@ public: bool m_bShowingPlaceHolder; OUString m_aAlias; OUString m_aTag; + OUString m_aLock; sal_Int32 m_nSdtPrToken; void DeleteAndResetTheLists(); diff --git a/sw/source/ui/vba/vbacontentcontrol.cxx b/sw/source/ui/vba/vbacontentcontrol.cxx index 0a156e35c96b..e53ad6c51d31 100644 --- a/sw/source/ui/vba/vbacontentcontrol.cxx +++ b/sw/source/ui/vba/vbacontentcontrol.cxx @@ -95,6 +95,13 @@ sal_Bool SwVbaContentControl::getChecked() void SwVbaContentControl::setChecked(sal_Bool bSet) { + // Word 2010: if locked, then the checked status is changed, but not the underlying text. + // Do we really want to do that? That is pretty bizarre behaviour... + // For now, just implement what seems to be a more logical response. + // TODO: test with modern versions. + if (getLockContents()) + return; + std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl(); if (pCC->GetCheckbox() && pCC->GetChecked() != static_cast<bool>(bSet)) { @@ -487,21 +494,27 @@ sal_Int32 SwVbaContentControl::getLevel() sal_Bool SwVbaContentControl::getLockContentControl() { - SAL_INFO("sw.vba", "SwVbaContentControl::getLockContentControl stub"); - // returns whether the user can delete a content control from the active document. - return true; + const std::shared_ptr<SwContentControl>& pCC = m_rCC.GetContentControl().GetContentControl(); + std::optional<bool> oLock = pCC->GetLock(/*bControl=*/true); + return oLock && *oLock; } -void SwVbaContentControl::setLockContentControl(sal_Bool /*bSet*/) +void SwVbaContentControl::setLockContentControl(sal_Bool bSet) { - SAL_INFO("sw.vba", "SwVbaContentControl::setLockContentControl stub"); + std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl(); + std::optional<bool> oLock = pCC->GetLock(/*bControl=*/false); + pCC->SetLock(/*bContents=*/oLock && *oLock, /*bControl=*/bSet); } sal_Bool SwVbaContentControl::getLockContents() { - // Pseudo-implementation - the need for locking in a form would be very rare. - // LO uses this for internal purposes. Only expose it to VBA when safe. const std::shared_ptr<SwContentControl>& pCC = m_rCC.GetContentControl().GetContentControl(); + // If the theoretical design model says it is locked, then report as locked. + std::optional<bool> oLock = pCC->GetLock(/*bControl=*/false); + if (oLock && *oLock) + return true; + + // Now check the real implementation. // Checkbox/DropDown/Picture are normally locked - but not in this sense. Report as unlocked. if (pCC->GetType() == SwContentControlType::CHECKBOX || pCC->GetType() == SwContentControlType::DROP_DOWN_LIST @@ -516,6 +529,10 @@ sal_Bool SwVbaContentControl::getLockContents() void SwVbaContentControl::setLockContents(sal_Bool bSet) { std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl(); + // Set the lock both theoretically and actually. + std::optional<bool> oLock = pCC->GetLock(/*bControl=*/true); + pCC->SetLock(/*bContents=*/bSet, /*bControl=*/oLock && *oLock); + // Checkbox/DropDown/Picture are normally locked in LO implementation - don't unlock them. if (pCC->GetType() == SwContentControlType::CHECKBOX || pCC->GetType() == SwContentControlType::DROP_DOWN_LIST @@ -673,17 +690,24 @@ void SwVbaContentControl::Copy() void SwVbaContentControl::Cut() { + if (getLockContentControl()) + return; + SAL_INFO("sw.vba", "SwVbaContentControl::Cut[" << getID() << "], but missing sending to clipboard"); - m_rCC.Delete(/*bSaveContents=*/false); + m_rCC.Delete(/*bSaveContents=*/getLockContents()); } void SwVbaContentControl::Delete(const uno::Any& DeleteContents) { + if (getLockContentControl()) + return; + bool bDeleteContents = false; DeleteContents >>= bDeleteContents; - m_rCC.Delete(!bDeleteContents); + + m_rCC.Delete(/*bSaveContents=*/!bDeleteContents || getLockContents()); } void SwVbaContentControl::SetCheckedSymbol(sal_Int32 Character, const uno::Any& Font) diff --git a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx index be5154414551..8f9ea8ca5ddb 100644 --- a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx +++ b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx @@ -71,6 +71,10 @@ CPPUNIT_TEST_FIXTURE(Test, testSdtRunRichText) xContentControlProps->getPropertyValue("Tag") >>= aTag; // This was empty. CPPUNIT_ASSERT_EQUAL(OUString("mytag"), aTag); + OUString aLock; + xContentControlProps->getPropertyValue("Lock") >>= aLock; + // This was empty. + CPPUNIT_ASSERT_EQUAL(OUString("contentLocked"), aLock); } CPPUNIT_TEST_FIXTURE(Test, testSdtRunPlainText) diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx Binary files differindex b7f291f776bf..aabc745bcf0e 100644 --- a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx +++ b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 1e0f9597b3b4..a0b06009b6c6 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -2848,6 +2848,7 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) case NS_ooxml::LN_CT_SdtPr_showingPlcHdr: case NS_ooxml::LN_CT_SdtPr_color: case NS_ooxml::LN_CT_SdtPr_tag: + case NS_ooxml::LN_CT_SdtPr_lock: { if (!m_pImpl->GetSdtStarts().empty()) { @@ -2885,6 +2886,12 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) break; } + if (nSprmId == NS_ooxml::LN_CT_SdtPr_lock) + { + m_pImpl->m_pSdtHelper->SetLock(sStringValue); + break; + } + if (nSprmId == NS_ooxml::LN_CT_SdtPr_checkbox) { m_pImpl->m_pSdtHelper->setControlType(SdtControlType::checkBox); @@ -2932,6 +2939,7 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) case NS_ooxml::LN_CT_SdtPr_id: sName = "ooxml:CT_SdtPr_id"; break; case NS_ooxml::LN_CT_SdtPr_alias: sName = "ooxml:CT_SdtPr_alias"; break; case NS_ooxml::LN_CT_SdtPr_tag: sName = "ooxml:CT_SdtPr_tag"; break; + case NS_ooxml::LN_CT_SdtPr_lock: sName = "ooxml:CT_SdtPr_lock"; break; case NS_ooxml::LN_CT_SdtPlaceholder_docPart: sName = "ooxml:CT_SdtPlaceholder_docPart"; break; case NS_ooxml::LN_CT_SdtPr_showingPlcHdr: sName = "ooxml:CT_SdtPr_showingPlcHdr"; break; case NS_ooxml::LN_CT_SdtPr_color: sName = "ooxml:CT_SdtPr_color"; break; @@ -2953,8 +2961,10 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) if (pProperties) pProperties->resolve(*this); - if (nSprmId == NS_ooxml::LN_CT_SdtPr_alias || nSprmId == NS_ooxml::LN_CT_SdtPr_tag) + if (nSprmId == NS_ooxml::LN_CT_SdtPr_alias || nSprmId == NS_ooxml::LN_CT_SdtPr_tag + || nSprmId == NS_ooxml::LN_CT_SdtPr_lock) { + // Grabbag string values beans::PropertyValue aValue; aValue.Name = sName; aValue.Value <<= sStringValue; diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 064bb5b15da6..3f3906570c61 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -957,6 +957,11 @@ void DomainMapper_Impl::PopSdt() xContentControlProps->setPropertyValue("Id", uno::Any(m_pSdtHelper->GetId())); } + if (!m_pSdtHelper->GetLock().isEmpty()) + { + xContentControlProps->setPropertyValue("Lock", uno::Any(m_pSdtHelper->GetLock())); + } + if (m_pSdtHelper->getControlType() == SdtControlType::checkBox) { xContentControlProps->setPropertyValue("Checkbox", uno::Any(true)); diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx index a6a6b6cb08f8..81b03df774cd 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -521,6 +521,7 @@ void SdtHelper::clear() m_aAlias.clear(); m_aTag.clear(); m_nId = 0; + m_aLock.clear(); } void SdtHelper::SetPlaceholderDocPart(const OUString& rPlaceholderDocPart) @@ -546,6 +547,10 @@ void SdtHelper::SetId(sal_Int32 nId) { m_nId = nId; } sal_Int32 SdtHelper::GetId() const { return m_nId; } +void SdtHelper::SetLock(const OUString& rLock) { m_aLock = rLock; } + +const OUString& SdtHelper::GetLock() const { return m_aLock; } + } // namespace writerfilter::dmapper /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx index 736476e63c2f..dbe0b2659e7a 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -135,6 +135,9 @@ class SdtHelper final : public virtual SvRefBase /// <w:sdtPr>'s <w:id w:val="...">. sal_Int32 m_nId = 0; + /// <w:sdtPr>'s <w:lock w:val="...">. + OUString m_aLock; + public: explicit SdtHelper(DomainMapper_Impl& rDM_Impl, css::uno::Reference<css::uno::XComponentContext> xContext); @@ -223,6 +226,9 @@ public: void SetId(sal_Int32 nId); sal_Int32 GetId() const; + void SetLock(const OUString& rLock); + const OUString& GetLock() const; + std::optional<OUString> getValueFromDataBinding(); }; diff --git a/xmloff/qa/unit/data/content-control-alias.fodt b/xmloff/qa/unit/data/content-control-alias.fodt index 8307f682e474..8f541bef42cc 100644 --- a/xmloff/qa/unit/data/content-control-alias.fodt +++ b/xmloff/qa/unit/data/content-control-alias.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:alias="my alias" loext:tag="my tag">test</loext:content-control></text:p> + <text:p><loext:content-control loext:alias="my alias" loext:tag="my tag" loext:lock="sdtContentLocked">test</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 45f3323d92a5..2187e3d2a89d 100644 --- a/xmloff/qa/unit/text.cxx +++ b/xmloff/qa/unit/text.cxx @@ -781,6 +781,7 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testAliasContentControlExport) uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); xContentControlProps->setPropertyValue("Alias", uno::Any(OUString("my alias"))); xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("my tag"))); + xContentControlProps->setPropertyValue("Lock", uno::Any(OUString("unlocked"))); xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); // When exporting to ODT: @@ -794,6 +795,7 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testAliasContentControlExport) // i.e. alias was lost on export. assertXPath(pXmlDoc, "//loext:content-control", "alias", "my alias"); assertXPath(pXmlDoc, "//loext:content-control", "tag", "my tag"); + assertXPath(pXmlDoc, "//loext:content-control", "lock", "unlocked"); } CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testComboBoxContentControlImport) @@ -853,6 +855,9 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testAliasContentControlImport) OUString aTag; xContentControlProps->getPropertyValue("Tag") >>= aTag; CPPUNIT_ASSERT_EQUAL(OUString("my tag"), aTag); + OUString aLock; + xContentControlProps->getPropertyValue("Lock") >>= aLock; + CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), aLock); } CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testDropdownContentControlAutostyleExport) diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 1dc56ced1114..3eb529ea1517 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -1234,6 +1234,7 @@ namespace xmloff::token { TOKEN( "list-style", XML_LIST_STYLE ), TOKEN( "list-style-name", XML_LIST_STYLE_NAME ), TOKEN( "ln", XML_LN ), + TOKEN( "lock", XML_LOCK ), TOKEN( "locked", XML_LOCKED ), TOKEN( "log", XML_LOG ), TOKEN( "logarithmic", XML_LOGARITHMIC ), diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index 8c0d53e8da93..b2117dfdc726 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -4066,6 +4066,13 @@ void XMLTextParagraphExport::ExportContentControl( { GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_TAG, aTag); } + + OUString aLock; + xPropertySet->getPropertyValue("Lock") >>= aLock; + if (!aLock.isEmpty()) + { + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_LOCK, aLock); + } } 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 c069a6eba9c1..83c1621b057e 100644 --- a/xmloff/source/text/xmlcontentcontrolcontext.cxx +++ b/xmloff/source/text/xmlcontentcontrolcontext.cxx @@ -151,6 +151,11 @@ void XMLContentControlContext::startFastElement( m_aTag = rIter.toString(); break; } + case XML_ELEMENT(LO_EXT, XML_LOCK): + { + m_aLock = rIter.toString(); + break; + } default: XMLOFF_WARN_UNKNOWN("xmloff", rIter); } @@ -261,6 +266,11 @@ void XMLContentControlContext::endFastElement(sal_Int32) { xPropertySet->setPropertyValue("Tag", uno::Any(m_aTag)); } + + if (!m_aLock.isEmpty()) + { + xPropertySet->setPropertyValue("Lock", uno::Any(m_aLock)); + } } css::uno::Reference<css::xml::sax::XFastContextHandler> diff --git a/xmloff/source/text/xmlcontentcontrolcontext.hxx b/xmloff/source/text/xmlcontentcontrolcontext.hxx index 936fc03c781b..f0b1eea0b010 100644 --- a/xmloff/source/text/xmlcontentcontrolcontext.hxx +++ b/xmloff/source/text/xmlcontentcontrolcontext.hxx @@ -53,6 +53,7 @@ class XMLContentControlContext : public SvXMLImportContext bool m_bDropDown = false; OUString m_aAlias; OUString m_aTag; + OUString m_aLock; public: XMLContentControlContext(SvXMLImport& rImport, sal_Int32 nElement, XMLHints_Impl& rHints, diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 5fef52535c63..2fb710f1c323 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -1134,6 +1134,7 @@ list-name list-style list-style-name ln +lock locked log logarithmic |