oovbaapi/UnoApi_oovbaapi.mk                    |    3 
 oovbaapi/ooo/vba/word/WdContentControlType.idl |   25 
 oovbaapi/ooo/vba/word/XContentControl.idl      |  103 +++
 oovbaapi/ooo/vba/word/XContentControls.idl     |   23 
 oovbaapi/ooo/vba/word/XDocument.idl            |    3 
 sw/Library_vbaswobj.mk                         |    2 
 sw/qa/core/data/docm/testModernVBA.docm        |binary
 sw/qa/core/macros-test.cxx                     |    4 
 sw/source/ui/vba/vbacontentcontrol.cxx         |  754 +++++++++++++++++++++++++
 sw/source/ui/vba/vbacontentcontrol.hxx         |  142 ++++
 sw/source/ui/vba/vbacontentcontrols.cxx        |  262 ++++++++
 sw/source/ui/vba/vbacontentcontrols.hxx        |   40 +
 sw/source/ui/vba/vbadocument.cxx               |   27 
 sw/source/ui/vba/vbadocument.hxx               |    3 
 14 files changed, 1391 insertions(+)

New commits:
commit b7b3538b60455053edbc697ae3fe888c17f1eaee
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Mon Nov 21 11:44:52 2022 -0500
Commit:     Justin Luth <jl...@mail.com>
CommitDate: Fri Dec 9 12:23:52 2022 +0000

    tdf#151548 vba ContentControls: Add basic word::XContentControl
    
    squashed patch, including Stephan's cleanup patch
    090fafaaaa1d8ccf5f7b17b7b6f2a85b3fafa0c5
    
    This adds basic VBA macro support for accessing
    the modern content controls used for creating forms.
    
    I ran out of time to make it fully functional.
    
    TODO
    -Invalidation: the screen isn't updating the modified results until
     interaction from the user (mouse click, etc.)
    -Unlike FormFields, content controls really depend on having
     Range working. I didn't have time to look into that.
    -I was hoping to check that my approach could accommodate the other
    methods that create a filtered ContentControls object:
     * Document.SelectLinkedControls,
     * Document.SelectUnlinkedControls
     * Range.ContentControls.
     I guess it will be left up to whoever needs these to add the bits
     that will create an appropriate collection for these limited sets.
    -setType: changing one type to another - both LO and Word allow
     limited use of this - depending on the text contents fitting
     the new type.
    
    What works:
    -getByIndex - which probably is the "normal" way to do it,
     since the UI doesn't provide a name/ID; just got via msgbox .ID.
    -full checkbox support (minus the visual invalidation)
    -VBA accepts almost all properties/methods that are requested.
    
    make CppunitTest_sw_macros_test CPPUNIT_TEST_NAME=testVba
    
    If Not ActiveDocument.ContentControls(1).Checked
    If ActiveDocument.ContentControls(2).Checked
    'If ActiveDocument.ContentControls(2).Range.Text <> "$"
    ActiveDocument.SelectContentControlsByTag("checkboxes").Item(1).Checked
     = ActiveDocument.SelectContentControlsByTag("checkboxes").Item(2).Checked
    ActiveDocument.SelectContentControlsByTag("checkboxes")
     .Item(2).SetUncheckedSymbol (8364) '€
    
    With ActiveDocument.SelectContentControlsByTitle("listbox").Item(1)
      If Not .ShowingPlaceholderText
      'If .Range.Text <> "Choose an item."
      If .Type <> wdContentControlDropdownList
    End With
    
    With ActiveDocument.ContentControls.Item(5)
        'If Not .Temporary Then GoTo errorhandler:
        If .Temporary <> False Then GoTo errorhandler:
        If .Tag <> "" Then GoTo errorhandler:
        If .Title <> "" Then GoTo errorhandler:
    End With
    
    With ActiveDocument.ContentControls.Item(6)
        If .Type <> wdContentControlText
        If .MultiLine Then GoTo errorhandler:
        If ActiveDocument.ContentControls.Count <> 7
        .Delete 'Doesn't actually Delete in LO yet - unsafe
       ' If ActiveDocument.ContentControls.Count <> 6
    End With
    
    ' Change to 6 when delete is working safely
    With ActiveDocument.ContentControls.Item(7)
        If .Type <> wdContentControlDate Then GoTo errorhandler:
        .Color = wdColorBlueGray 'unknown to Word 2010
        If .Color <> wdColorBlueGray Then GoTo errorhandler:
        If .DateDisplayFormat <> "mm/yy/dd" Then GoTo errorhandler:
        If .DateCalendarType <> wdCalendarWestern Then GoTo errorhandler:
        If .LockContents <> False Then GoTo errorhandler:
    End With
    
    Change-Id: I1c636f671de81e0283c040a578838a0433ef1f5b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143080
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <jl...@mail.com>
    
    Fix typos
    
    Change-Id: I9d22e4a2140f5195d3829e6289ddf3d2f79b632e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143281
    Tested-by: Jenkins
    Reviewed-by: Julien Nabet <serval2...@yahoo.fr>
    
    cid#1517059 disentangle Improper use of negative value
    
    Change-Id: I907f54cd93d78fe3638e407c70253b94f849eac3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143351
    Tested-by: Jenkins
        Reviewed-by: Caolán McNamara <caol...@redhat.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143419
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Justin Luth <jl...@mail.com>
    Reviewed-by: Tor Lillqvist <t...@collabora.com>

diff --git a/oovbaapi/UnoApi_oovbaapi.mk b/oovbaapi/UnoApi_oovbaapi.mk
index 331cf4937e32..063700762e46 100644
--- a/oovbaapi/UnoApi_oovbaapi.mk
+++ b/oovbaapi/UnoApi_oovbaapi.mk
@@ -833,6 +833,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba/word,\
     WdFarEastLineBreakLevel \
     WdFieldKind \
     WdFieldShading \
+    WdContentControlType \
     WdFieldType \
     WdFindMatch \
     WdFindWrap \
@@ -1057,6 +1058,8 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba/word,\
        XFields \
        XFind \
        XFont \
+       XContentControl \
+       XContentControls \
        XFormField \
        XFormFields \
        XFrame \
