offapi/com/sun/star/text/ContentControl.idl                      |    8 
 oox/source/token/tokens.txt                                      |    1 
 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng      |   10 
 sw/inc/formatcontentcontrol.hxx                                  |    7 
 sw/inc/unoprnms.hxx                                              |    1 
 sw/qa/core/unocore/unocore.cxx                                   |    4 
 sw/qa/extras/ooxmlexport/ooxmlexport17.cxx                       |    4 
 sw/qa/extras/ooxmlexport/ooxmlexport18.cxx                       |    6 
 sw/source/core/txtnode/attrcontentcontrol.cxx                    |    2 
 sw/source/core/unocore/unocontentcontrol.cxx                     |   29 +
 sw/source/core/unocore/unomap1.cxx                               |    1 
 sw/source/filter/ww8/docxattributeoutput.cxx                     |   35 -
 sw/source/filter/ww8/docxattributeoutput.hxx                     |    6 
 sw/source/ui/vba/vbacontentcontrol.cxx                           |  278 
++++------
 sw/source/ui/vba/vbacontentcontrol.hxx                           |    4 
 sw/source/ui/vba/vbacontentcontrollistentries.cxx                |   38 -
 sw/source/ui/vba/vbacontentcontrollistentries.hxx                |    4 
 sw/source/ui/vba/vbacontentcontrollistentry.cxx                  |   80 +-
 sw/source/ui/vba/vbacontentcontrollistentry.hxx                  |    4 
 sw/source/ui/vba/vbacontentcontrols.cxx                          |   35 -
 writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx               |    8 
 writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx |binary
 writerfilter/source/dmapper/DomainMapper.cxx                     |   17 
 writerfilter/source/dmapper/DomainMapper_Impl.cxx                |    5 
 writerfilter/source/dmapper/SdtHelper.cxx                        |    5 
 writerfilter/source/dmapper/SdtHelper.hxx                        |    6 
 writerfilter/source/ooxml/model.xml                              |    4 
 xmloff/qa/unit/data/content-control-alias.fodt                   |    2 
 xmloff/qa/unit/text.cxx                                          |   10 
 xmloff/source/text/txtparae.cxx                                  |   15 
 xmloff/source/text/xmlcontentcontrolcontext.cxx                  |   27 
 xmloff/source/text/xmlcontentcontrolcontext.hxx                  |    2 
 32 files changed, 400 insertions(+), 258 deletions(-)

New commits:
commit c68e01ad578baa16ee108137f723a7126163c445
Author:     Justin Luth <[email protected]>
AuthorDate: Wed Nov 16 21:56:51 2022 -0500
Commit:     Andras Timar <[email protected]>
CommitDate: Sat Jan 28 18:47:18 2023 +0100

    sw content controls: enhance preserve id
    
    Follow-up to 100c914d44ae8f362924fe567d7d41d0033ae8ad
    which added the initial id preservation for DOCX.
    
    adding DOCX block SDT grabbaging, ODF import/export
    [content controls can't exist in DOC format]
    
    The ID field is CRITICAL to preserve for VBA purposes.
    This patch adjusts BlockSDT to also round-trip the id
    instead of just creating a random one.
    
    m_nRunSdtPrToken <never equals> FSNS(XML_w, XML_id) since 2021
    with 5f3af56b2c0ef6c628a7cfe5ce6e86f8e1765f5f,
    so I removed that part of the clause.
    
    I had thought about changing the ID to use a string instead of an int,
    but then the integer version was adopted to fix a regression
    in the commit mentioned earlier.
    
    I think it is AWFUL to have a number as the identifier
    when it will be used in StarBASIC. The VBA guys have to deal
    with it, but it would be nice to do something reasonable
    for LO native access to content controls.
    
    However, the concern would be if we allow VBA macro content created in LO
    to be exported to DOCX format - that would cause problems converting
    from a string ID to a number ID. VBA editing already is happening to
    some extent, and mmeeks seems interested in enabling it.
    So over-all it seems best to just stick with an integer ID.
    
    I used the commits for <w:alias> and <w:tag> to compose this patch.
    XML_ID already existed in include/xmloff/xmltoken.hxx
    and "id" already exists in xmloff/source/token/tokens.txt
    
    The ID can be used in VBA to select a specific control.
    The id (which is a positive or negative integer in DOCX)
    specifies a unique control - either by passing the number as a string
    (of the UNSIGNED value) or by passing as a float (specified with #).
    
    For example:
    msgbox ActiveDocument.ContentControls(2587720202#).ID
    msgbox ActiveDocument.ContentControls("2587720202").ID
    but not as an integer since that is used for index access.
    dim index as integer
    index = 1
    msgbox ActiveDocument.ContentControls(index).ID
    
    make CppunitTest_writerfilter_dmapper CPPUNIT_TEST_NAME=testSdtRunRichText
    make CppunitTest_sw_ooxmlexport17 
CPPUNIT_TEST_NAME=testDateContentControlExport
    make CppunitTest_sw_ooxmlexport18 CPPUNIT_TEST_NAME=testTdf151912
    make CppunitTest_sw_core_unocore CPPUNIT_TEST_NAME=testContentControlDate
    make CppunitTest_xmloff_text CPPUNIT_TEST_NAME=testAliasContentControlExport
    make CppunitTest_xmloff_text CPPUNIT_TEST_NAME=testAliasContentControlImport
    
    Change-Id: I5c4022dc932d941fad9da6d75ce899ee1ff66ff5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142818
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/offapi/com/sun/star/text/ContentControl.idl 
b/offapi/com/sun/star/text/ContentControl.idl
index 3b6e86b1ac1c..c2fe46757656 100644
--- a/offapi/com/sun/star/text/ContentControl.idl
+++ b/offapi/com/sun/star/text/ContentControl.idl
@@ -111,12 +111,14 @@ service ContentControl
     [optional, property] boolean DropDown;
 
     /** The alias: kind of a human-readable title / description, show up on 
the UI.
+                   -also used by VBA to group controls into a smaller, indexed 
collection
 
         @since LibreOffice 7.5
     */
     [optional, property] string Alias;
 
     /** The tag: similar to Alias, but is meant to be machine-readable.
+                 -also used by VBA to group controls into a smaller, indexed 
collection
 
         @since LibreOffice 7.5
     */
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index e326b8913646..0a8aadad1ba3 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2976,6 +2976,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:id">
+          <rng:ref name="integer"/>
+        </rng:attribute>
+      </rng:optional>
       <rng:optional>
         <rng:attribute name="loext:tab-index">
           <rng:ref name="nonNegativeInteger"/>
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index af31044e4190..0c2da1b0524a 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("Id", 
uno::Any(static_cast<sal_Int32>(123)));
     xContentControlProps->setPropertyValue("TabIndex", 
uno::Any(sal_uInt32(1)));
     xContentControlProps->setPropertyValue("Lock", 
uno::Any(OUString("sdtContentLocked")));
     xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
@@ -660,6 +661,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(static_cast<sal_Int32>(123), 
pContentControl->GetId());
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(1), 
pContentControl->GetTabIndex());
     CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), 
pContentControl->GetLock());
 }
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 04795920a0db..a04a7813f9c6 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -425,6 +425,7 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     xContentControlProps->setPropertyValue("Appearance", 
uno::Any(OUString("hidden")));
     xContentControlProps->setPropertyValue("Alias", 
uno::Any(OUString("myalias")));
     xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("mytag")));
+    xContentControlProps->setPropertyValue("Id", 
uno::Any(static_cast<sal_Int32>(123)));
     xContentControlProps->setPropertyValue("TabIndex", 
uno::Any(sal_uInt32(2)));
     xContentControlProps->setPropertyValue("Lock", 
uno::Any(OUString("sdtLocked")));
 
@@ -451,6 +452,7 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w15:appearance", "val", "hidden");
     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:id", "val", "123");
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:tabIndex", "val", "2");
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:lock", "val", "sdtLocked");
 }
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index b495e62902fa..e27a1f085b11 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -106,6 +106,12 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf149551_mongolianVert)
 DECLARE_OOXMLEXPORT_TEST(testTdf151912, "tdf151912.docx")
 {
     // For now just ensure roundtrip is successful
+
+    //tdf#151548 - ensure block SDT preserves id (instead of random 
re-assignment)
+    if (!isExported())
+        return;
+    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+    assertXPath(pXmlDoc, "//w:sdt//w:sdtPr/w:id", "val", "1802566103");
 }
 
 DECLARE_OOXMLEXPORT_TEST(testTdf147724, "tdf147724.docx")
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 88c3cb8a2d24..03b73dbaee84 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -626,13 +626,13 @@ void SdtBlockHelper::DeleteAndResetTheLists()
     if (!m_aAppearance.isEmpty())
         m_aAppearance.clear();
     m_bShowingPlaceHolder = false;
+    m_nId = 0;
     m_nTabIndex = 0;
-    m_bHasId = false;
 }
 
 void SdtBlockHelper::WriteSdtBlock(const ::sax_fastparser::FSHelperPtr& 
pSerializer, bool bRunTextIsOn, bool bParagraphHasDrawing)
 {
-    if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_bHasId)
+    if (m_nSdtPrToken <= 0 && !m_pDataBindingAttrs.is() && !m_nId)
         return;
 
     // sdt start mark
