diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-11-08 16:45:00 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-11-09 13:09:01 +0100 |
commit | 6b419811d46966e556e05024d240d050947ebb81 (patch) | |
tree | a3b5078e257847f3954124242475448eba0c7bb1 /sw | |
parent | e33d8fd1ff466876e5e95c8495c73a08c454d8c9 (diff) |
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.
(cherry picked from commit a8448ded5555947925b0e9ddb4aeea7043f03933)
Change-Id: I26240c9ddbd06f4f57f5f65460ef75a2ace94825
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142477
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Diffstat (limited to 'sw')
-rw-r--r-- | sw/inc/textcontentcontrol.hxx | 3 | ||||
-rw-r--r-- | sw/inc/unotxdoc.hxx | 6 | ||||
-rw-r--r-- | sw/qa/core/unocore/unocore.cxx | 50 | ||||
-rw-r--r-- | sw/source/core/inc/unocontentcontrol.hxx | 24 | ||||
-rw-r--r-- | sw/source/core/txtnode/attrcontentcontrol.cxx | 19 | ||||
-rw-r--r-- | sw/source/core/unocore/unocontentcontrol.cxx | 70 | ||||
-rw-r--r-- | sw/source/uibase/uno/unotxdoc.cxx | 20 |
7 files changed, 192 insertions, 0 deletions
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 dc022d34ccc7..4e4d3f160eec 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; @@ -265,6 +268,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 18286efe9ed9..20d93d688f96 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -706,6 +706,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 631ccdaf02d3..90d408a7964d 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; /** @@ -155,4 +157,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 418f35878f85..263b64a2da8f 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -598,6 +598,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 0e19b7e57f41..bae19e9b6edd 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> @@ -1324,4 +1325,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(); + uno::Reference<css::text::XTextContent> xContentControl + = SwXContentControl::CreateXContentControl(*rFormatContentControl.GetContentControl()); + uno::Any aRet; + aRet <<= 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 a233745ae7fd..f3f1911d3ee2 100644 --- a/sw/source/uibase/uno/unotxdoc.cxx +++ b/sw/source/uibase/uno/unotxdoc.cxx @@ -167,6 +167,7 @@ #include <IDocumentOutlineNodes.hxx> #include <SearchResultLocator.hxx> #include <textcontentcontrol.hxx> +#include <unocontentcontrol.hxx> using namespace ::com::sun::star; using namespace ::com::sun::star::text; @@ -660,6 +661,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() { SolarMutexGuard aGuard; @@ -1489,6 +1502,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(); |