diff --git a/oovbaapi/ooo/vba/word/WdContentControlType.idl 
b/oovbaapi/ooo/vba/word/WdContentControlType.idl
new file mode 100644
index 000000000000..d93159a07b66
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/WdContentControlType.idl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+module ooo { module vba { module word {
+    constants WdContentControlType {
+        const long wdContentControlRichText = 0;
+        const long wdContentControlText = 1;
+        const long wdContentControlPicture = 2;
+        const long wdContentControlComboBox = 3;
+        const long wdContentControlDropdownList = 4;
+        const long wdContentControlBuildingBlockGallery = 5;
+        const long wdContentControlDate = 6;
+        const long wdContentControlGroup = 7;
+        const long wdContentControlCheckbox = 8;
+        const long wdContentControlRepeatingSection = 9;
+    };
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/word/XContentControl.idl 
b/oovbaapi/ooo/vba/word/XContentControl.idl
new file mode 100644
index 000000000000..e53846022786
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/XContentControl.idl
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+module ooo {  module vba {  module word {
+
+interface XRange;
+interface XContentControlListEntries;
+interface XContentControl
+{
+    interface ooo::vba::XHelperInterface;
+
+    /// returns or sets whether users can add/remove sections from the 
specified repeating section
+    /// content control by using the user interface.
+    /// Use only with repeating section content controls.
+    [attribute] boolean AllowInsertDeleteSection;
+    /// returns or sets the appearance of the content control.
+    /// 
(wdContentControlBoundingBox/wdContentControlHidden/wdContentControlTags)
+    [attribute] long Appearance;
+    /// returns or sets a String that represents the category for a building 
block content control.
+    [attribute] string BuildingBlockCategory;
+    /// returns or sets a WdBuildingBlockTypes constant that represents the 
type of building block
+    /// for a building block content control.
+    [attribute] long BuildingBlockType;
+    /// returns or sets a Boolean that represents a check box's current state 
(checked/unchecked).
+    [attribute] boolean Checked;
+    /// returns or sets the color of the content control.
+    [attribute] long Color;
+    /// returns or sets a WdCalendarType constant that represents the calendar 
type.
+    [attribute] long DateCalendarType;
+    /// returns or sets a String that represents the format in which dates are 
displayed.
+    [attribute] string DateDisplayFormat;
+    /// returns a WdLanguageID that represents the language format for the 
date displayed.
+    [attribute, readonly] long DateDisplayLocale;
+    /// returns or sets a WdContentControlDateStorageFormat that represents 
the format for storage
+    /// and retrieval of dates when a date content control is bound to the XML 
data store.
+    [attribute] long DateStorageFormat;
+    /// returns or sets a Variant that represents the name of the character 
style to use to format text in a text content control.
+    //[attribute] string DefaultTextStyle;
+    /// returns a ContentControlListEntries collection that represents the 
items
+    /// in a drop-down list content control or in a combo box content control.
+    [attribute, readonly] any DropdownListEntries;
+    /// returns a String that represents the identification for a content 
control.
+    [attribute, readonly] string ID;
+    /// returns the level of the content control—whether the content control 
surrounds text, paragraphs, table cells, or table rows; or if it is inline.
+    /// 
(wdContentControlLevelCell/wdContentControlLevelInline/wdContentControlLevelParagraph/wdContentControlLevelRow)
+    [attribute, readonly] long Level;
+    /// returns or sets whether the user can delete a content control from the 
active document.
+    [attribute] boolean LockContentControl;
+    /// returns or sets whether the user can edit the contents of a content 
control.
+    [attribute] boolean LockContents;
+    /// returns or sets whether a text content control allows multiple lines 
of text.
+    [attribute] boolean MultiLine;
+    /// returns a ContentControl that represents the parent content control 
for a content control that is nested inside a rich-text control or group 
control.
+    //[attribute, readonly] XContentControl ParentContentControl;
+    /// returns a BuildingBlock object that represents the placeholder text 
for a content control.
+    [attribute, readonly] /*WRONG - should be XBuildingBlock*/ string 
PlaceholderText;
+    /// returns a Range that represents the contents of the content control in 
the active document.
+    [attribute, readonly] XRange Range;
+    /// returns the collection of repeating section items in the specified 
repeating section content control.
+    //[attribute, readonly] RepeatingSectionItems;
+    /// returns or sets the name of the repeating section items used in the 
context menu associated
+    /// with the specified repeating section content control.
+    [attribute] string RepeatingSectionItemTitle;
+    /// returns whether the placeholder text for the content control is 
displayed.
+    [attribute, readonly] boolean ShowingPlaceholderText;
+    /// returns or sets a String that represents a value to identify a content 
control.
+    [attribute] string Tag;
+    /// returns or sets whether to remove a content control from the active 
document
+    /// when the user edits the contents of the control.
+    [attribute] boolean Temporary;
+    /// returns or sets a String that represents the title for a content 
control.
+    [attribute] string Title;
+    /// returns or sets a WdContentControlType that represents the type for a 
content control.
+    [attribute] long Type;
+    /// returns an XMLMapping object that represents the mapping of a content 
control to XML data in the data store of a document.
+    //[attribute, readonly] XMLMapping;
+
+    /// Copies the content control from the active document to the Clipboard.
+    void Copy();
+    /// Removes the content control from the active document and moves it to 
the Clipboard.
+    void Cut();
+    /// Deletes the specified content control and the contents of the content 
control.
+    void Delete( [in] /*optional*/ any bDeleteContents );
+    /// Sets the symbol used to represent the checked state of a check box 
content control.
+    void SetCheckedSymbol( [in] long Character, [in] /*optional*/ any sFont );
+    /// Sets the symbol used to represent the unchecked state of a check box 
content control.
+    void SetUnCheckedSymbol( [in] long Character, [in] /*optional*/ any sFont 
);
+    /// Sets the placeholder text that displays until a user enters their own 
text.
+    void SetPlaceholderText( [in] /*optional*/ any BuildingBlock, [in] 
/*optional*/ any Range, [in] /*optional*/ any sFont );
+    /// Removes a group content control. Its children are no longer nested and 
can be freely edited.
+    void Ungroup();
+
+};
+
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/word/XContentControls.idl 
b/oovbaapi/ooo/vba/word/XContentControls.idl
new file mode 100644
index 000000000000..49facea70b40
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/XContentControls.idl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+module ooo {  module vba {  module word {
+
+interface XContentControl;
+interface XContentControls
+{
+    interface ::ooo::vba::XCollection;
+
+    /// Returns a ContentControl object that represents a new 
WdContentControlType added at a range
+    //XContentControl Add( [in] /*optional*/ any Type, [in] /*optional*/ any 
Range ) raises ( com::sun::star::script::BasicErrorException );
+};
+
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/word/XDocument.idl 
b/oovbaapi/ooo/vba/word/XDocument.idl
index 80cbfe977d78..30abf13cfacf 100644
--- a/oovbaapi/ooo/vba/word/XDocument.idl
+++ b/oovbaapi/ooo/vba/word/XDocument.idl
@@ -44,6 +44,9 @@ interface XDocument
     any BuiltInDocumentProperties( [in] any Index );
     any CustomDocumentProperties( [in] any Index );
     any Bookmarks( [in] any Index );
+    any ContentControls( [in] any Index );
+    any SelectContentControlsByTag( [in] any Index );
+    any SelectContentControlsByTitle( [in] any Index );
     any Variables( [in] any Index );
     any Paragraphs( [in] any Index );
     any Styles( [in] any Index ) raises 
(com::sun::star::script::BasicErrorException);
diff --git a/sw/Library_vbaswobj.mk b/sw/Library_vbaswobj.mk
index 408994c3d929..ec4e8f044672 100644
--- a/sw/Library_vbaswobj.mk
+++ b/sw/Library_vbaswobj.mk
@@ -72,6 +72,8 @@ $(eval $(call gb_Library_add_exception_objects,vbaswobj,\
     sw/source/ui/vba/vbacells \
     sw/source/ui/vba/vbacolumn \
     sw/source/ui/vba/vbacolumns \
+    sw/source/ui/vba/vbacontentcontrol \
+    sw/source/ui/vba/vbacontentcontrols \
     sw/source/ui/vba/vbaformfield \
     sw/source/ui/vba/vbaformfields \
     sw/source/ui/vba/vbaformfieldcheckbox \
diff --git a/sw/qa/core/data/docm/testModernVBA.docm 
b/sw/qa/core/data/docm/testModernVBA.docm
new file mode 100644
index 000000000000..faa85768884b
Binary files /dev/null and b/sw/qa/core/data/docm/testModernVBA.docm differ
diff --git a/sw/qa/core/macros-test.cxx b/sw/qa/core/macros-test.cxx
index dc571ed928af..43b47198243a 100644
--- a/sw/qa/core/macros-test.cxx
+++ b/sw/qa/core/macros-test.cxx
@@ -113,6 +113,10 @@ void SwMacrosTest::testVba()
         {
             OUString("testVBA.docm"),
             
OUString("vnd.sun.Star.script:Project.ThisDocument.testAll?language=Basic&location=document")
+        },
+        {
+            OUString("testModernVBA.docm"),
+            
OUString("vnd.sun.Star.script:Project.ThisDocument.testAll?language=Basic&location=document")
         }
     };
     for ( size_t  i=0; i<SAL_N_ELEMENTS( testInfo ); ++i )
diff --git a/sw/source/ui/vba/vbacontentcontrol.cxx 
b/sw/source/ui/vba/vbacontentcontrol.cxx
new file mode 100644
index 000000000000..3d51d8bd4448
--- /dev/null
+++ b/sw/source/ui/vba/vbacontentcontrol.cxx
@@ -0,0 +1,754 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <ooo/vba/word/WdColor.hpp>
+#include <ooo/vba/word/WdCalendarType.hpp>
+#include <ooo/vba/word/WdContentControlType.hpp>
+#include <ooo/vba/word/WdLanguageID.hpp>
+
+#include <sal/log.hxx>
+
+#include <ndtxt.hxx>
+
+#include "vbacontentcontrol.hxx"
+//#include "vbacontentcontroldropdownlistentries.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+/**
+ * Content controls are the modern version of FormFields, providing inline 
functionality similar
+ * to that of ActiveX form controls. Individual content controls may contain 
contents
+ * such as dates, lists, or paragraphs of formatted text.
+ *
+ * Not all functions are applicable to each type of control, so use getType 
verification liberally.
+ */
+SwVbaContentControl::SwVbaContentControl(const 
uno::Reference<XHelperInterface>& rParent,
+                                         const 
uno::Reference<uno::XComponentContext>& rContext,
+                                         const 
uno::Reference<text::XTextDocument>& xTextDocument,
+                                         SwTextContentControl& rContentControl)
+    : SwVbaContentControl_BASE(rParent, rContext)
+    , mxTextDocument(xTextDocument)
+    , m_rCC(rContentControl)
+{
+}
+
+SwVbaContentControl::~SwVbaContentControl() {}
+
+sal_Bool SwVbaContentControl::getAllowInsertDeleteSection()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getAllowInsertDeleteSection 
stub");
+    return false;
+}
+
+void SwVbaContentControl::setAllowInsertDeleteSection(sal_Bool /*bSet*/)
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::setAllowInsertDeleteSection 
stub");
+}
+
+sal_Int32 SwVbaContentControl::getAppearance()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getAppearance stub");
+    // wdContentControlBoundingBox / wdContentControlHidden / 
wdContentControlTags
+    return 0;
+}
+
+void SwVbaContentControl::setAppearance(sal_Int32 nSet)
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::setAppearance[" << nSet << "] 
stub");
+}
+
+OUString SwVbaContentControl::getBuildingBlockCategory()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getBuildingBlockCategory stub");
+    return OUString();
+}
+
+void SwVbaContentControl::setBuildingBlockCategory(const OUString& sSet)
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::setBuildingBlockCategory[" << 
sSet << "] stub");
+}
+
+sal_Int32 SwVbaContentControl::getBuildingBlockType()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getBuildingBlockType stub");
+    // returns a WdBuildingBlockTypes that represents the type of building 
block
+    return 0;
+}
+
+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();
+}
+
+void SwVbaContentControl::setChecked(sal_Bool bSet)
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+    if (pCC->GetCheckbox() && pCC->GetChecked() != static_cast<bool>(bSet))
+    {
+        pCC->SetChecked(bSet);
+        //pCC->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();
+    if (sColor == "wdColorAutomatic")
+        return word::WdColor::wdColorAutomatic;
+    if (sColor == "wdColorBlack")
+        return word::WdColor::wdColorBlack;
+    if (sColor == "wdColorBlue")
+        return word::WdColor::wdColorBlue;
+    if (sColor == "wdColorBlueGray")
+        return word::WdColor::wdColorBlueGray;
+    if (sColor == "wdColorBrightGreen")
+        return word::WdColor::wdColorBrightGreen;
+    if (sColor == "wdColorBrown")
+        return word::WdColor::wdColorBrown;
+    if (sColor == "wdColorDarkBlue")
+        return word::WdColor::wdColorDarkBlue;
+    if (sColor == "wdColorDarkGreen")
+        return word::WdColor::wdColorDarkGreen;
+    if (sColor == "wdColorDarkRed")
+        return word::WdColor::wdColorDarkRed;
+    if (sColor == "wdColorDarkTeal")
+        return word::WdColor::wdColorDarkTeal;
+    if (sColor == "wdColorDarkYellow")
+        return word::WdColor::wdColorDarkYellow;
+    if (sColor == "wdColorGold")
+        return word::WdColor::wdColorGold;
+    if (sColor == "wdColorGray05")
+        return word::WdColor::wdColorGray05;
+    if (sColor == "wdColorGray10")
+        return word::WdColor::wdColorGray10;
+    if (sColor == "wdColorGray125")
+        return word::WdColor::wdColorGray125;
+    if (sColor == "wdColorGray15")
+        return word::WdColor::wdColorGray15;
+    if (sColor == "wdColorGray20")
+        return word::WdColor::wdColorGray20;
+    if (sColor == "wdColorGray25")
+        return word::WdColor::wdColorGray25;
+    if (sColor == "wdColorGray30")
+        return word::WdColor::wdColorGray30;
+    if (sColor == "wdColorGray35")
+        return word::WdColor::wdColorGray35;
+    if (sColor == "wdColorGray375")
+        return word::WdColor::wdColorGray375;
+    if (sColor == "wdColorGray40")
+        return word::WdColor::wdColorGray40;
+    if (sColor == "wdColorGray45")
+        return word::WdColor::wdColorGray45;
+    if (sColor == "wdColorGray50")
+        return word::WdColor::wdColorGray50;
+    if (sColor == "wdColorGray55")
+        return word::WdColor::wdColorGray55;
+    if (sColor == "wdColorGray60")
+        return word::WdColor::wdColorGray60;
+    if (sColor == "wdColorGray625")
+        return word::WdColor::wdColorGray625;
+    if (sColor == "wdColorGray65")
+        return word::WdColor::wdColorGray65;
+    if (sColor == "wdColorGray70")
+        return word::WdColor::wdColorGray70;
+    if (sColor == "wdColorGray75")
+        return word::WdColor::wdColorGray75;
+    if (sColor == "wdColorGray80")
+        return word::WdColor::wdColorGray80;
+    if (sColor == "wdColorGray85")
+        return word::WdColor::wdColorGray85;
+    if (sColor == "wdColorGray875")
+        return word::WdColor::wdColorGray875;
+    if (sColor == "wdColorGray90")
+        return word::WdColor::wdColorGray90;
+    if (sColor == "wdColorGray95")
+        return word::WdColor::wdColorGray95;
+    if (sColor == "wdColorGreen")
+        return word::WdColor::wdColorGreen;
+    if (sColor == "wdColorIndigo")
+        return word::WdColor::wdColorIndigo;
+    if (sColor == "wdColorLavender")
+        return word::WdColor::wdColorLavender;
+    if (sColor == "wdColorLightBlue")
+        return word::WdColor::wdColorLightBlue;
+    if (sColor == "wdColorLightGreen")
+        return word::WdColor::wdColorLightGreen;
+    if (sColor == "wdColorLightOrange")
+        return word::WdColor::wdColorLightOrange;
+    if (sColor == "wdColorLightTurquoise")
+        return word::WdColor::wdColorLightTurquoise;
+    if (sColor == "wdColorLightYellow")
+        return word::WdColor::wdColorLightYellow;
+    if (sColor == "wdColorLime")
+        return word::WdColor::wdColorLime;
+    if (sColor == "wdColorOliveGreen")
+        return word::WdColor::wdColorOliveGreen;
+    if (sColor == "wdColorOrange")
+        return word::WdColor::wdColorOrange;
+    if (sColor == "wdColorPaleBlue")
+        return word::WdColor::wdColorPaleBlue;
+    if (sColor == "wdColorPink")
+        return word::WdColor::wdColorPink;
+    if (sColor == "wdColorPlum")
+        return word::WdColor::wdColorPlum;
+    if (sColor == "wdColorRed")
+        return word::WdColor::wdColorRed;
+    if (sColor == "wdColorRose")
+        return word::WdColor::wdColorRose;
+    if (sColor == "wdColorSeaGreen")
+        return word::WdColor::wdColorSeaGreen;
+    if (sColor == "wdColorSkyBlue")
+        return word::WdColor::wdColorSkyBlue;
+    if (sColor == "wdColorTan")
+        return word::WdColor::wdColorTan;
+    if (sColor == "wdColorTeal")
+        return word::WdColor::wdColorTeal;
+    if (sColor == "wdColorTurquoise")
+        return word::WdColor::wdColorTurquoise;
+    if (sColor == "wdColorViolet")
+        return word::WdColor::wdColorViolet;
+    if (sColor == "wdColorWhite")
+        return word::WdColor::wdColorWhite;
+    if (sColor == "wdColorYellow")
+        return word::WdColor::wdColorYellow;
+
+    return word::WdColor::wdColorBlack;
+}
+
+void SwVbaContentControl::setColor(sal_Int32 nWdColor)
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+
+    switch (nWdColor)
+    {
+        case word::WdColor::wdColorAqua:
+            pCC->SetColor("wdColorAqua");
+            break;
+        case word::WdColor::wdColorAutomatic:
+            pCC->SetColor("wdColorAutomatic");
+            break;
+        case word::WdColor::wdColorBlack:
+            pCC->SetColor("wdColorBlack");
+            break;
+        case word::WdColor::wdColorBlue:
+            pCC->SetColor("wdColorBlue");
+            break;
+        case word::WdColor::wdColorBlueGray:
+            pCC->SetColor("wdColorBlueGray");
+            break;
+        case word::WdColor::wdColorBrightGreen:
+            pCC->SetColor("wdColorBrightGreen");
+            break;
+        case word::WdColor::wdColorBrown:
+            pCC->SetColor("wdColorBrown");
+            break;
+        case word::WdColor::wdColorDarkBlue:
+            pCC->SetColor("wdColorDarkBlue");
+            break;
+        case word::WdColor::wdColorDarkGreen:
+            pCC->SetColor("wdColorDarkGreen");
+            break;
+        case word::WdColor::wdColorDarkRed:
+            pCC->SetColor("wdColorDarkRed");
+            break;
+        case word::WdColor::wdColorDarkTeal:
+            pCC->SetColor("wdColorDarkTeal");
+            break;
+        case word::WdColor::wdColorDarkYellow:
+            pCC->SetColor("wdColorDarkYellow");
+            break;
+        case word::WdColor::wdColorGold:
+            pCC->SetColor("wdColorGold");
+            break;
+        case word::WdColor::wdColorGray05:
+            pCC->SetColor("wdColorGray05");
+            break;
+        case word::WdColor::wdColorGray10:
+            pCC->SetColor("wdColorGray10");
+            break;
+        case word::WdColor::wdColorGray125:
+            pCC->SetColor("wdColorGray125");
+            break;
+        case word::WdColor::wdColorGray15:
+            pCC->SetColor("wdColorGray15");
+            break;
+        case word::WdColor::wdColorGray20:
+            pCC->SetColor("wdColorGray20");
+            break;
+        case word::WdColor::wdColorGray25:
+            pCC->SetColor("wdColorGray25");
+            break;
+        case word::WdColor::wdColorGray30:
+            pCC->SetColor("wdColorGray30");
+            break;
+        case word::WdColor::wdColorGray35:
+            pCC->SetColor("wdColorGray35");
+            break;
+        case word::WdColor::wdColorGray375:
+            pCC->SetColor("wdColorGray375");
+            break;
+        case word::WdColor::wdColorGray40:
+            pCC->SetColor("wdColorGray40");
+            break;
+        case word::WdColor::wdColorGray45:
+            pCC->SetColor("wdColorGray45");
+            break;
+        case word::WdColor::wdColorGray50:
+            pCC->SetColor("wdColorGray50");
+            break;
+        case word::WdColor::wdColorGray55:
+            pCC->SetColor("wdColorGray55");
+            break;
+        case word::WdColor::wdColorGray60:
+            pCC->SetColor("wdColorGray60");
+            break;
+        case word::WdColor::wdColorGray625:
+            pCC->SetColor("wdColorGray625");
+            break;
+        case word::WdColor::wdColorGray65:
+            pCC->SetColor("wdColorGray65");
+            break;
+        case word::WdColor::wdColorGray70:
+            pCC->SetColor("wdColorGray70");
+            break;
+        case word::WdColor::wdColorGray75:
+            pCC->SetColor("wdColorGray75");
+            break;
+        case word::WdColor::wdColorGray80:
+            pCC->SetColor("wdColorGray80");
+            break;
+        case word::WdColor::wdColorGray85:
+            pCC->SetColor("wdColorGray85");
+            break;
+        case word::WdColor::wdColorGray875:
+            pCC->SetColor("wdColorGray875");
+            break;
+        case word::WdColor::wdColorGray90:
+            pCC->SetColor("wdColorGray90");
+            break;
+        case word::WdColor::wdColorGray95:
+            pCC->SetColor("wdColorGray95");
+            break;
+        case word::WdColor::wdColorGreen:
+            pCC->SetColor("wdColorGreen");
+            break;
+        case word::WdColor::wdColorIndigo:
+            pCC->SetColor("wdColorIndigo");
+            break;
+        case word::WdColor::wdColorLavender:
+            pCC->SetColor("wdColorLavender");
+            break;
+        case word::WdColor::wdColorLightBlue:
+            pCC->SetColor("wdColorLightBlue");
+            break;
+        case word::WdColor::wdColorLightGreen:
+            pCC->SetColor("wdColorLightGreen");
+            break;
+        case word::WdColor::wdColorLightOrange:
+            pCC->SetColor("wdColorLightOrange");
+            break;
+        case word::WdColor::wdColorLightTurquoise:
+            pCC->SetColor("wdColorLightTurquoise");
+            break;
+        case word::WdColor::wdColorLightYellow:
+            pCC->SetColor("wdColorLightYellow");
+            break;
+        case word::WdColor::wdColorLime:
+            pCC->SetColor("wdColorLime");
+            break;
+        case word::WdColor::wdColorOliveGreen:
+            pCC->SetColor("wdColorOliveGreen");
+            break;
+        case word::WdColor::wdColorOrange:
+            pCC->SetColor("wdColorOrange");
+            break;
+        case word::WdColor::wdColorPaleBlue:
+            pCC->SetColor("wdColorPaleBlue");
+            break;
+        case word::WdColor::wdColorPink:
+            pCC->SetColor("wdColorPink");
+            break;
+        case word::WdColor::wdColorPlum:
+            pCC->SetColor("wdColorPlum");
+            break;
+        case word::WdColor::wdColorRed:
+            pCC->SetColor("wdColorRed");
+            break;
+        case word::WdColor::wdColorRose:
+            pCC->SetColor("wdColorRose");
+            break;
+        case word::WdColor::wdColorSeaGreen:
+            pCC->SetColor("wdColorSeaGreen");
+            break;
+        case word::WdColor::wdColorSkyBlue:
+            pCC->SetColor("wdColorSkyBlue");
+            break;
+        case word::WdColor::wdColorTan:
+            pCC->SetColor("wdColorTan");
+            break;
+        case word::WdColor::wdColorTeal:
+            pCC->SetColor("wdColorTeal");
+            break;
+        case word::WdColor::wdColorTurquoise:
+            pCC->SetColor("wdColorTurquoise");
+            break;
+        case word::WdColor::wdColorViolet:
+            pCC->SetColor("wdColorViolet");
+            break;
+        case word::WdColor::wdColorWhite:
+            pCC->SetColor("wdColorWhite");
+            break;
+        default:;
+    }
+}
+
+sal_Int32 SwVbaContentControl::getDateCalendarType()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getDateCalendarType stub");
+    // returns a WdCalendarTypes that represents the type of building block
+    return word::WdCalendarType::wdCalendarWestern;
+}
+
+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();
+}
+
+void SwVbaContentControl::setDateDisplayFormat(const OUString& sSet)
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+    pCC->SetDateFormat(sSet);
+}
+
+sal_Int32 SwVbaContentControl::getDateStorageFormat()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getDateStorageFormat stub");
+    // returns a WdContentControlDateStorageFormat when bound to the XML data 
store.
+    return 0;
+}
+
+void SwVbaContentControl::setDateStorageFormat(sal_Int32 nSet)
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::setDateStorageFormat[" << nSet << 
"] stub");
+}
+
+sal_Int32 SwVbaContentControl::getDateDisplayLocale()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getDateDisplayLocale stub");
+    // returns a WdLanguageID that represents the language format for a date 
content control.
+    return word::WdLanguageID::wdEnglishUS;
+}
+
+uno::Any SwVbaContentControl::getDropdownListEntries()
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+    //if (!pCC->GetDropDown() && !pCC->GetComboBox())
+    //    return uno::Any();
+    //return uno::Any(uno::Reference<XCollection>(
+    //    new SwVbaContentControlDropDownListEntries(this, mxContext, m_rCC)));
+    return uno::Any();
+}
+
+OUString SwVbaContentControl::getID()
+{
+    //const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
+    //return OUString::number(static_cast<sal_uInt32>(pCC->GetId()));
+    return OUString();
+}
+
+sal_Int32 SwVbaContentControl::getLevel()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getLevel stub");
+    // returns a WdContentControlLevel
+    return 0;
+}
+
+sal_Bool SwVbaContentControl::getLockContentControl()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getLockContentControl stub");
+    // returns whether the user can delete a content control from the active 
document.
+    return true;
+}
+
+void SwVbaContentControl::setLockContentControl(sal_Bool /*bSet*/)
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::setLockContentControl stub");
+}
+
+sal_Bool SwVbaContentControl::getLockContents()
+{
+    // Pseudo-implementation - the need for locking in a form would be very 
rare.
+    // LO uses this for internal purposes. Only expose it to VBA when safe.
+    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
+    // 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)
+    {
+        return false;
+    }
+
+    return pCC->GetReadWrite();
+}
+
+void SwVbaContentControl::setLockContents(sal_Bool bSet)
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+    // 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)
+    {
+        return;
+    }
+    pCC->SetReadWrite(bSet);
+}
+
+sal_Bool SwVbaContentControl::getMultiLine()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getMultiLine stub");
+    return false;
+}
+
+void SwVbaContentControl::setMultiLine(sal_Bool /*bSet*/)
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::setMultiLine stub");
+}
+
+OUString SwVbaContentControl::getPlaceholderText()
+{
+    // return 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();
+}
+
+uno::Reference<word::XRange> SwVbaContentControl::getRange()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getRange stub");
+    return uno::Reference<word::XRange>();
+}
+
+OUString SwVbaContentControl::getRepeatingSectionItemTitle()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getRepeatingSectionItemTitle 
stub");
+    return OUString();
+}
+
+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();
+}
+
+void SwVbaContentControl::setTag(const OUString& rSet)
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+    return pCC->SetTag(rSet);
+}
+
+sal_Bool SwVbaContentControl::getTemporary()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::getTemporary stub");
+    // Is content control removed when user edits (one time use)? Not 
implemented in LO.
+    return false;
+}
+
+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();
+}
+
+void SwVbaContentControl::setTitle(const OUString& rSet)
+{
+    std::shared_ptr<SwContentControl> pCC = 
m_rCC.GetContentControl().GetContentControl();
+    return pCC->SetAlias(rSet);
+}
+
+sal_Int32 SwVbaContentControl::getType()
+{
+    const std::shared_ptr<SwContentControl>& pCC = 
m_rCC.GetContentControl().GetContentControl();
+    SwContentControlType eType = pCC->GetType();
+    sal_Int32 eVbaType = word::WdContentControlType::wdContentControlRichText;
+
+    switch (eType)
+    {
+        case SwContentControlType::CHECKBOX:
+            eVbaType = word::WdContentControlType::wdContentControlCheckbox;
+            break;
+        case SwContentControlType::DROP_DOWN_LIST:
+            eVbaType = 
word::WdContentControlType::wdContentControlDropdownList;
+            break;
+        case SwContentControlType::PICTURE:
+            eVbaType = word::WdContentControlType::wdContentControlPicture;
+            break;
+        case SwContentControlType::DATE:
+            eVbaType = word::WdContentControlType::wdContentControlDate;
+            break;
+        case SwContentControlType::PLAIN_TEXT:
+            eVbaType = word::WdContentControlType::wdContentControlText;
+            break;
+        case SwContentControlType::COMBO_BOX:
+            eVbaType = word::WdContentControlType::wdContentControlComboBox;
+            break;
+        case SwContentControlType::RICH_TEXT:
+        default:;
+    }
+    return eVbaType;
+}
+
+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)
+    //     {
+    //         case word::WdContentControlType::wdContentControlCheckbox:
+    //             eType = SwContentControlType::CHECKBOX;
+    //             break;
+    //         case word::WdContentControlType::wdContentControlDropdownList:
+    //             eType = SwContentControlType::DROP_DOWN_LIST;
+    //             break;
+    //         case word::WdContentControlType::wdContentControlPicture:
+    //             eType = SwContentControlType::PICTURE;
+    //             break;
+    //         case word::WdContentControlType::wdContentControlDate:
+    //             eType = SwContentControlType::DATE;
+    //             break;
+    //         case word::WdContentControlType::wdContentControlText:
+    //             eType = SwContentControlType::PLAIN_TEXT;
+    //             break;
+    //         case word::WdContentControlType::wdContentControlComboBox:
+    //             eType = SwContentControlType::COMBO_BOX;
+    //             break;
+    //         case word::WdContentControlType::wdContentControlRichText:
+    //         default:;
+    //     }
+    //     pCC->SetType(eType);
+}
+
+void SwVbaContentControl::Copy()
+{
+    SAL_INFO("sw.vba", "SwVbaContentControl::Copy[" << getID() << "] stub");
+}
+
+void SwVbaContentControl::Cut()
+{
+    SAL_INFO("sw.vba",
+             "SwVbaContentControl::Cut[" << getID() << "], but missing sending 
to clipboard");
+
+    Delete(uno::Any(false));
+}
+
+void SwVbaContentControl::Delete(const uno::Any& DeleteContents)
+{
+    bool bDeleteContents = false;
+    DeleteContents >>= bDeleteContents;
+    SAL_INFO("sw.vba", "SwVbaContentControl::Delete[" << DeleteContents << "] 
stub");
+    //m_rCC.ChgTextNode(nullptr); // works, but crashes on UI touch - probably 
requires invalidation
+}
+
+void SwVbaContentControl::SetCheckedSymbol(sal_Int32 Character, const 
uno::Any& Font)
+{
+    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)));
+    //if (getChecked())
+    //    pCC->Invalidate();
+}
+
+void SwVbaContentControl::SetUnCheckedSymbol(sal_Int32 Character, const 
uno::Any& Font)
+{
+    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)));
+    //if (!getChecked())
+    //    pCC->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.
+    }
+    else if (Range.hasValue())
+    {
+        // Set placeholder text to the contents of the Range, however you do 
that.
+    }
+    else if (Text.hasValue())
+    {
+        // Set placeholder text to the provided string
+    }
+    else
+    {
+        // Remove placeholder text.
+        pCC->SetPlaceholderDocPart("");
+    }
+    //if (getShowingPlaceholderText())
+    //{
+    //    if (!pCC->GetCheckbox())
+    //      pCC->Invalidate();
+    //    // Ensure that invalidation doesn't turn off showing placeholder as 
true
+    //    pCC->SetShowingPlaceHolder(true);
+    //}
+}
+
+void SwVbaContentControl::Ungroup() { SAL_INFO("sw.vba", 
"SwVbaContentControl::UnGroup stub"); }
+
+OUString SwVbaContentControl::getServiceImplName() { return 
"SwVbaContentControl"; }
+
+uno::Sequence<OUString> SwVbaContentControl::getServiceNames()
+{
+    static uno::Sequence<OUString> const aServiceNames{ 
"ooo.vba.word.ContentControl" };
+    return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbacontentcontrol.hxx 
b/sw/source/ui/vba/vbacontentcontrol.hxx
new file mode 100644
index 000000000000..2d3c2ee56ad1
--- /dev/null
+++ b/sw/source/ui/vba/vbacontentcontrol.hxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <ooo/vba/word/XContentControl.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+#include <textcontentcontrol.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl<ooo::vba::word::XContentControl> 
SwVbaContentControl_BASE;
+
+class SwVbaContentControl : public SwVbaContentControl_BASE
+{
+private:
+    css::uno::Reference<css::text::XTextDocument> mxTextDocument;
+    SwTextContentControl& m_rCC;
+
+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);
+    ~SwVbaContentControl() override;
+
+    // XContentControl Properties
+    sal_Bool SAL_CALL getAllowInsertDeleteSection() override;
+    void SAL_CALL setAllowInsertDeleteSection(sal_Bool bSet) override;
+
+    sal_Int32 SAL_CALL getAppearance() override;
+    void SAL_CALL setAppearance(sal_Int32 nSet) override;
+
+    OUString SAL_CALL getBuildingBlockCategory() override;
+    void SAL_CALL setBuildingBlockCategory(const OUString& sSet) override;
+
+    sal_Int32 SAL_CALL getBuildingBlockType() override;
+    void SAL_CALL setBuildingBlockType(sal_Int32 nSet) override;
+
+    sal_Bool SAL_CALL getChecked() override;
+    void SAL_CALL setChecked(sal_Bool bSet) override;
+
+    // returns or sets a WdColor (@since after 2010 I assume)
+    sal_Int32 SAL_CALL getColor() override;
+    void SAL_CALL setColor(sal_Int32 nSet) override;
+
+    sal_Int32 SAL_CALL getDateCalendarType() override;
+    void SAL_CALL setDateCalendarType(sal_Int32 nSet) override;
+
+    OUString SAL_CALL getDateDisplayFormat() override;
+    void SAL_CALL setDateDisplayFormat(const OUString& sSet) override;
+
+    sal_Int32 SAL_CALL getDateDisplayLocale() override;
+
+    sal_Int32 SAL_CALL getDateStorageFormat() override;
+    void SAL_CALL setDateStorageFormat(sal_Int32 nSet) override;
+
+    css::uno::Any SAL_CALL getDropdownListEntries() override;
+
+    // This is an integer used as a unique identifier string
+    OUString SAL_CALL getID() override;
+
+    sal_Int32 SAL_CALL getLevel() override;
+
+    // returns or sets if the user can delete the control
+    sal_Bool SAL_CALL getLockContentControl() override;
+    void SAL_CALL setLockContentControl(sal_Bool bSet) override;
+
+    // returns or sets if the user can edit the contents (i.e. read-only flag)
+    sal_Bool SAL_CALL getLockContents() override;
+    void SAL_CALL setLockContents(sal_Bool bSet) override;
+
+    sal_Bool SAL_CALL getMultiLine() override;
+    void SAL_CALL setMultiLine(sal_Bool bSet) override;
+
+    // WRONG- THIS SHOULD RETURN XBUILDINGBLOCK
+    OUString SAL_CALL getPlaceholderText() override;
+
+    sal_Bool SAL_CALL getShowingPlaceholderText() override;
+
+    OUString SAL_CALL getRepeatingSectionItemTitle() override;
+    void SAL_CALL setRepeatingSectionItemTitle(const OUString& rSet) override;
+
+    css::uno::Reference<ooo::vba::word::XRange> SAL_CALL getRange() override;
+
+    OUString SAL_CALL getTag() override;
+    void SAL_CALL setTag(const OUString& rSet) override;
+
+    // returns or sets if the control is removed after accepting user change 
(i.e. control -> text)
+    sal_Bool SAL_CALL getTemporary() override;
+    void SAL_CALL setTemporary(sal_Bool bSet) override;
+
+    OUString SAL_CALL getTitle() override;
+    void SAL_CALL setTitle(const OUString& rSet) override;
+
+    // returns or sets a WdContentControlType that represents the type for a 
content control.
+    sal_Int32 SAL_CALL getType() override;
+    void SAL_CALL setType(sal_Int32 nSet) override;
+
+    // XContentControl Methods
+
+    // Copies the content control from the active document to the Clipboard.
+    // Retrieve from the clipboard using the Paste method of the Selection 
object
+    // or of the Range object, or use the Paste function from within Microsoft 
Word.
+    void SAL_CALL Copy() override;
+
+    // Removes the control from the active document and moves it to the 
Clipboard.
+    void SAL_CALL Cut() override;
+
+    // Specifies whether to delete the contents of the content control. The 
default value is False.
+    // True removes both the content control and its contents.
+    // False removes the control but leaves the contents of the content 
control in the document.
+    void SAL_CALL Delete(const css::uno::Any& bDeleteContents) override;
+
+    // Set the Unicode character used to display the checked state.
+    void SAL_CALL SetCheckedSymbol(sal_Int32 Character, const css::uno::Any& 
sFont) override;
+
+    // Set the Unicode character used to display the unchecked state.
+    void SAL_CALL SetUnCheckedSymbol(sal_Int32 Character, const css::uno::Any& 
sFont) override;
+
+    // Sets the placeholder text that displays until a user enters their own 
text.
+    // Only one of the parameters is used when specifying placeholder text.
+    // If more than one parameter is provided, use the text specified in the 
first parameter.
+    // If all parameters are omitted, the placeholder text is blank.
+    void SAL_CALL SetPlaceholderText(const css::uno::Any& BuildingBlock, const 
css::uno::Any& Range,
+                                     const css::uno::Any& sText) override;
+
+    void SAL_CALL Ungroup() override;
+
+    // XHelperInterface
+    OUString getServiceImplName() override;
+    css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbacontentcontrols.cxx 
b/sw/source/ui/vba/vbacontentcontrols.cxx
new file mode 100644
index 000000000000..4ef73f5d196e
--- /dev/null
+++ b/sw/source/ui/vba/vbacontentcontrols.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <textcontentcontrol.hxx>
+
+#include "vbacontentcontrol.hxx"
+#include "vbacontentcontrols.hxx"
+#include "wordvbahelper.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+// Helper function to access the content controls
+// @param rIndex
+//        [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*
+lcl_getContentControl(std::u16string_view sName, std::u16string_view sTag,
+                      std::u16string_view sTitle, sal_Int32& rIndex,
+                      const uno::Reference<text::XTextDocument>& xTextDocument,
+                      uno::Sequence<OUString>* pElementNames = nullptr)
+{
+    SwDoc* pDoc = word::getDocShell(xTextDocument)->GetDoc();
+    if (!pDoc)
+        return nullptr;
+
+    assert(sTag.empty() || sTitle.empty()); // only one grouping at a time is 
allowed
+
+    SwTextContentControl* pControl = nullptr;
+    std::vector<OUString> vElementNames;
+    SwContentControlManager& rManager = pDoc->GetContentControlManager();
+    const size_t nLen = rManager.GetCount();
+    if (!pElementNames && rIndex > 0 && sName.empty() && sTag.empty() && 
sTitle.empty())
+    {
+        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);
+        else
+            rIndex = nLen;
+    }
+    else
+    {
+        // loop through everything collecting names, filtering by Tag/Title
+        sal_Int32 nCounter = 0;
+        for (size_t i = 0; i < nLen; ++i)
+        {
+            pControl = rManager.Get(i);
+            if (!sTag.empty()
+                && sTag != 
pControl->GetContentControl().GetContentControl()->GetTag())
+                continue;
+            if (!sTitle.empty()
+                && sTitle != 
pControl->GetContentControl().GetContentControl()->GetAlias())
+                continue;
+
+            //OUString sID = OUString::number(static_cast<sal_uInt32>(
+            //    pControl->GetContentControl().GetContentControl()->GetId()));
+            //if (!sName.empty() && sName != sID)
+            //    continue;
+
+            //if (pElementNames)
+            //    vElementNames.push_back(sID);
+
+            if (rIndex == nCounter /*|| !sName.empty()*/)
+                break;
+
+            pControl = nullptr;
+            ++nCounter;
+        }
+        rIndex = nCounter;
+    }
+    if (pElementNames)
+        *pElementNames = comphelper::containerToSequence(vElementNames);
+    return pControl;
+}
+
+namespace
+{
+class ContentControlsEnumWrapper : public EnumerationHelper_BASE
+{
+    uno::Reference<container::XIndexAccess> mxIndexAccess;
+    sal_Int32 nIndex;
+
+public:
+    explicit 
ContentControlsEnumWrapper(uno::Reference<container::XIndexAccess> xIndexAccess)
+        : mxIndexAccess(std::move(xIndexAccess))
+        , nIndex(0)
+    {
+    }
+
+    sal_Bool SAL_CALL hasMoreElements() override { return (nIndex < 
mxIndexAccess->getCount()); }
+
+    uno::Any SAL_CALL nextElement() override
+    {
+        if (nIndex < mxIndexAccess->getCount())
+        {
+            return mxIndexAccess->getByIndex(nIndex++);
+        }
+        throw container::NoSuchElementException();
+    }
+};
+
+class ContentControlCollectionHelper
+    : public ::cppu::WeakImplHelper<container::XNameAccess, 
container::XIndexAccess,
+                                    container::XEnumerationAccess>
+{
+private:
+    uno::Reference<XHelperInterface> mxParent;
+    uno::Reference<uno::XComponentContext> mxContext;
+    uno::Reference<text::XTextDocument> mxTextDocument;
+    const OUString m_sTag;
+    const OUString m_sTitle;
+    SwTextContentControl* m_pCache;
+
+public:
+    /// @throws css::uno::RuntimeException
+    ContentControlCollectionHelper(uno::Reference<ov::XHelperInterface> 
xParent,
+                                   uno::Reference<uno::XComponentContext> 
xContext,
+                                   uno::Reference<text::XTextDocument> 
xTextDocument,
+                                   const OUString& rTag, const OUString& 
rTitle)
+
+        : mxParent(std::move(xParent))
+        , mxContext(std::move(xContext))
+        , mxTextDocument(std::move(xTextDocument))
+        , m_sTag(rTag)
+        , m_sTitle(rTitle)
+        , m_pCache(nullptr)
+    {
+    }
+
+    // XIndexAccess
+    sal_Int32 SAL_CALL getCount() override
+    {
+        sal_Int32 nCount = SAL_MAX_INT32;
+        lcl_getContentControl(u"", m_sTag, m_sTitle, nCount, mxTextDocument);
+        return nCount == SAL_MAX_INT32 || nCount < 0 ? 0 : nCount;
+    }
+
+    uno::Any SAL_CALL getByIndex(sal_Int32 Index) override
+    {
+        m_pCache = lcl_getContentControl(u"", m_sTag, m_sTitle, Index, 
mxTextDocument);
+        if (!m_pCache)
+            throw lang::IndexOutOfBoundsException();
+
+        return uno::Any(uno::Reference<word::XContentControl>(
+            new SwVbaContentControl(mxParent, mxContext, mxTextDocument, 
*m_pCache)));
+    }
+
+    // XNameAccess
+    uno::Sequence<OUString> SAL_CALL getElementNames() override
+    {
+        sal_Int32 nCount = SAL_MAX_INT32;
+        uno::Sequence<OUString> aSeq;
+        lcl_getContentControl(u"", m_sTag, m_sTitle, nCount, mxTextDocument, 
&aSeq);
+        return aSeq;
+    }
+
+    uno::Any SAL_CALL getByName(const OUString& aName) override
+    {
+        if (!hasByName(aName))
+            throw container::NoSuchElementException();
+
+        return uno::Any(uno::Reference<word::XContentControl>(
+            new SwVbaContentControl(mxParent, mxContext, mxTextDocument, 
*m_pCache)));
+    }
+
+    sal_Bool SAL_CALL hasByName(const OUString& aName) override
+    {
+        sal_Int32 nCount = -1;
+        m_pCache = lcl_getContentControl(aName, m_sTag, m_sTitle, nCount, 
mxTextDocument);
+        return m_pCache != nullptr;
+    }
+
+    // XElementAccess
+    uno::Type SAL_CALL getElementType() override
+    {
+        return cppu::UnoType<word::XContentControl>::get();
+    }
+
+    sal_Bool SAL_CALL hasElements() override { return getCount() != 0; }
+
+    // XEnumerationAccess
+    uno::Reference<container::XEnumeration> SAL_CALL createEnumeration() 
override
+    {
+        return new ContentControlsEnumWrapper(this);
+    }
+};
+}
+
+/**
+ * Content Controls can be accessed and filtered in many different ways.
+ * Surprisingly however, there is no clear, descriptive "by name" access.
+ * Instead, each content control (probably) has a unique _signed-integer_ 
identifier,
+ * which can be passed to Item() as a float or _unsigned-integer_ string
+ * (to differentiate it from getByIndex).
+ *
+ * Index access can be filtered by Tag, Title, Range, and XML link.
+ * TODO: add filtering for Range, SelectLinkedControls, SelectUnlinkedControls
+ */
+SwVbaContentControls::SwVbaContentControls(const 
uno::Reference<XHelperInterface>& xParent,
+                                           const 
uno::Reference<uno::XComponentContext>& xContext,
+                                           const 
uno::Reference<text::XTextDocument>& xTextDocument,
+                                           const OUString& rTag, const 
OUString& rTitle)
+    : SwVbaContentControls_BASE(
+          xParent, xContext,
+          uno::Reference<container::XIndexAccess>(
+              new ContentControlCollectionHelper(xParent, xContext, 
xTextDocument, rTag, rTitle)))
+{
+}
+
+// uno::Reference<ooo::vba::word::XContentControl> 
SwVbaContentControls::Add(const uno::Any& Range,
+//                                                                 sal_Int32 
Type)
+// {
+//     sw::mark::IFieldmark* pFieldmark = nullptr;
+//     switch (Type)
+//     {
+//         case ooo::vba::word::WdFieldType::wdFieldFormCheckBox:
+//             break;
+//         case ooo::vba::word::WdFieldType::wdFieldFormDropDown:
+//             break;
+//         case ooo::vba::word::WdFieldType::wdFieldFormTextInput:
+//         default:;
+//     }
+//
+//     return uno::Reference<ooo::vba::word::XContentControl>(
+//         new SwVbaContentControl(mxParent, mxContext, m_xTextDocument, 
*pFieldmark));
+// }
+
+// XEnumerationAccess
+uno::Type SwVbaContentControls::getElementType()
+{
+    return cppu::UnoType<word::XContentControl>::get();
+}
+
+uno::Reference<container::XEnumeration> 
SwVbaContentControls::createEnumeration()
+{
+    return new ContentControlsEnumWrapper(m_xIndexAccess);
+}
+
+uno::Any SwVbaContentControls::createCollectionObject(const uno::Any& aSource) 
{ return aSource; }
+
+OUString SwVbaContentControls::getServiceImplName() { return 
"SwVbaContentControls"; }
+
+uno::Sequence<OUString> SwVbaContentControls::getServiceNames()
+{
+    static uno::Sequence<OUString> const sNames{ 
"ooo.vba.word.ContentControls" };
+    return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbacontentcontrols.hxx 
b/sw/source/ui/vba/vbacontentcontrols.hxx
new file mode 100644
index 000000000000..20ff65ae8943
--- /dev/null
+++ b/sw/source/ui/vba/vbacontentcontrols.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <ooo/vba/word/XContentControls.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+typedef CollTestImplHelper<ooo::vba::word::XContentControls> 
SwVbaContentControls_BASE;
+
+class SwVbaContentControls : public SwVbaContentControls_BASE
+{
+public:
+    /// @throws css::uno::RuntimeException
+    SwVbaContentControls(const css::uno::Reference<ov::XHelperInterface>& 
xParent,
+                         const 
css::uno::Reference<css::uno::XComponentContext>& xContext,
+                         const css::uno::Reference<css::text::XTextDocument>& 
xTextDocument,
+                         const OUString& rTag, const OUString& rTitle);
+
+    // XContentControls
+    //css::uno::Reference<ooo::vba::word::XContentControl> SAL_CALL Add(const 
css::uno::Any& Type, const css::uno::Any& Range) override;
+
+    // XEnumerationAccess
+    css::uno::Type SAL_CALL getElementType() override;
+    css::uno::Reference<css::container::XEnumeration> SAL_CALL 
createEnumeration() override;
+
+    // SwVbaContentControls_BASE
+    css::uno::Any createCollectionObject(const css::uno::Any& aSource) 
override;
+    OUString getServiceImplName() override;
+    css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbadocument.cxx b/sw/source/ui/vba/vbadocument.cxx
index 16e4f762abb1..e72fee6d478c 100644
--- a/sw/source/ui/vba/vbadocument.cxx
+++ b/sw/source/ui/vba/vbadocument.cxx
@@ -21,6 +21,7 @@
 #include <sal/log.hxx>
 
 #include "vbafilterpropsfromformat.hxx"
+#include "vbacontentcontrols.hxx"
 #include "vbadocument.hxx"
 #include "vbaformfields.hxx"
 #include "vbarange.hxx"
@@ -202,6 +203,32 @@ SwVbaDocument::Bookmarks( const uno::Any& rIndex )
     return xBookmarksVba->Item( rIndex, uno::Any() );
 }
 