@@ -692,10 +692,10 @@ void SdtBlockHelper::WriteSdtBlock(const 
::sax_fastparser::FSHelperPtr& pSeriali
 
 void SdtBlockHelper::WriteExtraParams(const ::sax_fastparser::FSHelperPtr& 
pSerializer)
 {
-    if (m_nSdtPrToken == FSNS(XML_w, XML_id) || m_bHasId)
-        //Word won't open a document with an empty id tag, we fill it with a 
random number
-        pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
-            OString::number(comphelper::rng::uniform_int_distribution(0, 
std::numeric_limits<int>::max())));
+    if (m_nId)
+    {
+        pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val), 
OString::number(m_nId));
+    }
 
     if (m_pDataBindingAttrs.is())
     {
@@ -860,6 +860,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_id")
+        {
+            if (!(aPropertyValue.Value >>= m_nId))
+                SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected 
sdt id value");
+        }
         else if (aPropertyValue.Name == "ooxml:CT_SdtPr_tabIndex" && 
!m_nTabIndex)
         {
             if (!(aPropertyValue.Value >>= m_nTabIndex))
@@ -870,8 +875,6 @@ void SdtBlockHelper::GetSdtParamsFromGrabBag(const 
uno::Sequence<beans::Property
             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")
             m_nSdtPrToken = FSNS(XML_w, XML_citation);
         else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index c4f152b85e5c..989a76268e81 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -128,14 +128,14 @@ class SdtBlockHelper
 {
 public:
     SdtBlockHelper()
-        : m_bHasId(false)
+        : m_nId(0)
         , m_bStartedSdt(false)
         , m_bShowingPlaceHolder(false)
         , m_nTabIndex(0)
         , m_nSdtPrToken(0)
     {}
 
-    bool m_bHasId;
+    sal_Int32 m_nId;
     bool m_bStartedSdt;
     rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenChildren;
     rtl::Reference<sax_fastparser::FastAttributeList> m_pTokenAttributes;
diff --git a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx 
b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx
index 7b27ff66d08c..fc99cd2e3416 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);
+    sal_Int32 nId = 0;
+    xContentControlProps->getPropertyValue("Id") >>= nId;
+    // This was 0.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2147483647), nId);
     sal_uInt32 nTabIndex = 0;
     xContentControlProps->getPropertyValue("TabIndex") >>= nTabIndex;
     // This was 0
diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx 
b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx
index ca980abb0356..d5644f33e9d9 100644
Binary files a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx 
and b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx differ
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index ab7cfeb401f0..61b4d6d114d4 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -3023,13 +3023,15 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const 
PropertyMapPtr& rContext )
         }
         else if (nSprmId == NS_ooxml::LN_CT_SdtPr_showingPlcHdr)
         {
+            // Grabbag boolean values
             beans::PropertyValue aValue;
             aValue.Name = sName;
             aValue.Value <<= bool(nIntValue);
             m_pImpl->m_pSdtHelper->appendToInteropGrabBag(aValue);
         }
-        else if (nSprmId == NS_ooxml::LN_CT_SdtPr_tabIndex)
+        else if (nSprmId == NS_ooxml::LN_CT_SdtPr_id || nSprmId == 
NS_ooxml::LN_CT_SdtPr_tabIndex)
         {
+            // Grabbag integer values
             beans::PropertyValue aValue;
             aValue.Name = sName;
             aValue.Value <<= nIntValue;
diff --git a/xmloff/qa/unit/data/content-control-alias.fodt 
b/xmloff/qa/unit/data/content-control-alias.fodt
index 48fdc7b9436c..0742610ca63a 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" 
loext:tab-index="4" 
loext:lock="sdtContentLocked">test</loext:content-control></text:p>
+      <text:p><loext:content-control loext:alias="my alias" loext:tag="my tag" 
loext:id="2147483647" loext:tab-index="4" 
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 9ae9e5cc1d22..f7155f546df3 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("Id", 
uno::Any(static_cast<sal_Int32>(-2147483648)));
     xContentControlProps->setPropertyValue("TabIndex", 
uno::Any(sal_uInt32(3)));
     xContentControlProps->setPropertyValue("Lock", 
uno::Any(OUString("unlocked")));
     xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
@@ -796,6 +797,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", "id", "-2147483648");
     assertXPath(pXmlDoc, "//loext:content-control", "tab-index", "3");
     assertXPath(pXmlDoc, "//loext:content-control", "lock", "unlocked");
 }
@@ -857,6 +859,9 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testAliasContentControlImport)
     OUString aTag;
     xContentControlProps->getPropertyValue("Tag") >>= aTag;
     CPPUNIT_ASSERT_EQUAL(OUString("my tag"), aTag);
+    sal_Int32 nId = 0;
+    xContentControlProps->getPropertyValue("Id") >>= nId;
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2147483647), nId);
     sal_uInt32 nTabIndex;
     xContentControlProps->getPropertyValue("TabIndex") >>= nTabIndex;
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(4), nTabIndex);
diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx
index 2c922a6242af..5f874c50c4cf 100644
--- a/xmloff/source/text/txtparae.cxx
+++ b/xmloff/source/text/txtparae.cxx
@@ -4072,6 +4072,13 @@ void XMLTextParagraphExport::ExportContentControl(
             GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_TAG, aTag);
         }
 
+        sal_Int32 nId = 0;
+        xPropertySet->getPropertyValue("Id") >>= nId;
+        if (nId)
+        {
+            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_ID, 
OUString::number(nId));
+        }
+
         sal_uInt32 nTabIndex;
         xPropertySet->getPropertyValue("TabIndex") >>= nTabIndex;
         if (nTabIndex)
diff --git a/xmloff/source/text/xmlcontentcontrolcontext.cxx 
b/xmloff/source/text/xmlcontentcontrolcontext.cxx
index caf1af04b8d7..2a7ef5b2eebe 100644
--- a/xmloff/source/text/xmlcontentcontrolcontext.cxx
+++ b/xmloff/source/text/xmlcontentcontrolcontext.cxx
@@ -152,6 +152,14 @@ void XMLContentControlContext::startFastElement(
                 m_aTag = rIter.toString();
                 break;
             }
+            case XML_ELEMENT(LO_EXT, XML_ID):
+            {
+                if (sax::Converter::convertNumber(nTmp, rIter.toView()))
+                {
+                    m_nId = nTmp;
+                }
+                break;
+            }
             case XML_ELEMENT(LO_EXT, XML_TAB_INDEX):
             {
                 if (sax::Converter::convertNumber(nTmp, rIter.toView()))
@@ -276,6 +284,11 @@ void XMLContentControlContext::endFastElement(sal_Int32)
         xPropertySet->setPropertyValue("Tag", uno::Any(m_aTag));
     }
 
