tdf#151548 vba ContentControls: Add basic word::XContentControl

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 <jluth@mail.com>
diff --git a/oovbaapi/UnoApi_oovbaapi.mk b/oovbaapi/UnoApi_oovbaapi.mk
index 331cf49..06370076 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 0000000..d93159a
--- /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 0000000..e538460
--- /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 0000000..49facea
--- /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 0f7b5f9..354cac1 100644
--- a/oovbaapi/ooo/vba/word/XDocument.idl
+++ b/oovbaapi/ooo/vba/word/XDocument.idl
@@ -37,6 +37,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 1b0ca04..e6b45b4 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 0000000..faa8576
--- /dev/null
+++ b/sw/qa/core/data/docm/testModernVBA.docm
Binary files differ
diff --git a/sw/qa/core/macros-test.cxx b/sw/qa/core/macros-test.cxx
index a4030b0..df56212 100644
--- a/sw/qa/core/macros-test.cxx
+++ b/sw/qa/core/macros-test.cxx
@@ -91,6 +91,10 @@ void SwMacrosTest::testVba()
            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")
        },
        {
            OUString("testFind.docm"),
            OUString("vnd.sun.Star.script:Project.Module1.testAll?language=Basic&location=document")
        },
diff --git a/sw/source/ui/vba/vbacontentcontrol.cxx b/sw/source/ui/vba/vbacontentcontrol.cxx
new file mode 100644
index 0000000..3d51d8b
--- /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 0000000..e5fa927
--- /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 indentifier 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.
    // Retreive 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 0000000..549541c
--- /dev/null
+++ b/sw/source/ui/vba/vbacontentcontrols.cxx
@@ -0,0 +1,264 @@
/* -*- 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();
    size_t i = static_cast<size_t>(rIndex);
    const size_t nLen = rManager.GetCount();
    if (!pElementNames && rIndex > 0 && sName.empty() && sTag.empty() && sTitle.empty())
    {
        // 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 (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)))
    , m_sTag(rTag)
    , m_sTitle(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 0000000..a31ca58
--- /dev/null
+++ b/sw/source/ui/vba/vbacontentcontrols.hxx
@@ -0,0 +1,44 @@
/* -*- 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
{
private:
    OUString m_sTag;
    OUString m_sTitle;

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 6dcbdbf..f08974e 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"
@@ -217,6 +218,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 f1352e4..31ca375 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;