+uno::Any SAL_CALL SwVbaDocument::ContentControls(const uno::Any& index)
+{
+    uno::Reference<XCollection> xContentControls(
+        new SwVbaContentControls(this, mxContext, mxTextDocument, "", ""));
+    if (index.hasValue())
+        return xContentControls->Item(index, uno::Any());
+
+    return uno::Any(xContentControls);
+}
+
+uno::Any SAL_CALL SwVbaDocument::SelectContentControlsByTag(const uno::Any& 
index)
+{
+    OUString sTag;
+    index >>= sTag;
+    return uno::Any(uno::Reference<XCollection>(
+                        new SwVbaContentControls(this, mxContext, 
mxTextDocument, sTag, "")));
+}
+
+uno::Any SAL_CALL SwVbaDocument::SelectContentControlsByTitle(const uno::Any& 
index)
+{
+    OUString sTitle;
+    index >>= sTitle;
+    return uno::Any(uno::Reference<XCollection>(
+                        new SwVbaContentControls(this, mxContext, 
mxTextDocument, "", sTitle)));
+}
+
 uno::Any SAL_CALL
 SwVbaDocument::Variables( const uno::Any& rIndex )
 {
diff --git a/sw/source/ui/vba/vbadocument.hxx b/sw/source/ui/vba/vbadocument.hxx
index 0d213690a982..aa71de23688d 100644
--- a/sw/source/ui/vba/vbadocument.hxx
+++ b/sw/source/ui/vba/vbadocument.hxx
@@ -55,6 +55,9 @@ public:
     virtual css::uno::Any SAL_CALL BuiltInDocumentProperties( const 
css::uno::Any& index ) override;
     virtual css::uno::Any SAL_CALL CustomDocumentProperties( const 
css::uno::Any& index ) override;
     virtual css::uno::Any SAL_CALL Bookmarks( const css::uno::Any& rIndex ) 
override;
+    css::uno::Any SAL_CALL ContentControls(const css::uno::Any& index) 
override;
+    css::uno::Any SAL_CALL SelectContentControlsByTag(const css::uno::Any& 
index) override;
+    css::uno::Any SAL_CALL SelectContentControlsByTitle(const css::uno::Any& 
index) override;
     virtual css::uno::Any SAL_CALL Variables( const css::uno::Any& rIndex ) 
override;
     virtual css::uno::Any SAL_CALL getAttachedTemplate() override;
     virtual void SAL_CALL setAttachedTemplate( const css::uno::Any& 
_attachedtemplate ) override;

Reply via email to