+    if (m_nId)
+    {
+        xPropertySet->setPropertyValue("Id", uno::Any(m_nId));
+    }
+
     if (m_nTabIndex)
     {
         xPropertySet->setPropertyValue("TabIndex", uno::Any(m_nTabIndex));
diff --git a/xmloff/source/text/xmlcontentcontrolcontext.hxx 
b/xmloff/source/text/xmlcontentcontrolcontext.hxx
index 44abe71d6a08..13c1e50f23fa 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;
+    sal_Int32 m_nId = 0;
     sal_uInt32 m_nTabIndex = 0;
     OUString m_aLock;
 
commit d213cdc382744b78b659f02c1e905160376e0b41
Author:     Justin Luth <[email protected]>
AuthorDate: Fri Dec 2 10:48:46 2022 -0500
Commit:     Andras Timar <[email protected]>
CommitDate: Sat Jan 28 18:46:36 2023 +0100

    tdf#151548 sw content controls: preserve tabIndex
    
    This has to be vital to keyboard navigation.
    Certainly it is good to have it imported
    before we start to consider tab-movements
    for form controls.
    
    All tabIndex 1's are processed (in placement order)
    and then the 2's etc. 0's are to be done last.
    
    XML_TAB_INDEX already existed in include/xmloff/xmltoken.hxx
    and "tab-index" already exists in xmloff/source/token/tokens.txt
    
    make CppunitTest_writerfilter_dmapper CPPUNIT_TEST_NAME=testSdtRunRichText
    make CppunitTest_sw_ooxmlexport17 
CPPUNIT_TEST_NAME=testDateContentControlExport
    make CppunitTest_sw_core_unocore CPPUNIT_TEST_NAME=testContentControlDate
    make CppunitTest_xmloff_text CPPUNIT_TEST_NAME=testAliasContentControlExport
    make CppunitTest_xmloff_text CPPUNIT_TEST_NAME=testAliasContentControlImport
    
    No existing unit test found containing blockSDT with tabIndex.
    
    Change-Id: I8a958844e6192b079a2b22a62dedfd8739021f4a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143603
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/offapi/com/sun/star/text/ContentControl.idl 
b/offapi/com/sun/star/text/ContentControl.idl
index a37e3f514bfe..3b6e86b1ac1c 100644
--- a/offapi/com/sun/star/text/ContentControl.idl
+++ b/offapi/com/sun/star/text/ContentControl.idl
@@ -134,6 +134,12 @@ service ContentControl
     */
     [optional, property] long Id;
 
+    /** Describes the order in which keyboard navigation moves between controls
+
+        @since LibreOffice 7.6
+    */
+    [optional, property] unsigned long TabIndex;
+
     /** Describes whether the control itself and/or its data can be modified 
or deleted by the user.
 
         @since LibreOffice 7.6
diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt
index a70e13a81e17..25951891d2a7 100644
--- a/oox/source/token/tokens.txt
+++ b/oox/source/token/tokens.txt
@@ -5124,6 +5124,7 @@ tOff
 tR
 tab
 tabColor
+tabIndex
 tabLst
 tabRatio
 tabSelected
diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng 
b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
index c8898e27ccd4..e326b8913646 100644
--- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
+++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng
@@ -2976,6 +2976,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:tab-index">
+          <rng:ref name="nonNegativeInteger"/>
+        </rng:attribute>
+      </rng:optional>
       <rng:optional>
         <rng:attribute name="loext:lock">
           <rng:ref name="string"/>
diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index 3a95aae622d8..d0801f671cd3 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -182,6 +182,9 @@ class SW_DLLPUBLIC SwContentControl : public 
sw::BroadcastingModify
     /// The id: just remembered.
     sal_Int32 m_nId = 0;
 
+    /// The tabIndex: just remembered.
+    sal_uInt32 m_nTabIndex = 0;
+
     /// The control and content locks: mostly just remembered.
     OUString m_aLock;
 
@@ -369,6 +372,10 @@ public:
 
     sal_Int32 GetId() const { return m_nId; }
 
+    void SetTabIndex(sal_uInt32 nTabIndex) { m_nTabIndex = nTabIndex; }
+
+    sal_uInt32 GetTabIndex() const { return m_nTabIndex; }
+
     // 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; }
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index c3201f27652a..42a38fdef0aa 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -919,6 +919,7 @@ inline constexpr OUStringLiteral UNO_NAME_APPEARANCE = 
u"Appearance";
 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_TAB_INDEX = u"TabIndex";
 inline constexpr OUStringLiteral UNO_NAME_LOCK = u"Lock";
 inline constexpr OUStringLiteral UNO_NAME_DATE_STRING = u"DateString";
 #endif
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 220c6461c3bb..af31044e4190 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("TabIndex", 
uno::Any(sal_uInt32(1)));
     xContentControlProps->setPropertyValue("Lock", 
uno::Any(OUString("sdtContentLocked")));
     xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
 
@@ -659,6 +660,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(static_cast<sal_uInt32>(1), 
pContentControl->GetTabIndex());
     CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), 
pContentControl->GetLock());
 }
 
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
index 7048b856bf71..04795920a0db 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx
@@ -425,6 +425,7 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     xContentControlProps->setPropertyValue("Appearance", 
uno::Any(OUString("hidden")));
     xContentControlProps->setPropertyValue("Alias", 
uno::Any(OUString("myalias")));
     xContentControlProps->setPropertyValue("Tag", uno::Any(OUString("mytag")));
+    xContentControlProps->setPropertyValue("TabIndex", 
uno::Any(sal_uInt32(2)));
     xContentControlProps->setPropertyValue("Lock", 
uno::Any(OUString("sdtLocked")));
 
     xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
@@ -450,6 +451,7 @@ CPPUNIT_TEST_FIXTURE(Test, testDateContentControlExport)
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w15:appearance", "val", "hidden");
     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:tabIndex", "val", "2");
     assertXPath(pXmlDoc, "//w:sdt/w:sdtPr/w:lock", "val", "sdtLocked");
 }
 
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx 
b/sw/source/core/txtnode/attrcontentcontrol.cxx
index 995b35efdd51..233b6acf75d7 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -564,6 +564,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("tab-index"),
+                                      
BAD_CAST(OString::number(m_nTabIndex).getStr()));
     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("lock"),
                                       BAD_CAST(m_aLock.toUtf8().getStr()));
 
diff --git a/sw/source/core/unocore/unocontentcontrol.cxx 
b/sw/source/core/unocore/unocontentcontrol.cxx
index 4195c5ca9cbf..b8546a0a6e28 100644
--- a/sw/source/core/unocore/unocontentcontrol.cxx
+++ b/sw/source/core/unocore/unocontentcontrol.cxx
@@ -180,6 +180,7 @@ public:
     OUString m_aAlias;
     OUString m_aTag;
     sal_Int32 m_nId;
+    sal_uInt32 m_nTabIndex;
     OUString m_aLock;
 
     Impl(SwXContentControl& rThis, SwDoc& rDoc, SwContentControl* 
pContentControl,
@@ -199,6 +200,7 @@ public:
         , m_bComboBox(false)
         , m_bDropDown(false)
         , m_nId(0)
+        , m_nTabIndex(0)
     {
         if (m_pContentControl)
         {
@@ -496,6 +498,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->SetTabIndex(m_pImpl->m_nTabIndex);
     pContentControl->SetLock(m_pImpl->m_aLock);
 
     SwFormatContentControl aContentControl(pContentControl, nWhich);
@@ -1002,6 +1005,21 @@ void SAL_CALL SwXContentControl::setPropertyValue(const 
OUString& rPropertyName,
             }
         }
     }
+    else if (rPropertyName == UNO_NAME_TAB_INDEX)
+    {
+        sal_uInt32 nValue = 0;
+        if (rValue >>= nValue)
+        {
+            if (m_pImpl->m_bIsDescriptor)
+            {
+                m_pImpl->m_nTabIndex = nValue;
+            }
+            else
+            {
+                m_pImpl->m_pContentControl->SetTabIndex(nValue);
+            }
+        }
+    }
     else if (rPropertyName == UNO_NAME_LOCK)
     {
         OUString aValue;
@@ -1290,6 +1308,17 @@ uno::Any SAL_CALL 
SwXContentControl::getPropertyValue(const OUString& rPropertyN
             aRet <<= m_pImpl->m_pContentControl->GetId();
         }
     }
+    else if (rPropertyName == UNO_NAME_TAB_INDEX)
+    {
+        if (m_pImpl->m_bIsDescriptor)
+        {
+            aRet <<= m_pImpl->m_nTabIndex;
+        }
+        else
+        {
+            aRet <<= m_pImpl->m_pContentControl->GetTabIndex();
+        }
+    }
     else if (rPropertyName == UNO_NAME_LOCK)
     {
         if (m_pImpl->m_bIsDescriptor)
diff --git a/sw/source/core/unocore/unomap1.cxx 
b/sw/source/core/unocore/unomap1.cxx
index f8efd75a2b28..e5fcd7779607 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -1020,6 +1020,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_TAB_INDEX, 0, cppu::UnoType<sal_uInt32>::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 cc7ec2bee0ac..88c3cb8a2d24 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -626,6 +626,7 @@ void SdtBlockHelper::DeleteAndResetTheLists()
     if (!m_aAppearance.isEmpty())
         m_aAppearance.clear();
     m_bShowingPlaceHolder = false;
+    m_nTabIndex = 0;
     m_bHasId = false;
 }
 
@@ -734,6 +735,10 @@ 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_nTabIndex)
+        pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, XML_val),
+                                     OString::number(m_nTabIndex));
+
     if (!m_aLock.isEmpty())
         pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val), 
m_aLock);
 }
@@ -855,6 +860,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_tabIndex" && 
!m_nTabIndex)
+        {
+            if (!(aPropertyValue.Value >>= m_nTabIndex))
+                SAL_WARN("sw.ww8", "DocxAttributeOutput::GrabBag: unexpected 
sdt tabIndex value");
+        }
         else if (aPropertyValue.Name == "ooxml:CT_SdtPr_lock" && 
m_aLock.isEmpty())
         {
             if (!(aPropertyValue.Value >>= m_aLock))
@@ -2426,6 +2436,12 @@ void DocxAttributeOutput::WriteContentControlStart()
                                        
OString::number(m_pContentControl->GetId()));
     }
 
+    if (m_pContentControl->GetTabIndex())
+    {
+        m_pSerializer->singleElementNS(XML_w, XML_tabIndex, FSNS(XML_w, 
XML_val),
+                                       
OString::number(m_pContentControl->GetTabIndex()));
+    }
+
     if (!m_pContentControl->GetLock().isEmpty())
     {
         m_pSerializer->singleElementNS(XML_w, XML_lock, FSNS(XML_w, XML_val),
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index 776785e8101f..c4f152b85e5c 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -131,6 +131,7 @@ public:
         : m_bHasId(false)
         , m_bStartedSdt(false)
         , m_bShowingPlaceHolder(false)
+        , m_nTabIndex(0)
         , m_nSdtPrToken(0)
     {}
 
@@ -146,6 +147,7 @@ public:
     bool m_bShowingPlaceHolder;
     OUString m_aAlias;
     OUString m_aTag;
+    sal_Int32 m_nTabIndex;
     OUString m_aLock;
     sal_Int32 m_nSdtPrToken;
 
diff --git a/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx 
b/writerfilter/qa/cppunittests/dmapper/SdtHelper.cxx
index 8f9ea8ca5ddb..7b27ff66d08c 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);
+    sal_uInt32 nTabIndex = 0;
+    xContentControlProps->getPropertyValue("TabIndex") >>= nTabIndex;
+    // This was 0
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(5), nTabIndex);
     OUString aLock;
     xContentControlProps->getPropertyValue("Lock") >>= aLock;
     // This was empty.
diff --git a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx 
b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx
index aabc745bcf0e..ca980abb0356 100644
Binary files a/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx 
and b/writerfilter/qa/cppunittests/dmapper/data/sdt-run-rich-text.docx differ
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index 5f2bd0e61268..ab7cfeb401f0 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -2880,6 +2880,7 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const 
PropertyMapPtr& rContext )
     case NS_ooxml::LN_CT_SdtPr_color:
     case NS_ooxml::LN_CT_SdtPr_appearance:
     case NS_ooxml::LN_CT_SdtPr_tag:
+    case NS_ooxml::LN_CT_SdtPr_tabIndex:
     case NS_ooxml::LN_CT_SdtPr_lock:
     {
         if (!m_pImpl->GetSdtStarts().empty())
@@ -2928,6 +2929,12 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const 
PropertyMapPtr& rContext )
                 break;
             }
 
+            if (nSprmId == NS_ooxml::LN_CT_SdtPr_tabIndex)
+            {
+                m_pImpl->m_pSdtHelper->SetTabIndex(nIntValue);
+                break;
+            }
+
             if (nSprmId == NS_ooxml::LN_CT_SdtPr_lock)
             {
                 m_pImpl->m_pSdtHelper->SetLock(sStringValue);
@@ -2981,6 +2988,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_tabIndex:    sName = 
"ooxml:CT_SdtPr_tabIndex"; 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;
@@ -3020,6 +3028,13 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const 
PropertyMapPtr& rContext )
             aValue.Value <<= bool(nIntValue);
             m_pImpl->m_pSdtHelper->appendToInteropGrabBag(aValue);
         }
+        else if (nSprmId == NS_ooxml::LN_CT_SdtPr_tabIndex)
+        {
+            beans::PropertyValue aValue;
+            aValue.Name = sName;
+            aValue.Value <<= nIntValue;
+            m_pImpl->m_pSdtHelper->appendToInteropGrabBag(aValue);
+        }
         else
             m_pImpl->m_pSdtHelper->appendToInteropGrabBag(getInteropGrabBag());
         
m_pImpl->m_pSdtHelper->setOutsideAParagraph(m_pImpl->IsOutsideAParagraph());
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index cfb0c1043df9..6417b858a2f3 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -968,6 +968,11 @@ void DomainMapper_Impl::PopSdt()
         xContentControlProps->setPropertyValue("Id", 
uno::Any(m_pSdtHelper->GetId()));
     }
 
+    if (m_pSdtHelper->GetTabIndex())
+    {
+        xContentControlProps->setPropertyValue("TabIndex", 
uno::Any(m_pSdtHelper->GetTabIndex()));
+    }
+
     if (!m_pSdtHelper->GetLock().isEmpty())
     {
         xContentControlProps->setPropertyValue("Lock", 
uno::Any(m_pSdtHelper->GetLock()));
diff --git a/writerfilter/source/dmapper/SdtHelper.cxx 
b/writerfilter/source/dmapper/SdtHelper.cxx
index 9d7b119f9969..f7642c464889 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_nTabIndex = 0;
     m_aLock.clear();
 }
 
@@ -551,6 +552,10 @@ void SdtHelper::SetId(sal_Int32 nId) { m_nId = nId; }
 
 sal_Int32 SdtHelper::GetId() const { return m_nId; }
 
+void SdtHelper::SetTabIndex(sal_uInt32 nTabIndex) { m_nTabIndex = nTabIndex; }
+
+sal_uInt32 SdtHelper::GetTabIndex() const { return m_nTabIndex; }
+
 void SdtHelper::SetLock(const OUString& rLock) { m_aLock = rLock; }
 
 const OUString& SdtHelper::GetLock() const { return m_aLock; }
diff --git a/writerfilter/source/dmapper/SdtHelper.hxx 
b/writerfilter/source/dmapper/SdtHelper.hxx
index f9cdfd8bf966..6803db16ef06 100644
--- a/writerfilter/source/dmapper/SdtHelper.hxx
+++ b/writerfilter/source/dmapper/SdtHelper.hxx
@@ -138,6 +138,9 @@ class SdtHelper final : public virtual SvRefBase
     /// <w:sdtPr>'s <w:id w:val="...">.
     sal_Int32 m_nId = 0;
 
+    /// <w:sdtPr>'s <w:tabIndex w:val="...">.
+    sal_uInt32 m_nTabIndex = 0;
+
     /// <w:sdtPr>'s <w:lock w:val="...">.
     OUString m_aLock;
 
@@ -232,6 +235,9 @@ public:
     void SetId(sal_Int32 nId);
     sal_Int32 GetId() const;
 
+    void SetTabIndex(sal_uInt32 nTabIndex);
+    sal_uInt32 GetTabIndex() const;
+
     void SetLock(const OUString& rLock);
     const OUString& GetLock() const;
 
diff --git a/writerfilter/source/ooxml/model.xml 
b/writerfilter/source/ooxml/model.xml
index e2b139e52bd5..d6fb6cd81e1e 100644
--- a/writerfilter/source/ooxml/model.xml
+++ b/writerfilter/source/ooxml/model.xml
@@ -14003,6 +14003,9 @@
           <element name="id">
             <ref name="CT_DecimalNumber"/>
           </element>
+          <element name="tabIndex">
+            <ref name="CT_DecimalNumber"/>
+          </element>
           <element name="tag">
             <ref name="CT_String"/>
           </element>
@@ -18309,6 +18312,7 @@
       <element name="dataBinding" tokenid="ooxml:CT_SdtPr_dataBinding"/>
       <element name="temporary" tokenid="ooxml:CT_SdtPr_temporary"/>
       <element name="id" tokenid="ooxml:CT_SdtPr_id"/>
+      <element name="tabIndex" tokenid="ooxml:CT_SdtPr_tabIndex"/>
       <element name="tag" tokenid="ooxml:CT_SdtPr_tag"/>
       <element name="equation" tokenid="ooxml:CT_SdtPr_equation"/>
       <element name="comboBox" tokenid="ooxml:CT_SdtPr_comboBox"/>
diff --git a/xmloff/qa/unit/data/content-control-alias.fodt 
b/xmloff/qa/unit/data/content-control-alias.fodt
index 8f541bef42cc..48fdc7b9436c 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" 
loext:lock="sdtContentLocked">test</loext:content-control></text:p>
+      <text:p><loext:content-control loext:alias="my alias" loext:tag="my tag" 
loext:tab-index="4" 
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 39869ab7445a..9ae9e5cc1d22 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("TabIndex", 
uno::Any(sal_uInt32(3)));
     xContentControlProps->setPropertyValue("Lock", 
uno::Any(OUString("unlocked")));
     xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
 
@@ -795,6 +796,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", "tab-index", "3");
     assertXPath(pXmlDoc, "//loext:content-control", "lock", "unlocked");
 }
 
@@ -855,6 +857,9 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testAliasContentControlImport)
     OUString aTag;
     xContentControlProps->getPropertyValue("Tag") >>= aTag;
     CPPUNIT_ASSERT_EQUAL(OUString("my tag"), aTag);
+    sal_uInt32 nTabIndex;
+    xContentControlProps->getPropertyValue("TabIndex") >>= nTabIndex;
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt32>(4), nTabIndex);
     OUString aLock;
     xContentControlProps->getPropertyValue("Lock") >>= aLock;
     CPPUNIT_ASSERT_EQUAL(OUString("sdtContentLocked"), aLock);
diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx
index 6c0f1689f140..2c922a6242af 100644
--- a/xmloff/source/text/txtparae.cxx
+++ b/xmloff/source/text/txtparae.cxx
@@ -4072,6 +4072,14 @@ void XMLTextParagraphExport::ExportContentControl(
             GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_TAG, aTag);
         }
 
+        sal_uInt32 nTabIndex;
+        xPropertySet->getPropertyValue("TabIndex") >>= nTabIndex;
+        if (nTabIndex)
+        {
+            GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_TAB_INDEX,
+                                     OUString::number(nTabIndex));
+        }
+
         OUString aLock;
         xPropertySet->getPropertyValue("Lock") >>= aLock;
         if (!aLock.isEmpty())
diff --git a/xmloff/source/text/xmlcontentcontrolcontext.cxx 
b/xmloff/source/text/xmlcontentcontrolcontext.cxx
index 83c1621b057e..caf1af04b8d7 100644
--- a/xmloff/source/text/xmlcontentcontrolcontext.cxx
+++ b/xmloff/source/text/xmlcontentcontrolcontext.cxx
@@ -49,6 +49,7 @@ void XMLContentControlContext::startFastElement(
     for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
     {
         bool bTmp = false;
+        sal_Int32 nTmp = 0;
 
         switch (rIter.getToken())
         {
@@ -151,6 +152,14 @@ void XMLContentControlContext::startFastElement(
                 m_aTag = rIter.toString();
                 break;
             }
+            case XML_ELEMENT(LO_EXT, XML_TAB_INDEX):
+            {
+                if (sax::Converter::convertNumber(nTmp, rIter.toView()))
+                {
+                    m_nTabIndex = nTmp;
+                }
+                break;
+            }
             case XML_ELEMENT(LO_EXT, XML_LOCK):
             {
                 m_aLock = rIter.toString();
@@ -267,6 +276,11 @@ void XMLContentControlContext::endFastElement(sal_Int32)
         xPropertySet->setPropertyValue("Tag", uno::Any(m_aTag));
     }
 
+    if (m_nTabIndex)
+    {
+        xPropertySet->setPropertyValue("TabIndex", uno::Any(m_nTabIndex));
+    }
+
     if (!m_aLock.isEmpty())
     {
         xPropertySet->setPropertyValue("Lock", uno::Any(m_aLock));
diff --git a/xmloff/source/text/xmlcontentcontrolcontext.hxx 
b/xmloff/source/text/xmlcontentcontrolcontext.hxx
index f0b1eea0b010..44abe71d6a08 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;
+    sal_uInt32 m_nTabIndex = 0;
     OUString m_aLock;
 
 public:
commit 4660314df4cec62defcaf966d09a3de4ee1f6c7a
Author:     Justin Luth <[email protected]>
AuthorDate: Tue Dec 13 12:15:22 2022 -0500
Commit:     Andras Timar <[email protected]>
CommitDate: Sat Jan 28 18:46:04 2023 +0100

    tdf#151548 vba ContentControls: fix fundamental error
    
    The problem was that the SwTextControl could be deleted
    by the user or in VBA and my references (or pointers
    for that matter) to m_rCC would be invalid.
    
    Instead, use the shared_ptr, which will always be valid,
    from the SwContentControl. Thankfully, there is
    backwards access to the Format and Text controls.
    
    This is nicer anyways. It has reduced code lines,
    and simpler access to the most used aspect.
    
    This is a squashed commit, including -------------
    tdf#151548 vba ContentControls: showingPlaceholder improvements
    
    I reviewed the effect of the recent change to how showingPlaceholder
    status was reset. It really didn't have any effect that I could find,
    but the code review resulted in these minor improvements.
    
    Change-Id: I4ba8da694c1dd4a4e7c77fe0cf20e5a5082564df
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144134
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    ------------------and-----------------
    tdf#151548 vba ContentControls: don't always return a filtered value
    
    Searching for content controls can be filtered by tag, title, or Id.
    The function was erroneously returning the last control in the
    list if the specified control did not exist.
    
    Change-Id: I0cfbccadc134b64456d703f1e19db6e142f162fd
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144346
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    -----------------
    
    Change-Id: Ie382b33ecda5c3d0024f0eabcb8ff158db942f52
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144345
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144554
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/sw/source/ui/vba/vbacontentcontrol.cxx 
b/sw/source/ui/vba/vbacontentcontrol.cxx
index 03569406cb53..8e8d8a12382b 100644
--- a/sw/source/ui/vba/vbacontentcontrol.cxx
+++ b/sw/source/ui/vba/vbacontentcontrol.cxx
@@ -34,11 +34,12 @@ using namespace ::com::sun::star;
 SwVbaContentControl::SwVbaContentControl(const 
uno::Reference<XHelperInterface>& rParent,
                                          const 
uno::Reference<uno::XComponentContext>& rContext,
                                          const 
uno::Reference<text::XTextDocument>& xTextDocument,
-                                         SwTextContentControl& rContentControl)
+                                         std::shared_ptr<SwContentControl> 
pContentControl)
     : SwVbaContentControl_BASE(rParent, rContext)
     , mxTextDocument(xTextDocument)
-    , m_rCC(rContentControl)
+    , m_pCC(pContentControl)
 {
+    assert(m_pCC && "SwVbaContentControl created without a shared_ptr. Why 
would you do that?");
 }
 
 SwVbaContentControl::~SwVbaContentControl() {}
@@ -89,11 +90,7 @@ void SwVbaContentControl::setBuildingBlockType(sal_Int32 
nSet)
     SAL_INFO("sw.vba", "SwVbaContentControl::setBuildingBlockType[" << nSet << 
"] stub");
 }
 
-sal_Bool SwVbaContentControl::getChecked()
-{
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->GetCheckbox() && pCC->GetChecked();
-}
+sal_Bool SwVbaContentControl::getChecked() { return m_pCC->GetCheckbox() && 
m_pCC->GetChecked(); }
 
 void SwVbaContentControl::setChecked(sal_Bool bSet)
 {
@@ -104,20 +101,19 @@ void SwVbaContentControl::setChecked(sal_Bool bSet)
     if (getLockContents())
         return;
 
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    if (pCC->GetCheckbox() && pCC->GetChecked() != static_cast<bool>(bSet))
+    if (m_pCC->GetCheckbox() && m_pCC->GetChecked() != static_cast<bool>(bSet))
     {
-        pCC->SetChecked(bSet);
-        pCC->SetShowingPlaceHolder(false);
-        m_rCC.Invalidate();
+        m_pCC->SetChecked(bSet);
+        m_pCC->SetShowingPlaceHolder(false);
+        if (m_pCC->GetTextAttr())
+            m_pCC->GetTextAttr()->Invalidate();
     }
 }
 
 sal_Int32 SwVbaContentControl::getColor()
 {
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
     //This is just an assumed implementation - I have no testing environment 
to confirm.
-    OUString sColor = pCC->GetColor();
+    OUString sColor = m_pCC->GetColor();
     if (sColor == "wdColorAutomatic")
         return word::WdColor::wdColorAutomatic;
     if (sColor == "wdColorBlack")
@@ -242,186 +238,184 @@ sal_Int32 SwVbaContentControl::getColor()
 
 void SwVbaContentControl::setColor(sal_Int32 nWdColor)
 {
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-
     switch (nWdColor)
     {
         case word::WdColor::wdColorAqua:
-            pCC->SetColor("wdColorAqua");
+            m_pCC->SetColor("wdColorAqua");
             break;
         case word::WdColor::wdColorAutomatic:
-            pCC->SetColor("wdColorAutomatic");
+            m_pCC->SetColor("wdColorAutomatic");
             break;
         case word::WdColor::wdColorBlack:
-            pCC->SetColor("wdColorBlack");
+            m_pCC->SetColor("wdColorBlack");
             break;
         case word::WdColor::wdColorBlue:
-            pCC->SetColor("wdColorBlue");
+            m_pCC->SetColor("wdColorBlue");
             break;
         case word::WdColor::wdColorBlueGray:
-            pCC->SetColor("wdColorBlueGray");
+            m_pCC->SetColor("wdColorBlueGray");
             break;
         case word::WdColor::wdColorBrightGreen:
-            pCC->SetColor("wdColorBrightGreen");
+            m_pCC->SetColor("wdColorBrightGreen");
             break;
         case word::WdColor::wdColorBrown:
-            pCC->SetColor("wdColorBrown");
+            m_pCC->SetColor("wdColorBrown");
             break;
         case word::WdColor::wdColorDarkBlue:
-            pCC->SetColor("wdColorDarkBlue");
+            m_pCC->SetColor("wdColorDarkBlue");
             break;
         case word::WdColor::wdColorDarkGreen:
-            pCC->SetColor("wdColorDarkGreen");
+            m_pCC->SetColor("wdColorDarkGreen");
             break;
         case word::WdColor::wdColorDarkRed:
-            pCC->SetColor("wdColorDarkRed");
+            m_pCC->SetColor("wdColorDarkRed");
             break;
         case word::WdColor::wdColorDarkTeal:
-            pCC->SetColor("wdColorDarkTeal");
+            m_pCC->SetColor("wdColorDarkTeal");
             break;
         case word::WdColor::wdColorDarkYellow:
-            pCC->SetColor("wdColorDarkYellow");
+            m_pCC->SetColor("wdColorDarkYellow");
             break;
         case word::WdColor::wdColorGold:
-            pCC->SetColor("wdColorGold");
+            m_pCC->SetColor("wdColorGold");
             break;
         case word::WdColor::wdColorGray05:
-            pCC->SetColor("wdColorGray05");
+            m_pCC->SetColor("wdColorGray05");
             break;
         case word::WdColor::wdColorGray10:
-            pCC->SetColor("wdColorGray10");
+            m_pCC->SetColor("wdColorGray10");
             break;
         case word::WdColor::wdColorGray125:
-            pCC->SetColor("wdColorGray125");
+            m_pCC->SetColor("wdColorGray125");
             break;
         case word::WdColor::wdColorGray15:
-            pCC->SetColor("wdColorGray15");
+            m_pCC->SetColor("wdColorGray15");
             break;
         case word::WdColor::wdColorGray20:
-            pCC->SetColor("wdColorGray20");
+            m_pCC->SetColor("wdColorGray20");
             break;
         case word::WdColor::wdColorGray25:
-            pCC->SetColor("wdColorGray25");
+            m_pCC->SetColor("wdColorGray25");
             break;
         case word::WdColor::wdColorGray30:
-            pCC->SetColor("wdColorGray30");
+            m_pCC->SetColor("wdColorGray30");
             break;
         case word::WdColor::wdColorGray35:
-            pCC->SetColor("wdColorGray35");
+            m_pCC->SetColor("wdColorGray35");
             break;
         case word::WdColor::wdColorGray375:
-            pCC->SetColor("wdColorGray375");
+            m_pCC->SetColor("wdColorGray375");
             break;
         case word::WdColor::wdColorGray40:
-            pCC->SetColor("wdColorGray40");
+            m_pCC->SetColor("wdColorGray40");
             break;
         case word::WdColor::wdColorGray45:
-            pCC->SetColor("wdColorGray45");
+            m_pCC->SetColor("wdColorGray45");
             break;
         case word::WdColor::wdColorGray50:
-            pCC->SetColor("wdColorGray50");
+            m_pCC->SetColor("wdColorGray50");
             break;
         case word::WdColor::wdColorGray55:
-            pCC->SetColor("wdColorGray55");
+            m_pCC->SetColor("wdColorGray55");
             break;
         case word::WdColor::wdColorGray60:
-            pCC->SetColor("wdColorGray60");
+            m_pCC->SetColor("wdColorGray60");
             break;
         case word::WdColor::wdColorGray625:
-            pCC->SetColor("wdColorGray625");
+            m_pCC->SetColor("wdColorGray625");
             break;
         case word::WdColor::wdColorGray65:
-            pCC->SetColor("wdColorGray65");
+            m_pCC->SetColor("wdColorGray65");
             break;
         case word::WdColor::wdColorGray70:
-            pCC->SetColor("wdColorGray70");
+            m_pCC->SetColor("wdColorGray70");
             break;
         case word::WdColor::wdColorGray75:
-            pCC->SetColor("wdColorGray75");
+            m_pCC->SetColor("wdColorGray75");
             break;
         case word::WdColor::wdColorGray80:
-            pCC->SetColor("wdColorGray80");
+            m_pCC->SetColor("wdColorGray80");
             break;
         case word::WdColor::wdColorGray85:
-            pCC->SetColor("wdColorGray85");
+            m_pCC->SetColor("wdColorGray85");
             break;
         case word::WdColor::wdColorGray875:
-            pCC->SetColor("wdColorGray875");
+            m_pCC->SetColor("wdColorGray875");
             break;
         case word::WdColor::wdColorGray90:
-            pCC->SetColor("wdColorGray90");
+            m_pCC->SetColor("wdColorGray90");
             break;
         case word::WdColor::wdColorGray95:
-            pCC->SetColor("wdColorGray95");
+            m_pCC->SetColor("wdColorGray95");
             break;
         case word::WdColor::wdColorGreen:
-            pCC->SetColor("wdColorGreen");
+            m_pCC->SetColor("wdColorGreen");
             break;
         case word::WdColor::wdColorIndigo:
-            pCC->SetColor("wdColorIndigo");
+            m_pCC->SetColor("wdColorIndigo");
             break;
         case word::WdColor::wdColorLavender:
-            pCC->SetColor("wdColorLavender");
+            m_pCC->SetColor("wdColorLavender");
             break;
         case word::WdColor::wdColorLightBlue:
-            pCC->SetColor("wdColorLightBlue");
+            m_pCC->SetColor("wdColorLightBlue");
             break;
         case word::WdColor::wdColorLightGreen:
-            pCC->SetColor("wdColorLightGreen");
+            m_pCC->SetColor("wdColorLightGreen");
             break;
         case word::WdColor::wdColorLightOrange:
-            pCC->SetColor("wdColorLightOrange");
+            m_pCC->SetColor("wdColorLightOrange");
             break;
         case word::WdColor::wdColorLightTurquoise:
-            pCC->SetColor("wdColorLightTurquoise");
+            m_pCC->SetColor("wdColorLightTurquoise");
             break;
         case word::WdColor::wdColorLightYellow:
-            pCC->SetColor("wdColorLightYellow");
+            m_pCC->SetColor("wdColorLightYellow");
             break;
         case word::WdColor::wdColorLime:
-            pCC->SetColor("wdColorLime");
+            m_pCC->SetColor("wdColorLime");
             break;
         case word::WdColor::wdColorOliveGreen:
-            pCC->SetColor("wdColorOliveGreen");
+            m_pCC->SetColor("wdColorOliveGreen");
             break;
         case word::WdColor::wdColorOrange:
-            pCC->SetColor("wdColorOrange");
+            m_pCC->SetColor("wdColorOrange");
             break;
         case word::WdColor::wdColorPaleBlue:
-            pCC->SetColor("wdColorPaleBlue");
+            m_pCC->SetColor("wdColorPaleBlue");
             break;
         case word::WdColor::wdColorPink:
-            pCC->SetColor("wdColorPink");
+            m_pCC->SetColor("wdColorPink");
             break;
         case word::WdColor::wdColorPlum:
-            pCC->SetColor("wdColorPlum");
+            m_pCC->SetColor("wdColorPlum");
             break;
         case word::WdColor::wdColorRed:
-            pCC->SetColor("wdColorRed");
+            m_pCC->SetColor("wdColorRed");
             break;
         case word::WdColor::wdColorRose:
-            pCC->SetColor("wdColorRose");
+            m_pCC->SetColor("wdColorRose");
             break;
         case word::WdColor::wdColorSeaGreen:
-            pCC->SetColor("wdColorSeaGreen");
+            m_pCC->SetColor("wdColorSeaGreen");
             break;
         case word::WdColor::wdColorSkyBlue:
-            pCC->SetColor("wdColorSkyBlue");
+            m_pCC->SetColor("wdColorSkyBlue");
             break;
         case word::WdColor::wdColorTan:
-            pCC->SetColor("wdColorTan");
+            m_pCC->SetColor("wdColorTan");
             break;
         case word::WdColor::wdColorTeal:
-            pCC->SetColor("wdColorTeal");
+            m_pCC->SetColor("wdColorTeal");
             break;
         case word::WdColor::wdColorTurquoise:
-            pCC->SetColor("wdColorTurquoise");
+            m_pCC->SetColor("wdColorTurquoise");
             break;
         case word::WdColor::wdColorViolet:
-            pCC->SetColor("wdColorViolet");
+            m_pCC->SetColor("wdColorViolet");
             break;
         case word::WdColor::wdColorWhite:
-            pCC->SetColor("wdColorWhite");
+            m_pCC->SetColor("wdColorWhite");
             break;
         default:;
     }
@@ -439,17 +433,9 @@ void SwVbaContentControl::setDateCalendarType(sal_Int32 
nSet)
     SAL_INFO("sw.vba", "SwVbaContentControl::setDateCalendarType[" << nSet << 
"] stub");
 }
 
-OUString SwVbaContentControl::getDateDisplayFormat()
-{
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->GetDateFormat();
-}
+OUString SwVbaContentControl::getDateDisplayFormat() { return 
m_pCC->GetDateFormat(); }
 
-void SwVbaContentControl::setDateDisplayFormat(const OUString& sSet)
-{
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    pCC->SetDateFormat(sSet);
-}
+void SwVbaContentControl::setDateDisplayFormat(const OUString& sSet) { 
m_pCC->SetDateFormat(sSet); }
 
 sal_Int32 SwVbaContentControl::getDateStorageFormat()
 {
@@ -472,19 +458,17 @@ sal_Int32 SwVbaContentControl::getDateDisplayLocale()
 
 uno::Any SwVbaContentControl::getDropdownListEntries()
 {
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    if (!pCC->GetDropDown() && !pCC->GetComboBox())
+    if (!m_pCC->GetDropDown() && !m_pCC->GetComboBox())
         return uno::Any();
 
     return uno::Any(
-        uno::Reference<XCollection>(new SwVbaContentControlListEntries(this, 
mxContext, m_rCC)));
+        uno::Reference<XCollection>(new SwVbaContentControlListEntries(this, 
mxContext, m_pCC)));
 }
 
 OUString SwVbaContentControl::getID()
 {
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
     // This signed integer is treated in VBA as if it was an unsigned int.
-    return OUString::number(static_cast<sal_uInt32>(pCC->GetId()));
+    return OUString::number(static_cast<sal_uInt32>(m_pCC->GetId()));
 }
 
 sal_Int32 SwVbaContentControl::getLevel()
@@ -496,53 +480,49 @@ sal_Int32 SwVbaContentControl::getLevel()
 
 sal_Bool SwVbaContentControl::getLockContentControl()
 {
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    std::optional<bool> oLock = pCC->GetLock(/*bControl=*/true);
+    std::optional<bool> oLock = m_pCC->GetLock(/*bControl=*/true);
     return oLock && *oLock;
 }
 
 void SwVbaContentControl::setLockContentControl(sal_Bool bSet)
 {
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    std::optional<bool> oLock = pCC->GetLock(/*bControl=*/false);
-    pCC->SetLock(/*bContents=*/oLock && *oLock, /*bControl=*/bSet);
+    std::optional<bool> oLock = m_pCC->GetLock(/*bControl=*/false);
+    m_pCC->SetLock(/*bContents=*/oLock && *oLock, /*bControl=*/bSet);
 }
 
 sal_Bool SwVbaContentControl::getLockContents()
 {
-    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);
+    std::optional<bool> oLock = m_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
-        || pCC->GetType() == SwContentControlType::PICTURE)
+    if (m_pCC->GetType() == SwContentControlType::CHECKBOX
+        || m_pCC->GetType() == SwContentControlType::DROP_DOWN_LIST
+        || m_pCC->GetType() == SwContentControlType::PICTURE)
     {
         return false;
     }
 
