offapi/UnoApi_offapi.mk | 2 offapi/com/sun/star/text/ContentControls.idl | 39 ++++++++++ offapi/com/sun/star/text/GenericTextDocument.idl | 3 offapi/com/sun/star/text/XContentControlsSupplier.idl | 36 +++++++++ sw/inc/textcontentcontrol.hxx | 3 sw/inc/unotxdoc.hxx | 6 + sw/qa/core/unocore/unocore.cxx | 50 ++++++++++++ sw/source/core/inc/unocontentcontrol.hxx | 24 ++++++ sw/source/core/txtnode/attrcontentcontrol.cxx | 19 ++++ sw/source/core/unocore/unocontentcontrol.cxx | 70 ++++++++++++++++++ sw/source/uibase/uno/unotxdoc.cxx | 20 +++++ 11 files changed, 272 insertions(+)
New commits: commit a8448ded5555947925b0e9ddb4aeea7043f03933 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Nov 8 16:45:00 2022 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Nov 8 18:09:15 2022 +0100 sw: introduce an UNO manager for content controls This builds on top of commit ad950f10dc382ea169f94a0c301ca8c424e7103e (sw: introduce a manager for content controls, 2022-11-08) and exposes it on the UNO API: - add a new css.text.ContentControls service, backed by SwContentControlManager - add a new css.text.XContentControlsSupplier interface, implemented by SwXTextDocument - implement XIndexAccess in ContentControls This allows UNO (and later VBA) clients to have random access to the content controls in a document, which is much easier than the relatively complex traversal of the whole doc model. Change-Id: I26240c9ddbd06f4f57f5f65460ef75a2ace94825 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142454 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 331e5eadaf5f..550422ded680 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -1344,6 +1344,7 @@ $(eval $(call gb_UnoApi_add_idlfiles_noheader,offapi,com/sun/star/text,\ ChainedTextFrame \ ChapterNumberingRule \ ContentControl \ + ContentControls \ ContentIndex \ ContentIndexMark \ Defaults \ @@ -3787,6 +3788,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/text,\ XAutoTextGroup \ XBookmarkInsertTool \ XBookmarksSupplier \ + XContentControlsSupplier \ XChapterNumberingSupplier \ XDefaultNumberingProvider \ XDependentTextField \ diff --git a/offapi/com/sun/star/text/ContentControls.idl b/offapi/com/sun/star/text/ContentControls.idl new file mode 100644 index 000000000000..d544b4ee767f --- /dev/null +++ b/offapi/com/sun/star/text/ContentControls.idl @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + module com { module sun { module star { module text { + +/** provides access to the content controls of a (text) + document. + + @since LibreOffice 7.5 + */ +service ContentControls +{ + + /** provides access to the content controls of the document. + */ + interface com::sun::star::container::XIndexAccess; + +}; + + +}; }; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/offapi/com/sun/star/text/GenericTextDocument.idl b/offapi/com/sun/star/text/GenericTextDocument.idl index 708e913353bc..6823df32f4e0 100644 --- a/offapi/com/sun/star/text/GenericTextDocument.idl +++ b/offapi/com/sun/star/text/GenericTextDocument.idl @@ -58,6 +58,9 @@ published service GenericTextDocument [optional] interface com::sun::star::text::XEndnotesSupplier; + /** @since LibreOffice 7.5 */ + [optional] interface com::sun::star::text::XContentControlsSupplier; + [optional] interface com::sun::star::util::XReplaceable; [optional] interface com::sun::star::text::XPagePrintable; diff --git a/offapi/com/sun/star/text/XContentControlsSupplier.idl b/offapi/com/sun/star/text/XContentControlsSupplier.idl new file mode 100644 index 000000000000..646e380cc12e --- /dev/null +++ b/offapi/com/sun/star/text/XContentControlsSupplier.idl @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +module com { module sun { module star { module text { + +/** makes it possible to access the content controls within + the context (e.g. document). + + @since LibreOffice 7.5 + */ +interface XContentControlsSupplier: com::sun::star::uno::XInterface +{ + /** returns a collection of content controls. + */ + com::sun::star::container::XIndexAccess getContentControls(); +}; + +}; }; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/textcontentcontrol.hxx b/sw/inc/textcontentcontrol.hxx index dc7d210f79a6..78a4d5120b1b 100644 --- a/sw/inc/textcontentcontrol.hxx +++ b/sw/inc/textcontentcontrol.hxx @@ -56,6 +56,9 @@ public: SwContentControlManager(); void Insert(SwTextContentControl* pTextContentControl); void Erase(SwTextContentControl* pTextContentControl); + size_t GetCount() const { return m_aContentControls.size(); } + bool IsEmpty() const { return m_aContentControls.empty(); } + SwTextContentControl* Get(size_t nIndex); void dumpAsXml(xmlTextWriterPtr pWriter) const; }; diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx index a69a678a6cf1..90f605406d5b 100644 --- a/sw/inc/unotxdoc.hxx +++ b/sw/inc/unotxdoc.hxx @@ -29,6 +29,7 @@ #include <com/sun/star/text/XNumberingRulesSupplier.hpp> #include <com/sun/star/text/XFootnotesSupplier.hpp> #include <com/sun/star/text/XEndnotesSupplier.hpp> +#include <com/sun/star/text/XContentControlsSupplier.hpp> #include <com/sun/star/text/XTextSectionsSupplier.hpp> #include <com/sun/star/text/XLineNumberingProperties.hpp> #include <com/sun/star/text/XChapterNumberingSupplier.hpp> @@ -99,6 +100,7 @@ typedef cppu::ImplInheritanceHelper css::text::XNumberingRulesSupplier, css::text::XFootnotesSupplier, css::text::XEndnotesSupplier, + css::text::XContentControlsSupplier, css::util::XReplaceable, css::text::XPagePrintable, css::text::XReferenceMarksSupplier, @@ -156,6 +158,7 @@ private: css::uno::Reference< css::beans::XPropertySet > mxXFootnoteSettings; css::uno::Reference< css::container::XIndexAccess > mxXEndnotes; css::uno::Reference< css::beans::XPropertySet > mxXEndnoteSettings; + css::uno::Reference< css::container::XIndexAccess > mxXContentControls; css::uno::Reference< css::container::XNameAccess > mxXReferenceMarks; css::uno::Reference< css::container::XEnumerationAccess > mxXTextFieldTypes; css::uno::Reference< css::container::XNameAccess > mxXTextFieldMasters; @@ -266,6 +269,9 @@ public: virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getEndnotes() override; virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL getEndnoteSettings() override; + // XContentControlsSupplier + css::uno::Reference<css::container::XIndexAccess> SAL_CALL getContentControls() override; + //XReplaceable virtual css::uno::Reference< css::util::XReplaceDescriptor > SAL_CALL createReplaceDescriptor() override; virtual sal_Int32 SAL_CALL replaceAll(const css::uno::Reference< css::util::XSearchDescriptor > & xDesc) override; diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index fdab71600312..b989a588b09e 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -798,6 +798,56 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlComboBox) CPPUNIT_ASSERT(pContentControl->GetComboBox()); } +CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControls) +{ + // Given an empty document: + createSwDoc(); + auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + uno::Reference<container::XIndexAccess> xContentControls = pXTextDocument->getContentControls(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), xContentControls->getCount()); + + // When inserting content controls: + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + // First tag1. + xText->insertString(xCursor, "test1", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + { + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("tag1"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + } + xCursor->gotoStart(/*bExpand=*/false); + // Then tag2 before tag1. + xText->insertString(xCursor, "test2", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->goRight(5, /*bExpand=*/true); + { + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("tag2"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + } + + // Then make sure that XContentControls contains the items in a correct order: + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xContentControls->getCount()); + uno::Reference<beans::XPropertySet> xContentControl; + xContentControls->getByIndex(0) >>= xContentControl; + OUString aTag; + xContentControl->getPropertyValue("Tag") >>= aTag; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: tag2 + // - Actual : tag1 + // i.e. the order of the items was sorted by insert time, not by their doc model position. + CPPUNIT_ASSERT_EQUAL(OUString("tag2"), aTag); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/inc/unocontentcontrol.hxx b/sw/source/core/inc/unocontentcontrol.hxx index 3ba4b8e3d2b3..c77aad1d9853 100644 --- a/sw/source/core/inc/unocontentcontrol.hxx +++ b/sw/source/core/inc/unocontentcontrol.hxx @@ -34,11 +34,13 @@ #include <cppuhelper/implbase.hxx> #include <unobaseclass.hxx> +#include <unocoll.hxx> typedef std::deque<css::uno::Reference<css::text::XTextRange>> TextRangeList_t; class SwPaM; class SwTextNode; +class SwFormatContentControl; class SwContentControl; /** @@ -153,4 +155,26 @@ public: const css::uno::Reference<css::beans::XVetoableChangeListener>& xListener) override; }; +/// UNO wrapper around SwContentControlManager. +class SwXContentControls final : public SwSimpleIndexAccessBaseClass, public SwUnoCollection +{ + ~SwXContentControls() override; + +public: + SwXContentControls(SwDoc* pDoc); + + // XIndexAccess + sal_Int32 SAL_CALL getCount() override; + css::uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override; + + // XElementAccess + css::uno::Type SAL_CALL getElementType() override; + sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override; + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; +}; + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index b1e4c07cfdfb..3807ee9f2dba 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -604,6 +604,25 @@ void SwContentControlManager::Erase(SwTextContentControl* pTextContentControl) m_aContentControls.end()); } +SwTextContentControl* SwContentControlManager::Get(size_t nIndex) +{ + // Only sort now: the items may not have an associated text node by the time they are inserted + // into the container. + std::sort(m_aContentControls.begin(), m_aContentControls.end(), + [](SwTextContentControl*& pLhs, SwTextContentControl*& pRhs) -> bool { + SwNodeOffset nIdxLHS = pLhs->GetTextNode()->GetIndex(); + SwNodeOffset nIdxRHS = pRhs->GetTextNode()->GetIndex(); + if (nIdxLHS == nIdxRHS) + { + return pLhs->GetStart() < pRhs->GetStart(); + } + + return nIdxLHS < nIdxRHS; + }); + + return m_aContentControls[nIndex]; +} + void SwContentControlManager::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlManager")); diff --git a/sw/source/core/unocore/unocontentcontrol.cxx b/sw/source/core/unocore/unocontentcontrol.cxx index 189cdb9ba316..62e93d33d68e 100644 --- a/sw/source/core/unocore/unocontentcontrol.cxx +++ b/sw/source/core/unocore/unocontentcontrol.cxx @@ -22,6 +22,7 @@ #include <mutex> #include <com/sun/star/text/XWordCursor.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> #include <comphelper/interfacecontainer4.hxx> #include <comphelper/servicehelper.hxx> @@ -1261,4 +1262,73 @@ uno::Reference<container::XEnumeration> SAL_CALL SwXContentControl::createEnumer } } +SwXContentControls::SwXContentControls(SwDoc* pDoc) + : SwUnoCollection(pDoc) +{ +} + +SwXContentControls::~SwXContentControls() {} + +sal_Int32 SwXContentControls::getCount() +{ + SolarMutexGuard aGuard; + + if (!IsValid()) + { + throw uno::RuntimeException(); + } + + return GetDoc()->GetContentControlManager().GetCount(); +} + +uno::Any SwXContentControls::getByIndex(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + + if (!IsValid()) + { + throw uno::RuntimeException(); + } + + SwContentControlManager& rManager = GetDoc()->GetContentControlManager(); + if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= rManager.GetCount()) + { + throw lang::IndexOutOfBoundsException(); + } + + SwTextContentControl* pTextContentControl = rManager.Get(nIndex); + const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl(); + rtl::Reference<SwXContentControl> xContentControl + = SwXContentControl::CreateXContentControl(*rFormatContentControl.GetContentControl()); + uno::Any aRet; + aRet <<= uno::Reference<text::XTextContent>(xContentControl); + return aRet; +} + +uno::Type SwXContentControls::getElementType() { return cppu::UnoType<text::XTextContent>::get(); } + +sal_Bool SwXContentControls::hasElements() +{ + SolarMutexGuard aGuard; + + if (!IsValid()) + { + throw uno::RuntimeException(); + } + + return !GetDoc()->GetContentControlManager().IsEmpty(); +} + +OUString SwXContentControls::getImplementationName() { return "SwXContentControls"; } + +sal_Bool SwXContentControls::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence<OUString> SwXContentControls::getSupportedServiceNames() +{ + return { "com.sun.star.text.ContentControls" }; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx index 6b49898d7c1b..e74c490f2098 100644 --- a/sw/source/uibase/uno/unotxdoc.cxx +++ b/sw/source/uibase/uno/unotxdoc.cxx @@ -166,6 +166,7 @@ #include <IDocumentOutlineNodes.hxx> #include <SearchResultLocator.hxx> #include <textcontentcontrol.hxx> +#include <unocontentcontrol.hxx> using namespace ::com::sun::star; using namespace ::com::sun::star::text; @@ -659,6 +660,18 @@ Reference< XPropertySet > SwXTextDocument::getEndnoteSettings() return mxXEndnoteSettings; } +Reference< XIndexAccess > SwXTextDocument::getContentControls() +{ + SolarMutexGuard aGuard; + if(!IsValid()) + throw DisposedException("", static_cast< XTextDocument* >(this)); + if(!mxXContentControls.is()) + { + mxXContentControls = new SwXContentControls(m_pDocShell->GetDoc()); + } + return mxXContentControls; +} + Reference< util::XReplaceDescriptor > SwXTextDocument::createReplaceDescriptor() { return new SwXTextSearch; @@ -1482,6 +1495,13 @@ void SwXTextDocument::InitNewDoc() mxXEndnotes.clear(); } + if(mxXContentControls.is()) + { + XIndexAccess* pContentControls = mxXContentControls.get(); + static_cast<SwXContentControls*>(pContentControls)->Invalidate(); + mxXContentControls.clear(); + } + if(mxXDocumentIndexes.is()) { XIndexAccess* pIdxs = mxXDocumentIndexes.get();