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();

Reply via email to