-    return pCC->GetReadWrite();
+    return m_pCC->GetReadWrite();
 }
 
 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);
+    std::optional<bool> oLock = m_pCC->GetLock(/*bControl=*/true);
+    m_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
-        || pCC->GetType() == SwContentControlType::PICTURE)
+    if (m_pCC->GetType() == SwContentControlType::CHECKBOX
+        || m_pCC->GetType() == SwContentControlType::DROP_DOWN_LIST
+        || m_pCC->GetType() == SwContentControlType::PICTURE)
     {
         return;
     }
-    pCC->SetReadWrite(bSet);
+    m_pCC->SetReadWrite(bSet);
 }
 
 sal_Bool SwVbaContentControl::getMultiLine()
@@ -558,27 +538,23 @@ void SwVbaContentControl::setMultiLine(sal_Bool /*bSet*/)
 
 OUString SwVbaContentControl::getPlaceholderText()
 {
-    // return pCC->GetPlaceholderDocPart(); // This is not correct. Much more 
complex than this...
+    // return m_pCC->GetPlaceholderDocPart(); // This is not correct. Much 
more complex than this...
     SAL_INFO("sw.vba", "SwVbaContentControl::getPlaceholderText stub");
     return OUString();
 }
 
-sal_Bool SwVbaContentControl::getShowingPlaceholderText()
-{
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->GetShowingPlaceHolder();
-}
+sal_Bool SwVbaContentControl::getShowingPlaceholderText() { return 
m_pCC->GetShowingPlaceHolder(); }
 
 uno::Reference<word::XRange> SwVbaContentControl::getRange()
 {
     uno::Reference<word::XRange> xRet;
-    SwTextNode* pTextNode = m_rCC.GetTextNode();
-    if (pTextNode)
+    SwTextNode* pTextNode = m_pCC->GetTextNode();
+    if (pTextNode && m_pCC->GetTextAttr())
     {
         // Don't select the text attribute itself at the start.
-        SwPosition aStart(*pTextNode, m_rCC.GetStart() + 1);
+        SwPosition aStart(*pTextNode, m_pCC->GetTextAttr()->GetStart() + 1);
         // Don't select the CH_TXTATR_BREAKWORD itself at the end.
-        SwPosition aEnd(*pTextNode, *m_rCC.End() - 1);
+        SwPosition aEnd(*pTextNode, *m_pCC->GetTextAttr()->End() - 1);
         uno::Reference<text::XTextRange> xText(
             SwXTextRange::CreateXTextRange(pTextNode->GetDoc(), aStart, 
&aEnd));
         if (xText.is())
@@ -599,17 +575,9 @@ void 
SwVbaContentControl::setRepeatingSectionItemTitle(const OUString& rSet)
     SAL_INFO("sw.vba", "SwVbaContentControl::setRepeatingSectionItemTitle[" << 
rSet << "] stub");
 }
 
-OUString SwVbaContentControl::getTag()
-{
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->GetTag();
-}
+OUString SwVbaContentControl::getTag() { return m_pCC->GetTag(); }
 
-void SwVbaContentControl::setTag(const OUString& rSet)
-{
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->SetTag(rSet);
-}
+void SwVbaContentControl::setTag(const OUString& rSet) { return 
m_pCC->SetTag(rSet); }
 
 sal_Bool SwVbaContentControl::getTemporary()
 {
@@ -623,22 +591,13 @@ void SwVbaContentControl::setTemporary(sal_Bool /*bSet*/)
     SAL_INFO("sw.vba", "SwVbaContentControl::setTemporary stub");
 }
 
-OUString SwVbaContentControl::getTitle()
-{
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->GetAlias();
-}
+OUString SwVbaContentControl::getTitle() { return m_pCC->GetAlias(); }
 
-void SwVbaContentControl::setTitle(const OUString& rSet)
-{
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    return pCC->SetAlias(rSet);
-}
+void SwVbaContentControl::setTitle(const OUString& rSet) { return 
m_pCC->SetAlias(rSet); }
 
 sal_Int32 SwVbaContentControl::getType()
 {
-    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
-    SwContentControlType eType = pCC->GetType();
+    SwContentControlType eType = m_pCC->GetType();
     sal_Int32 eVbaType = word::WdContentControlType::wdContentControlRichText;
 
     switch (eType)
@@ -670,7 +629,6 @@ sal_Int32 SwVbaContentControl::getType()
 void SwVbaContentControl::setType(sal_Int32 nSet)
 {
     SAL_INFO("sw.vba", "SwVbaContentControl::setType[" << nSet << "] stub");
-    //     std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
     //     SwContentControlType eType = SwContentControlType::RICH_TEXT;
     //     switch(nSet)
     //     {
@@ -695,7 +653,7 @@ void SwVbaContentControl::setType(sal_Int32 nSet)
     //         case word::WdContentControlType::wdContentControlRichText:
     //         default:;
     //     }
-    //     pCC->SetType(eType);
+    //     m_pCC->SetType(eType);
 }
 
 void SwVbaContentControl::Copy()
@@ -705,57 +663,60 @@ void SwVbaContentControl::Copy()
 
 void SwVbaContentControl::Cut()
 {
-    if (getLockContentControl())
+    if (getLockContentControl() || !m_pCC->GetTextAttr())
         return;
 
     SAL_INFO("sw.vba",
              "SwVbaContentControl::Cut[" << getID() << "], but missing sending 
to clipboard");
 
-    m_rCC.Delete(/*bSaveContents=*/getLockContents());
+    m_pCC->GetTextAttr()->Delete(/*bSaveContents=*/getLockContents());
 }
 
 void SwVbaContentControl::Delete(const uno::Any& DeleteContents)
 {
-    if (getLockContentControl())
+    if (getLockContentControl() || !m_pCC->GetTextAttr())
         return;
 
     bool bDeleteContents = false;
     DeleteContents >>= bDeleteContents;
 
-    m_rCC.Delete(/*bSaveContents=*/!bDeleteContents || getLockContents());
+    m_pCC->GetTextAttr()->Delete(/*bSaveContents=*/!bDeleteContents || 
getLockContents());
 }
 
 void SwVbaContentControl::SetCheckedSymbol(sal_Int32 Character, const 
uno::Any& Font)
 {
+    if (!m_pCC->GetTextAttr())
+        return;
+
     SAL_INFO_IF(Font.hasValue(), "sw.vba", "SetCheckedSymbol Font[" << Font << 
"] stub");
     if (Character < 31 || Character > SAL_MAX_UINT16)
         return; // unsupported character. Would such a thing exist in VBA?
 
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    pCC->SetCheckedState(OUString(static_cast<sal_Unicode>(Character)));
+    m_pCC->SetCheckedState(OUString(static_cast<sal_Unicode>(Character)));
 
-    if (pCC->GetCheckbox() && pCC->GetChecked())
-        m_rCC.Invalidate();
+    if (m_pCC->GetCheckbox() && m_pCC->GetChecked() && 
!m_pCC->GetShowingPlaceHolder())
+        m_pCC->GetTextAttr()->Invalidate();
 }
 
 void SwVbaContentControl::SetUnCheckedSymbol(sal_Int32 Character, const 
uno::Any& Font)
 {
+    if (!m_pCC->GetTextAttr())
+        return;
+
     SAL_INFO_IF(Font.hasValue(), "sw.vba", "SetUnCheckedSymbol Font[" << Font 
<< "] stub");
     if (Character < 31 || Character > SAL_MAX_UINT16)
         return; // unsupported character. Would such a thing exist in VBA?
 
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    pCC->SetUncheckedState(OUString(static_cast<sal_Unicode>(Character)));
+    m_pCC->SetUncheckedState(OUString(static_cast<sal_Unicode>(Character)));
 
-    if (pCC->GetCheckbox() && !pCC->GetChecked())
-        m_rCC.Invalidate();
+    if (m_pCC->GetCheckbox() && !m_pCC->GetChecked() && 
!m_pCC->GetShowingPlaceHolder())
+        m_pCC->GetTextAttr()->Invalidate();
 }
 
 void SwVbaContentControl::SetPlaceholderText(const uno::Any& BuildingBlock, 
const uno::Any& Range,
                                              const uno::Any& Text)
 {
     SAL_INFO("sw.vba", "SwVbaContentControl::SetPlaceholderText stub");
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
     if (BuildingBlock.hasValue())
     {
         // Set placeholder text to the building block - whatever that is.
@@ -771,9 +732,12 @@ void SwVbaContentControl::SetPlaceholderText(const 
uno::Any& BuildingBlock, cons
     else
     {
         // Remove placeholder text.
-        pCC->SetPlaceholderDocPart("");
+        m_pCC->SetPlaceholderDocPart("");
+    }
+    if (m_pCC->GetShowingPlaceHolder() && !getLockContents() && 
m_pCC->GetTextAttr())
+    {
+        //replace the text and ensure showing placeholder is still set
     }
-    m_rCC.Invalidate();
 }
 
 void SwVbaContentControl::Ungroup() { SAL_INFO("sw.vba", 
"SwVbaContentControl::UnGroup stub"); }
diff --git a/sw/source/ui/vba/vbacontentcontrol.hxx 
b/sw/source/ui/vba/vbacontentcontrol.hxx
index 2d3c2ee56ad1..9f98b92468b5 100644
--- a/sw/source/ui/vba/vbacontentcontrol.hxx
+++ b/sw/source/ui/vba/vbacontentcontrol.hxx
@@ -21,14 +21,14 @@ class SwVbaContentControl : public SwVbaContentControl_BASE
 {
 private:
     css::uno::Reference<css::text::XTextDocument> mxTextDocument;
-    SwTextContentControl& m_rCC;
+    std::shared_ptr<SwContentControl> m_pCC;
 
 public:
     /// @throws css::uno::RuntimeException
     SwVbaContentControl(const css::uno::Reference<ooo::vba::XHelperInterface>& 
rParent,
                         const 
css::uno::Reference<css::uno::XComponentContext>& rContext,
                         const css::uno::Reference<css::text::XTextDocument>& 
xTextDocument,
-                        SwTextContentControl& rContentControl);
+                        std::shared_ptr<SwContentControl> pContentControl);
     ~SwVbaContentControl() override;
 
     // XContentControl Properties
diff --git a/sw/source/ui/vba/vbacontentcontrollistentries.cxx 
b/sw/source/ui/vba/vbacontentcontrollistentries.cxx
index dcf71afb9337..996df011a149 100644
--- a/sw/source/ui/vba/vbacontentcontrollistentries.cxx
+++ b/sw/source/ui/vba/vbacontentcontrollistentries.cxx
@@ -48,23 +48,20 @@ class ContentControlListEntryCollectionHelper
 private:
     uno::Reference<XHelperInterface> mxParent;
     uno::Reference<uno::XComponentContext> mxContext;
-    SwTextContentControl& m_rCC;
+    std::shared_ptr<SwContentControl> m_pCC;
 
 public:
     /// @throws css::uno::RuntimeException
     
ContentControlListEntryCollectionHelper(uno::Reference<ov::XHelperInterface> 
xParent,
                                             
uno::Reference<uno::XComponentContext> xContext,
-                                            SwTextContentControl& rCC)
+                                            std::shared_ptr<SwContentControl> 
pCC)
         : mxParent(xParent)
         , mxContext(xContext)
-        , m_rCC(rCC)
+        , m_pCC(pCC)
     {
     }
 
-    sal_Int32 SAL_CALL getCount() override
-    {
-        return 
m_rCC.GetContentControl().GetContentControl()->GetListItems().size();
-    }
+    sal_Int32 SAL_CALL getCount() override { return 
m_pCC->GetListItems().size(); }
 
     uno::Any SAL_CALL getByIndex(sal_Int32 Index) override
     {
@@ -72,7 +69,7 @@ public:
             throw lang::IndexOutOfBoundsException();
 
         return uno::Any(uno::Reference<word::XContentControlListEntry>(
-            new SwVbaContentControlListEntry(mxParent, mxContext, m_rCC, 
Index)));
+            new SwVbaContentControlListEntry(mxParent, mxContext, m_pCC, 
Index)));
     }
 
     uno::Type SAL_CALL getElementType() override
@@ -96,12 +93,12 @@ public:
  */
 SwVbaContentControlListEntries::SwVbaContentControlListEntries(
     const uno::Reference<XHelperInterface>& xParent,
-    const uno::Reference<uno::XComponentContext>& xContext, 
SwTextContentControl& rCC)
+    const uno::Reference<uno::XComponentContext>& xContext, 
std::shared_ptr<SwContentControl> pCC)
     : SwVbaContentControlListEntries_BASE(
           xParent, xContext,
           uno::Reference<container::XIndexAccess>(
-              new ContentControlListEntryCollectionHelper(xParent, xContext, 
rCC)))
-    , m_rCC(rCC)
+              new ContentControlListEntryCollectionHelper(xParent, xContext, 
pCC)))
+    , m_pCC(pCC)
 {
 }
 
@@ -110,8 +107,7 @@ SwVbaContentControlListEntries::Add(const OUString& rName, 
const uno::Any& rValu
                                     const uno::Any& rIndex)
 {
     // No duplicate Names allowed in VBA
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    for (auto& rListItem : pCC->GetListItems())
+    for (auto& rListItem : m_pCC->GetListItems())
     {
         if (rListItem.ToString() == rName)
             return uno::Reference<word::XContentControlListEntry>();
@@ -122,28 +118,22 @@ SwVbaContentControlListEntries::Add(const OUString& 
rName, const uno::Any& rValu
     // rIndex is 1-based, nZIndex is 0-based. If rIndex is not given, then add 
as the last choice.
     assert(nZIndex > 0);
     --nZIndex;
-    nZIndex = std::min(static_cast<size_t>(nZIndex), 
pCC->GetListItems().size());
+    nZIndex = std::min(static_cast<size_t>(nZIndex), 
m_pCC->GetListItems().size());
 
     OUString sValue;
     rValue >>= sValue;
-    if (pCC->AddListItem(nZIndex, rName, sValue))
+    if (m_pCC->AddListItem(nZIndex, rName, sValue))
     {
         return uno::Reference<word::XContentControlListEntry>(
-            new SwVbaContentControlListEntry(mxParent, mxContext, m_rCC, 
nZIndex));
+            new SwVbaContentControlListEntry(mxParent, mxContext, m_pCC, 
nZIndex));
     }
 
     return uno::Reference<word::XContentControlListEntry>();
 }
 
-void SwVbaContentControlListEntries::Clear()
-{
-    m_rCC.GetContentControl().GetContentControl()->ClearListItems();
-}
+void SwVbaContentControlListEntries::Clear() { m_pCC->ClearListItems(); }
 
-sal_Int32 SwVbaContentControlListEntries::getCount()
-{
-    return 
m_rCC.GetContentControl().GetContentControl()->GetListItems().size();
-}
+sal_Int32 SwVbaContentControlListEntries::getCount() { return 
m_pCC->GetListItems().size(); }
 
 // XEnumerationAccess
 uno::Type SwVbaContentControlListEntries::getElementType()
diff --git a/sw/source/ui/vba/vbacontentcontrollistentries.hxx 
b/sw/source/ui/vba/vbacontentcontrollistentries.hxx
index 65ee6ac814a9..dc32203179dc 100644
--- a/sw/source/ui/vba/vbacontentcontrollistentries.hxx
+++ b/sw/source/ui/vba/vbacontentcontrollistentries.hxx
@@ -24,13 +24,13 @@ typedef 
CollTestImplHelper<ooo::vba::word::XContentControlListEntries>
 class SwVbaContentControlListEntries : public 
SwVbaContentControlListEntries_BASE
 {
 private:
-    SwTextContentControl& m_rCC;
+    std::shared_ptr<SwContentControl> m_pCC;
 
 public:
     /// @throws css::uno::RuntimeException
     SwVbaContentControlListEntries(const 
css::uno::Reference<ov::XHelperInterface>& xParent,
                                    const 
css::uno::Reference<css::uno::XComponentContext>& xContext,
-                                   SwTextContentControl& rCC);
+                                   std::shared_ptr<SwContentControl> pCC);
 
     // XContentControlListEntries
     css::uno::Reference<ooo::vba::word::XContentControlListEntry> SAL_CALL
diff --git a/sw/source/ui/vba/vbacontentcontrollistentry.cxx 
b/sw/source/ui/vba/vbacontentcontrollistentry.cxx
index 80603df21309..73f5e9d0a2cf 100644
--- a/sw/source/ui/vba/vbacontentcontrollistentry.cxx
+++ b/sw/source/ui/vba/vbacontentcontrollistentry.cxx
@@ -14,10 +14,10 @@ using namespace ::com::sun::star;
 
 SwVbaContentControlListEntry::SwVbaContentControlListEntry(
     const uno::Reference<ooo::vba::XHelperInterface>& rParent,
-    const uno::Reference<uno::XComponentContext>& rContext, 
SwTextContentControl& rCC,
+    const uno::Reference<uno::XComponentContext>& rContext, 
std::shared_ptr<SwContentControl> pCC,
     size_t nZIndex)
     : SwVbaContentControlListEntry_BASE(rParent, rContext)
-    , m_rCC(rCC)
+    , m_pCC(pCC)
     , m_nZIndex(nZIndex)
 {
 }
@@ -31,9 +31,8 @@ void SwVbaContentControlListEntry::setIndex(sal_Int32 nSet)
     if (nSet < 1 || static_cast<size_t>(nSet) == m_nZIndex + 1)
         return;
 
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
     // Given a one-based index to set to
-    size_t nIndex = std::min(static_cast<size_t>(nSet), 
pCC->GetListItems().size());
+    size_t nIndex = std::min(static_cast<size_t>(nSet), 
m_pCC->GetListItems().size());
     // change to zero-based index
     --nIndex;
     while (nIndex < m_nZIndex)
@@ -44,16 +43,14 @@ void SwVbaContentControlListEntry::setIndex(sal_Int32 nSet)
 
 OUString SwVbaContentControlListEntry::getText()
 {
-    const std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    assert(m_nZIndex < pCC->GetListItems().size());
-    const SwContentControlListItem& rListItem = pCC->GetListItems()[m_nZIndex];
+    assert(m_nZIndex < m_pCC->GetListItems().size());
+    const SwContentControlListItem& rListItem = 
m_pCC->GetListItems()[m_nZIndex];
     return rListItem.ToString();
 }
 
 void SwVbaContentControlListEntry::setText(const OUString& rSet)
 {
-    const std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
+    std::vector<SwContentControlListItem> vListItems = m_pCC->GetListItems();
     assert(m_nZIndex < vListItems.size());
 
     // prevent duplicates
@@ -63,97 +60,90 @@ void SwVbaContentControlListEntry::setText(const OUString& 
rSet)
             return;
     }
 
-    const std::optional<size_t> 
oSel(pCC->GetSelectedListItem(/*bCheckDocModel=*/true));
-    const bool bNeedsInvalidation = pCC->GetDropDown() && oSel && *oSel == 
m_nZIndex;
+    const std::optional<size_t> 
oSel(m_pCC->GetSelectedListItem(/*bCheckDocModel=*/true));
+    const bool bNeedsInvalidation = m_pCC->GetDropDown() && oSel && *oSel == 
m_nZIndex;
 
     vListItems[m_nZIndex].m_aDisplayText = rSet;
-    pCC->SetListItems(vListItems);
+    m_pCC->SetListItems(vListItems);
 
     if (bNeedsInvalidation)
     {
-        pCC->SetSelectedListItem(m_nZIndex);
-        m_rCC.Invalidate();
+        m_pCC->SetSelectedListItem(m_nZIndex);
+        if (m_pCC->GetTextAttr())
+            m_pCC->GetTextAttr()->Invalidate();
     }
 }
 
 OUString SwVbaContentControlListEntry::getValue()
 {
-    const std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    assert(m_nZIndex < pCC->GetListItems().size());
-    const SwContentControlListItem& rListItem = pCC->GetListItems()[m_nZIndex];
+    assert(m_nZIndex < m_pCC->GetListItems().size());
+    const SwContentControlListItem& rListItem = 
m_pCC->GetListItems()[m_nZIndex];
 
     return rListItem.m_aValue;
 }
 
 void SwVbaContentControlListEntry::setValue(const OUString& rSet)
 {
-    const std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    assert(m_nZIndex < pCC->GetListItems().size());
-    std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
+    assert(m_nZIndex < m_pCC->GetListItems().size());
+    std::vector<SwContentControlListItem> vListItems = m_pCC->GetListItems();
 
     // LO may pull the display text from Value. Ensure changing Value doesn't 
alter display text.
     if (vListItems[m_nZIndex].m_aDisplayText.isEmpty())
         vListItems[m_nZIndex].m_aDisplayText = 
vListItems[m_nZIndex].ToString();
 
     vListItems[m_nZIndex].m_aValue = rSet;
-    pCC->SetListItems(vListItems);
+    m_pCC->SetListItems(vListItems);
 }
 
-void SwVbaContentControlListEntry::Delete()
-{
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    pCC->DeleteListItem(m_nZIndex);
-}
+void SwVbaContentControlListEntry::Delete() { 
m_pCC->DeleteListItem(m_nZIndex); }
 
 void SwVbaContentControlListEntry::MoveDown()
 {
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
     // if already at last position, can't move down
-    if (m_nZIndex >= pCC->GetListItems().size() - 1)
+    if (m_nZIndex >= m_pCC->GetListItems().size() - 1)
         return;
 
-    const std::optional<size_t> oSelected = 
pCC->GetSelectedListItem(/*bCheckDocModel=*/false);
+    const std::optional<size_t> oSelected = 
m_pCC->GetSelectedListItem(/*bCheckDocModel=*/false);
     if (oSelected)
     {
         if (*oSelected == m_nZIndex)
-            pCC->SetSelectedListItem(m_nZIndex + 1);
+            m_pCC->SetSelectedListItem(m_nZIndex + 1);
         else if (*oSelected == m_nZIndex + 1)
-            pCC->SetSelectedListItem(*oSelected - 1);
+            m_pCC->SetSelectedListItem(*oSelected - 1);
     }
-    std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
+    std::vector<SwContentControlListItem> vListItems = m_pCC->GetListItems();
     std::swap(vListItems[m_nZIndex], vListItems[m_nZIndex + 1]);
-    pCC->SetListItems(vListItems);
+    m_pCC->SetListItems(vListItems);
     ++m_nZIndex;
 }
 
 void SwVbaContentControlListEntry::MoveUp()
 {
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
     // if already at position 0, can't move up
-    if (!m_nZIndex || m_nZIndex >= pCC->GetListItems().size())
+    if (!m_nZIndex || m_nZIndex >= m_pCC->GetListItems().size())
         return;
 
-    const std::optional<size_t> oSelected = 
pCC->GetSelectedListItem(/*bCheckDocModel=*/false);
+    const std::optional<size_t> oSelected = 
m_pCC->GetSelectedListItem(/*bCheckDocModel=*/false);
     if (oSelected)
     {
         if (*oSelected == m_nZIndex)
-            pCC->SetSelectedListItem(m_nZIndex - 1);
+            m_pCC->SetSelectedListItem(m_nZIndex - 1);
         else if (*oSelected == m_nZIndex - 1)
-            pCC->SetSelectedListItem(*oSelected + 1);
+            m_pCC->SetSelectedListItem(*oSelected + 1);
     }
-    std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
+    std::vector<SwContentControlListItem> vListItems = m_pCC->GetListItems();
     std::swap(vListItems[m_nZIndex], vListItems[m_nZIndex - 1]);
-    pCC->SetListItems(vListItems);
+    m_pCC->SetListItems(vListItems);
     --m_nZIndex;
 }
 
 void SwVbaContentControlListEntry::Select()
 {
-    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
-    assert(m_nZIndex < pCC->GetListItems().size());
-    pCC->SetSelectedListItem(m_nZIndex);
-    pCC->SetShowingPlaceHolder(false);
-    m_rCC.Invalidate();
+    assert(m_nZIndex < m_pCC->GetListItems().size());
+    m_pCC->SetSelectedListItem(m_nZIndex);
+    m_pCC->SetShowingPlaceHolder(false);
+    if (m_pCC->GetTextAttr())
+        m_pCC->GetTextAttr()->Invalidate();
 }
 
 // XHelperInterface
diff --git a/sw/source/ui/vba/vbacontentcontrollistentry.hxx 
b/sw/source/ui/vba/vbacontentcontrollistentry.hxx
index e6e3c341afe0..bb55eade34d5 100644
--- a/sw/source/ui/vba/vbacontentcontrollistentry.hxx
+++ b/sw/source/ui/vba/vbacontentcontrollistentry.hxx
@@ -20,7 +20,7 @@ typedef 
InheritedHelperInterfaceWeakImpl<ooo::vba::word::XContentControlListEntr
 class SwVbaContentControlListEntry : public SwVbaContentControlListEntry_BASE
 {
 private:
-    SwTextContentControl& m_rCC;
+    std::shared_ptr<SwContentControl> m_pCC;
     // All LO and internal UNO functions are 0-based. Convert to 1-based when 
sending to VBA
     size_t m_nZIndex;
 
@@ -28,7 +28,7 @@ public:
     /// @throws css::uno::RuntimeException
     SwVbaContentControlListEntry(const 
css::uno::Reference<ooo::vba::XHelperInterface>& rParent,
                                  const 
css::uno::Reference<css::uno::XComponentContext>& rContext,
-                                 SwTextContentControl& rCC, size_t nZIndex);
+                                 std::shared_ptr<SwContentControl> pCC, size_t 
nZIndex);
     ~SwVbaContentControlListEntry() override;
 
     // XContentControlListEntry
diff --git a/sw/source/ui/vba/vbacontentcontrols.cxx 
b/sw/source/ui/vba/vbacontentcontrols.cxx
index e39094df357a..3886fe76968f 100644
--- a/sw/source/ui/vba/vbacontentcontrols.cxx
+++ b/sw/source/ui/vba/vbacontentcontrols.cxx
@@ -26,7 +26,7 @@ using namespace ::com::sun::star;
 //        [in] negative indexes indicate the need to search by name, otherwise 
get by index,
 //             using SAL_MAX_INT32 to indicate the need to just get the total 
count.
 //        [out] rIndex indicates the found index, or the total number of 
content controls
-static SwTextContentControl*
+static std::shared_ptr<SwContentControl>
 lcl_getContentControl(std::u16string_view sName, std::u16string_view sTag,
                       std::u16string_view sTitle, sal_Int32& rIndex,
                       const uno::Reference<text::XTextDocument>& xTextDocument,
@@ -38,7 +38,7 @@ lcl_getContentControl(std::u16string_view sName, 
std::u16string_view sTag,
 
     assert(sTag.empty() || sTitle.empty()); // only one grouping at a time is 
allowed
 
-    SwTextContentControl* pControl = nullptr;
+    std::shared_ptr<SwContentControl> pControl;
     std::vector<OUString> vElementNames;
     SwContentControlManager& rManager = pDoc->GetContentControlManager();
     const size_t nLen = rManager.GetCount();
@@ -47,7 +47,7 @@ lcl_getContentControl(std::u16string_view sName, 
std::u16string_view sTag,
         size_t i = static_cast<size_t>(rIndex);
         // This is the normal get-by-index/getCount mode - no need for fancy 
filtering.
         if (i < nLen)
-            pControl = rManager.Get(i);
+            pControl = 
rManager.Get(i)->GetContentControl().GetContentControl();
         else
             rIndex = nLen;
     }
@@ -57,19 +57,25 @@ lcl_getContentControl(std::u16string_view sName, 
std::u16string_view sTag,
         sal_Int32 nCounter = 0;
         for (size_t i = 0; i < nLen; ++i)
         {
-            pControl = rManager.Get(i);
-            if (!sTag.empty()
-                && sTag != 
pControl->GetContentControl().GetContentControl()->GetTag())
+            pControl = 
rManager.Get(i)->GetContentControl().GetContentControl();
+            if (!sTag.empty() && sTag != pControl->GetTag())
+            {
+                pControl = nullptr;
                 continue;
-            if (!sTitle.empty()
-                && sTitle != 
pControl->GetContentControl().GetContentControl()->GetAlias())
+            }
+            if (!sTitle.empty() && sTitle != pControl->GetAlias())
+            {
+                pControl = nullptr;
                 continue;
+            }
 
             // When treated as a name, consider the integer ID to be unsigned
-            const OUString sID = OUString::number(static_cast<sal_uInt32>(
-                pControl->GetContentControl().GetContentControl()->GetId()));
+            const OUString sID = 
OUString::number(static_cast<sal_uInt32>(pControl->GetId()));
             if (!sName.empty() && sName != sID)
+            {
+                pControl = nullptr;
                 continue;
+            }
 
             if (pElementNames)
                 vElementNames.push_back(sID);
@@ -123,7 +129,7 @@ private:
     uno::Reference<text::XTextDocument> mxTextDocument;
     const OUString m_sTag;
     const OUString m_sTitle;
-    SwTextContentControl* m_pCache;
+    std::shared_ptr<SwContentControl> m_pCache;
 
 public:
     /// @throws css::uno::RuntimeException
@@ -137,7 +143,6 @@ public:
         , mxTextDocument(std::move(xTextDocument))
         , m_sTag(rTag)
         , m_sTitle(rTitle)
-        , m_pCache(nullptr)
     {
     }
 
@@ -156,7 +161,7 @@ public:
             throw lang::IndexOutOfBoundsException();
 
         return uno::Any(uno::Reference<word::XContentControl>(
-            new SwVbaContentControl(mxParent, mxContext, mxTextDocument, 
*m_pCache)));
+            new SwVbaContentControl(mxParent, mxContext, mxTextDocument, 
m_pCache)));
     }
 
     // XNameAccess
@@ -174,7 +179,7 @@ public:
             throw container::NoSuchElementException();
 
         return uno::Any(uno::Reference<word::XContentControl>(
-            new SwVbaContentControl(mxParent, mxContext, mxTextDocument, 
*m_pCache)));
+            new SwVbaContentControl(mxParent, mxContext, mxTextDocument, 
m_pCache)));
     }
 
     sal_Bool SAL_CALL hasByName(const OUString& aName) override
@@ -236,7 +241,7 @@ SwVbaContentControls::SwVbaContentControls(const 
uno::Reference<XHelperInterface
 //     }
 //
 //     return uno::Reference<ooo::vba::word::XContentControl>(
-//         new SwVbaContentControl(mxParent, mxContext, m_xTextDocument, 
*pFieldmark));
+//         new SwVbaContentControl(mxParent, mxContext, m_xTextDocument, 
pFieldmark));
 // }
 
 // XEnumerationAccess

Reply via email to