oovbaapi/UnoApi_oovbaapi.mk                          |    3 
 oovbaapi/ooo/vba/word/XDropDown.idl                  |   31 +++
 oovbaapi/ooo/vba/word/XListEntries.idl               |   25 ++
 oovbaapi/ooo/vba/word/XListEntry.idl                 |   27 ++
 sw/Library_vbaswobj.mk                               |    3 
 sw/inc/IMark.hxx                                     |   19 ++
 sw/qa/core/data/docm/testVBA.docm                    |binary
 sw/source/core/crsr/bookmark.cxx                     |  177 +++++++++++++++++++
 sw/source/core/inc/bookmark.hxx                      |    9 
 sw/source/ui/vba/vbaformfield.cxx                    |    6 
 sw/source/ui/vba/vbaformfielddropdown.cxx            |  101 ++++++++++
 sw/source/ui/vba/vbaformfielddropdown.hxx            |   52 +++++
 sw/source/ui/vba/vbaformfielddropdownlistentries.cxx |  171 ++++++++++++++++++
 sw/source/ui/vba/vbaformfielddropdownlistentries.hxx |   49 +++++
 sw/source/ui/vba/vbaformfielddropdownlistentry.cxx   |   56 ++++++
 sw/source/ui/vba/vbaformfielddropdownlistentry.hxx   |   48 +++++
 16 files changed, 773 insertions(+), 4 deletions(-)

New commits:
commit b8fa94b28a2fbf75edc0d31fbf776d40a227e794
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Thu Nov 3 17:59:14 2022 -0400
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Nov 15 11:07:06 2022 +0100

    tdf#151548 vba FormFields: Add basic word::XDropDown support
    
    make CppunitTest_sw_macros_test CPPUNIT_TEST_NAME=testVba
    
    This now allows MS Word Basic legacy list box form fields
    to be controlled by VBA basic.
    -allows getting and setting the text string/list entry
    -allows adding and deleting list entries
    
    Change-Id: Ia772c62395c40a6aa0afae2549f15f4ea3304dbf
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142396
    Reviewed-by: Justin Luth <jl...@mail.com>
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/oovbaapi/UnoApi_oovbaapi.mk b/oovbaapi/UnoApi_oovbaapi.mk
index 6d83196dbcf6..331cf4937e32 100644
--- a/oovbaapi/UnoApi_oovbaapi.mk
+++ b/oovbaapi/UnoApi_oovbaapi.mk
@@ -1052,6 +1052,7 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba/word,\
        XDocument \
        XDocumentOutgoing \
        XDocuments \
+       XDropDown \
        XField \
        XFields \
        XFind \
@@ -1063,6 +1064,8 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba/word,\
        XGlobals \
        XHeaderFooter \
        XHeadersFooters \
+       XListEntries \
+       XListEntry \
        XListFormat \
        XListGalleries \
        XListGallery \
diff --git a/oovbaapi/ooo/vba/word/XDropDown.idl 
b/oovbaapi/ooo/vba/word/XDropDown.idl
new file mode 100644
index 000000000000..d22f2c3299a3
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/XDropDown.idl
@@ -0,0 +1,31 @@
+/* -*- 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 XDropDown
+{
+    interface ooo::vba::XHelperInterface;
+    interface com::sun::star::script::XDefaultProperty;
+
+    /// Default member: True if the specified form field object is a valid 
drop down form field.
+    [attribute, readonly] boolean Valid;
+
+    /// Returns or sets a number that represents the default drop-down index.
+    [attribute] long Default;
+    /// Returns or sets the index of the selected item in a drop-down form 
field.
+    [attribute] long Value;
+
+    /// Returns a ListEntries collection that represents all the items in a 
DropDown object.
+    any ListEntries( [in] any Index );
+};
+
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/word/XListEntries.idl 
b/oovbaapi/ooo/vba/word/XListEntries.idl
new file mode 100644
index 000000000000..5db6b7bd4eb2
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/XListEntries.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 {
+
+interface XListEntry;
+interface XListEntries
+{
+    interface ooo::vba::XCollection;
+
+    /// Returns a ListEntry object that represents an item added to a 
drop-down form field.
+    XListEntry Add( [in] string Name, [in] /*optional long*/ any Index ) 
raises ( com::sun::star::script::BasicErrorException );
+    /// Removes all items from the drop-down form field.
+    void Clear();
+};
+
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oovbaapi/ooo/vba/word/XListEntry.idl 
b/oovbaapi/ooo/vba/word/XListEntry.idl
new file mode 100644
index 000000000000..753538a1786f
--- /dev/null
+++ b/oovbaapi/ooo/vba/word/XListEntry.idl
@@ -0,0 +1,27 @@
+/* -*- 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 XListEntry
+{
+    interface ooo::vba::XHelperInterface;
+
+    /// Returns the position of an item in a collection.
+    [attribute, readonly] long Index;
+    /// Returns or sets the name of the specified object.
+    [attribute] string Name;
+
+    /// Deletes the list entry.
+    void Delete();
+};
+
+}; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/Library_vbaswobj.mk b/sw/Library_vbaswobj.mk
index 5785f11f14cf..1b0ca044d565 100644
--- a/sw/Library_vbaswobj.mk
+++ b/sw/Library_vbaswobj.mk
@@ -75,6 +75,9 @@ $(eval $(call gb_Library_add_exception_objects,vbaswobj,\
     sw/source/ui/vba/vbaformfield \
     sw/source/ui/vba/vbaformfields \
     sw/source/ui/vba/vbaformfieldcheckbox \
+    sw/source/ui/vba/vbaformfielddropdown \
+    sw/source/ui/vba/vbaformfielddropdownlistentries \
+    sw/source/ui/vba/vbaformfielddropdownlistentry \
     sw/source/ui/vba/vbaformfieldtextinput \
     sw/source/ui/vba/vbaframe \
     sw/source/ui/vba/vbaframes \
diff --git a/sw/inc/IMark.hxx b/sw/inc/IMark.hxx
index 776d35c2f4e3..c256c2ef997c 100644
--- a/sw/inc/IMark.hxx
+++ b/sw/inc/IMark.hxx
@@ -125,6 +125,25 @@ namespace sw::mark
             ICheckboxFieldmark &operator =(ICheckboxFieldmark const&) = delete;
     };
 
+    class SW_DLLPUBLIC IDropdownFieldmark
+        : virtual public IFieldmark
+    {
+        protected:
+            IDropdownFieldmark() = default;
+
+        public:
+            virtual OUString GetContent(sal_Int32* pIndex) const = 0;
+            virtual OUString GetContent() const override = 0;
+            virtual void AddContent(const OUString& rText, sal_Int32* pIndex = 
nullptr) = 0;
+            virtual void DelContent(sal_Int32 nDelIndex = -1) = 0;
+            virtual void ReplaceContent(const OUString* pText, sal_Int32* 
pIndex) = 0;
+            virtual void ReplaceContent(const OUString& sNewContent) override 
= 0;
+
+    private:
+            IDropdownFieldmark(IDropdownFieldmark const &) = delete;
+            IDropdownFieldmark &operator =(IDropdownFieldmark const&) = delete;
+    };
+
     class SW_DLLPUBLIC IDateFieldmark
         : virtual public IFieldmark
     {
diff --git a/sw/qa/core/data/docm/testVBA.docm 
b/sw/qa/core/data/docm/testVBA.docm
index 9abcd091638d..839c8dd9f73c 100644
Binary files a/sw/qa/core/data/docm/testVBA.docm and 
b/sw/qa/core/data/docm/testVBA.docm differ
diff --git a/sw/source/core/crsr/bookmark.cxx b/sw/source/core/crsr/bookmark.cxx
index da13b5165849..a05e4024db9c 100644
--- a/sw/source/core/crsr/bookmark.cxx
+++ b/sw/source/core/crsr/bookmark.cxx
@@ -34,6 +34,7 @@
 #include <xmloff/odffields.hxx>
 #include <libxml/xmlwriter.h>
 #include <comphelper/random.hxx>
+#include <comphelper/sequence.hxx>
 #include <comphelper/anytostring.hxx>
 #include <sal/log.hxx>
 #include <svl/numformat.hxx>
@@ -749,6 +750,182 @@ namespace sw::mark
         FieldmarkWithDropDownButton::RemoveButton();
     }
 
+    /** GetContent
+     *  @param pIndex The zero-based index to retrieve
+     *                [in] if pIndex is null or negative, return the listbox's 
chosen result,
+     *                     else return the indicated entry (or last entry for 
invalid choice).
+     *                [out] the index of the returned result or -1 if error
+     */
+    OUString DropDownFieldmark::GetContent(sal_Int32* pIndex) const
+    {
+        sal_Int32 nIndex = pIndex ? *pIndex : -1;
+        auto rParameters = *GetParameters();
+        if (nIndex < 0)
+            rParameters[ODF_FORMDROPDOWN_RESULT] >>= nIndex;
+
+        uno::Sequence<OUString> aSeq;
+        rParameters[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
+        nIndex = std::min(nIndex, aSeq.getLength() - 1);
+
+        if (nIndex < 0)
+        {
+            if (pIndex)
+                *pIndex = -1;
+            return OUString();
+        }
+
+        if (pIndex)
+            *pIndex = nIndex;
+
+        return aSeq[nIndex];
+    }
+
+    OUString DropDownFieldmark::GetContent() const
+    {
+        return GetContent(nullptr);
+    }
+
+    /** AddContent : INSERTS a new choice
+     *  @param rText: The choice to add to the list choices.
+     *
+     *  @param pIndex [optional]
+     *                [in] If pIndex is null or invalid, append to the end of 
the list.
+     *                [out] Modified to point to the position of the choice if 
it already exists.
+     */
+    void DropDownFieldmark::AddContent(const OUString& rText, sal_Int32* 
pIndex)
+    {
+        uno::Sequence<OUString> aSeq;
+        sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+        (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
+
+        // no duplicates: if it already exists, modify the given index to 
point to it
+        const sal_Int32 nCurrentTextPos = comphelper::findValue(aSeq, rText);
+        if (nCurrentTextPos != -1)
+        {
+            if (pIndex)
+                *pIndex = nCurrentTextPos;
+            return;
+        }
+
+        const sal_Int32 nLen = aSeq.getLength();
+        const sal_Int32 nNewPos = pIndex && *pIndex > -1 ? std::min(*pIndex, 
nLen) : nLen;
+
+        // need to shift list result index up if adding new entry before it
+        sal_Int32 nResultIndex = -1;
+        (*pParameters)[ODF_FORMDROPDOWN_RESULT] >>= nResultIndex;
+        if (nNewPos <= nResultIndex)
+            (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= nResultIndex + 1;
+
+        auto aList = 
comphelper::sequenceToContainer<std::vector<OUString>>(aSeq);
+        if (nNewPos < nLen)
+            aList.insert(aList.begin() + nNewPos, rText);
+        else
+        {
+            *pIndex = nLen;
+            aList.push_back(rText);
+        }
+
+        (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= 
comphelper::containerToSequence(aList);
+        Invalidate();
+    }
+
+    /**
+     * ReplaceContent : changes the list result index or renames the existing 
choices
+     * @param pText
+     *               [in] If pIndex is null, change the list result index to 
this provided choice
+     *                       (but do nothing if pText is an invalid choice)
+     *                    else rename that entry.
+     *
+     * @param pIndex
+     *               [in] If pText is null, change the list result index to 
this provided Index
+     *                        (or the last position if it is an invalid choice)
+     *                    else rename this entry (doing nothing for invalid 
indexes).
+     *               [out] If pIndex is invalid, it is modified to use the 
last position.
+     *
+     * This function allows duplicate entries - which is also allowed in MS 
Word.
+     */
+    void DropDownFieldmark::ReplaceContent(const OUString* pText, sal_Int32* 
pIndex)
+    {
+        if (!pIndex && !pText)
+            return;
+
+        uno::Sequence<OUString> aSeq;
+        sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+        (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
+        const sal_Int32 nLen = aSeq.getLength();
+
+        if (!pText)
+        {
+            if (*pIndex < 0 || *pIndex >= nLen)
+                *pIndex = nLen - 1;
+
+            // select pIndex as the new value for the list box
+            (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= *pIndex;
+            Invalidate();
+            return;
+        }
+
+        if (!pIndex)
+        {
+            const sal_Int32 nNewPos = comphelper::findValue(aSeq, *pText);
+            if (nNewPos != -1)
+            {
+                (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= nNewPos;
+                Invalidate();
+            }
+            return;
+        }
+
+        if (*pIndex > -1 && *pIndex < nLen)
+        {
+            auto aList = 
comphelper::sequenceToContainer<std::vector<OUString>>(aSeq);
+            aList[*pIndex] = *pText;
+            (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= 
comphelper::containerToSequence(aList);
+            Invalidate();
+        }
+    }
+
+    void DropDownFieldmark::ReplaceContent(const OUString& rNewContent)
+    {
+        ReplaceContent(&rNewContent, nullptr);
+    }
+
+    /**
+     * Remove everything if the given index is negative, else remove the given 
index (if valid).
+     * If deleting the currently selected choice, reset the selection to the 
first choice.
+     */
+    void DropDownFieldmark::DelContent(sal_Int32 nDelIndex)
+    {
+        sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+        uno::Sequence<OUString> aSeq;
+        if (nDelIndex < 0)
+        {
+            pParameters->erase(ODF_FORMDROPDOWN_RESULT);
+            (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= aSeq;
+            Invalidate();
+            return;
+        }
+
+        (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
+        if (nDelIndex >= aSeq.getLength())
+            return;
+
+        // If deleting the current choice, select the first entry instead
+        // else need to shift list result index down if deleting an entry 
before it
+        sal_Int32 nResultIndex = -1;
+        (*pParameters)[ODF_FORMDROPDOWN_RESULT] >>= nResultIndex;
+        if (nDelIndex == nResultIndex)
+            nResultIndex = 0;
+        else if (nDelIndex < nResultIndex)
+            --nResultIndex;
+
+        comphelper::removeElementAt(aSeq, nDelIndex);
+        if (nResultIndex != -1)
+            (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= nResultIndex;
+        (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= aSeq;
+        Invalidate();
+    }
+
     void DropDownFieldmark::SetPortionPaintArea(const SwRect& 
rPortionPaintArea)
     {
         m_aPortionPaintArea = rPortionPaintArea;
diff --git a/sw/source/core/inc/bookmark.hxx b/sw/source/core/inc/bookmark.hxx
index 14c176c96a36..075564d1b386 100644
--- a/sw/source/core/inc/bookmark.hxx
+++ b/sw/source/core/inc/bookmark.hxx
@@ -290,7 +290,8 @@ namespace sw::mark {
 
         /// Fieldmark representing a drop-down form field.
         class DropDownFieldmark final
-            : public FieldmarkWithDropDownButton
+            : virtual public IDropdownFieldmark
+            , public FieldmarkWithDropDownButton
         {
         public:
             DropDownFieldmark(const SwPaM& rPaM, const OUString& rName);
@@ -298,6 +299,12 @@ namespace sw::mark {
 
             virtual void ShowButton(SwEditWin* pEditWin) override;
             virtual void RemoveButton() override;
+            OUString GetContent(sal_Int32* pIndex) const override;
+            OUString GetContent() const override;
+            void AddContent(const OUString& rText, sal_Int32* pIndex = 
nullptr) override;
+            void DelContent(sal_Int32 nDelIndex = -1) override;
+            void ReplaceContent(const OUString* pText, sal_Int32* pIndex) 
override;
+            void ReplaceContent(const OUString& sNewContent) override;
 
             // This method should be called only by the portion so we can now 
the portion's painting area
             void SetPortionPaintArea(const SwRect& rPortionPaintArea);
diff --git a/sw/source/ui/vba/vbaformfield.cxx 
b/sw/source/ui/vba/vbaformfield.cxx
index dd43e47b84bf..f79c92913f29 100644
--- a/sw/source/ui/vba/vbaformfield.cxx
+++ b/sw/source/ui/vba/vbaformfield.cxx
@@ -26,6 +26,7 @@
 
 #include "vbaformfield.hxx"
 #include "vbaformfieldcheckbox.hxx"
+#include "vbaformfielddropdown.hxx"
 #include "vbaformfieldtextinput.hxx"
 #include "wordvbahelper.hxx"
 
@@ -61,9 +62,8 @@ uno::Any SAL_CALL SwVbaFormField::CheckBox()
 
 uno::Any SAL_CALL SwVbaFormField::DropDown()
 {
-    // return uno::Any(uno::Reference<word::XDropDown>(
-    //     new SwVbaFormFieldDropDown(mxParent, mxContext, m_rFormField)));
-    return uno::Any();
+    return uno::Any(uno::Reference<word::XDropDown>(
+        new SwVbaFormFieldDropDown(mxParent, mxContext, m_rFormField)));
 }
 
 uno::Any SAL_CALL SwVbaFormField::TextInput()
diff --git a/sw/source/ui/vba/vbaformfielddropdown.cxx 
b/sw/source/ui/vba/vbaformfielddropdown.cxx
new file mode 100644
index 000000000000..a1c3254d766b
--- /dev/null
+++ b/sw/source/ui/vba/vbaformfielddropdown.cxx
@@ -0,0 +1,101 @@
+/* -*- 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/WdTextFormFieldType.hpp>
+
+#include "vbaformfielddropdown.hxx"
+#include "vbaformfielddropdownlistentries.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+/**
+ * Official documentation found at 
https://learn.microsoft.com/en-us/office/vba/api/word.dropdown
+ *
+ * DropDown formfields are inline text objects that are only found in MS Word.
+ * They cannot be created in Excel or in Calc.
+ *
+ * Note that VBA might call this a DropDown, but it might not actually be one,
+ * so make good use of getValid()
+ */
+SwVbaFormFieldDropDown::SwVbaFormFieldDropDown(
+    const uno::Reference<ooo::vba::XHelperInterface>& rParent,
+    const uno::Reference<uno::XComponentContext>& rContext, 
::sw::mark::IFieldmark& rFormField)
+    : SwVbaFormFieldDropDown_BASE(rParent, rContext)
+    , m_pDropDown(dynamic_cast<sw::mark::IDropdownFieldmark*>(&rFormField))
+{
+}
+
+SwVbaFormFieldDropDown::~SwVbaFormFieldDropDown() {}
+
+OUString SwVbaFormFieldDropDown::getDefaultPropertyName() { return "Valid"; }
+
+sal_Bool SwVbaFormFieldDropDown::getValid()
+{
+    return m_pDropDown
+           && IDocumentMarkAccess::GetType(*m_pDropDown)
+                  == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK;
+}
+
+sal_Int32 SwVbaFormFieldDropDown::getDefault() { return getValue(); }
+
+void SwVbaFormFieldDropDown::setDefault(sal_Int32 nSet)
+{
+    // Hard to know what to do here, since LO doesn't have a default property 
for DropDowns.
+    // Setting this really only makes sense when macro-adding a DropDown.
+    // In that case, we want it to affect the actual text content.
+    // However, if an item has already been selected by the user, then this 
shouldn't do anything.
+    // Assuming this is only ever set when adding a DropDown seems the sanest 
approach.
+    setValue(nSet);
+}
+
+sal_Int32 SwVbaFormFieldDropDown::getValue()
+{
+    sal_Int32 nRet = 0;
+    if (!getValid())
+        return nRet;
+
+    --nRet; // send -1, which requests being changed to the selected 
DropDown's zero-based index
+    m_pDropDown->GetContent(&nRet);
+    return nRet + 1;
+}
+
+void SwVbaFormFieldDropDown::setValue(sal_Int32 nIndex)
+{
+    if (!getValid() || nIndex == getValue())
+        return;
+
+    // switch to zero-based index for implementation
+    --nIndex;
+    m_pDropDown->ReplaceContent(/*pText=*/nullptr, &nIndex);
+}
+
+uno::Any SwVbaFormFieldDropDown::ListEntries(const uno::Any& rIndex)
+{
+    if (!getValid())
+        return uno::Any();
+
+    uno::Reference<XCollection> xCol(
+        new SwVbaFormFieldDropDownListEntries(this, mxContext, *m_pDropDown));
+
+    if (rIndex.hasValue())
+        return xCol->Item(rIndex, uno::Any());
+
+    return uno::Any(xCol);
+}
+
+OUString SwVbaFormFieldDropDown::getServiceImplName() { return 
"SwVbaFormFieldDropDown"; }
+
+uno::Sequence<OUString> SwVbaFormFieldDropDown::getServiceNames()
+{
+    static uno::Sequence<OUString> const aServiceNames{ 
"ooo.vba.word.DropDown" };
+    return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbaformfielddropdown.hxx 
b/sw/source/ui/vba/vbaformfielddropdown.hxx
new file mode 100644
index 000000000000..e92caa2f8e8c
--- /dev/null
+++ b/sw/source/ui/vba/vbaformfielddropdown.hxx
@@ -0,0 +1,52 @@
+/* -*- 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 <ooo/vba/word/XDropDown.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+#include <IDocumentMarkAccess.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl<ooo::vba::word::XDropDown> 
SwVbaFormFieldDropDown_BASE;
+
+class SwVbaFormFieldDropDown : public SwVbaFormFieldDropDown_BASE
+{
+private:
+    sw::mark::IDropdownFieldmark* m_pDropDown;
+
+public:
+    /// @throws css::uno::RuntimeException
+    SwVbaFormFieldDropDown(const 
css::uno::Reference<ooo::vba::XHelperInterface>& rParent,
+                           const 
css::uno::Reference<css::uno::XComponentContext>& rContext,
+                           sw::mark::IFieldmark& rFormField);
+    ~SwVbaFormFieldDropDown() override;
+
+    // XDropDown
+    OUString SAL_CALL getDefaultPropertyName() override;
+
+    // Default member: True if the specified form field object is a valid 
listbox field
+    sal_Bool SAL_CALL getValid() override;
+
+    // Returns and sets the index for the default listbox entry
+    sal_Int32 SAL_CALL getDefault() override;
+    void SAL_CALL setDefault(sal_Int32 nSet) override;
+    // Returns and sets the index of the selected listbox entry
+    sal_Int32 SAL_CALL getValue() override;
+    void SAL_CALL setValue(sal_Int32 nIndex) override;
+
+    // Returns a ListEntries collection that represents all the available 
entries
+    css::uno::Any SAL_CALL ListEntries(const css::uno::Any& rIndex) 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/vbaformfielddropdownlistentries.cxx 
b/sw/source/ui/vba/vbaformfielddropdownlistentries.cxx
new file mode 100644
index 000000000000..926ebeaa9656
--- /dev/null
+++ b/sw/source/ui/vba/vbaformfielddropdownlistentries.cxx
@@ -0,0 +1,171 @@
+/* -*- 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 <xmloff/odffields.hxx>
+
+#include "vbaformfielddropdownlistentries.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+static uno::Sequence<OUString> 
lcl_getListEntries(sw::mark::IDropdownFieldmark& rDropDown)
+{
+    uno::Sequence<OUString> aSeq;
+    (*rDropDown.GetParameters())[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
+    return aSeq;
+}
+
+namespace
+{
+class ListEntriesEnumWrapper : public EnumerationHelper_BASE
+{
+    uno::Reference<container::XIndexAccess> mxIndexAccess;
+    sal_Int32 nIndex;
+
+public:
+    explicit ListEntriesEnumWrapper(uno::Reference<container::XIndexAccess> 
xIndexAccess)
+        : mxIndexAccess(xIndexAccess)
+        , nIndex(0)
+    {
+    }
+
+    virtual sal_Bool SAL_CALL hasMoreElements() override
+    {
+        return (nIndex < mxIndexAccess->getCount());
+    }
+
+    virtual uno::Any SAL_CALL nextElement() override
+    {
+        if (nIndex < mxIndexAccess->getCount())
+        {
+            return mxIndexAccess->getByIndex(nIndex++);
+        }
+        throw container::NoSuchElementException();
+    }
+};
+
+class ListEntryCollectionHelper
+    : public ::cppu::WeakImplHelper<container::XIndexAccess, 
container::XEnumerationAccess>
+{
+private:
+    uno::Reference<XHelperInterface> mxParent;
+    uno::Reference<uno::XComponentContext> mxContext;
+    sw::mark::IDropdownFieldmark& m_rDropDown;
+
+public:
+    /// @throws css::uno::RuntimeException
+    ListEntryCollectionHelper(uno::Reference<ov::XHelperInterface> xParent,
+                              uno::Reference<uno::XComponentContext> xContext,
+                              sw::mark::IDropdownFieldmark& rFormField)
+        : mxParent(xParent)
+        , mxContext(xContext)
+        , m_rDropDown(rFormField)
+    {
+    }
+
+    virtual sal_Int32 SAL_CALL getCount() override
+    {
+        return lcl_getListEntries(m_rDropDown).getLength();
+    }
+
+    virtual uno::Any SAL_CALL getByIndex(sal_Int32 Index) override
+    {
+        if (Index < 0 || Index >= getCount())
+            throw lang::IndexOutOfBoundsException();
+
+        return uno::Any(uno::Reference<word::XListEntry>(
+            new SwVbaFormFieldDropDownListEntry(mxParent, mxContext, 
m_rDropDown, Index)));
+    }
+
+    virtual uno::Type SAL_CALL getElementType() override
+    {
+        return cppu::UnoType<word::XListEntry>::get();
+    }
+
+    virtual sal_Bool SAL_CALL hasElements() override { return getCount() != 0; 
}
+
+    // XEnumerationAccess
+    virtual uno::Reference<container::XEnumeration> SAL_CALL 
createEnumeration() override
+    {
+        return new ListEntriesEnumWrapper(this);
+    }
+};
+}
+
+SwVbaFormFieldDropDownListEntries::SwVbaFormFieldDropDownListEntries(
+    const uno::Reference<XHelperInterface>& xParent,
+    const uno::Reference<uno::XComponentContext>& xContext,
+    sw::mark::IDropdownFieldmark& rFormField)
+    : SwVbaFormFieldDropDownListEntries_BASE(
+          xParent, xContext,
+          uno::Reference<container::XIndexAccess>(
+              new ListEntryCollectionHelper(xParent, xContext, rFormField)))
+    , m_rDropDown(rFormField)
+{
+}
+
+// XListEntries
+uno::Reference<word::XListEntry>
+    SAL_CALL SwVbaFormFieldDropDownListEntries::Add(const OUString& rName, 
const uno::Any& rIndex)
+{
+    sal_Int32 nZIndex = 0;
+    rIndex >>= nZIndex;
+    // rIndex is 1-based, nZIndex is 0-based. If rIndex is not given, then add 
as the last choice.
+
+    // In testing with Word 2010, this gives a compile error: 
'ListEntries.Add("Name", 2)'
+    // This compiles, but gets an unsupported runtime error:  
'ListEntries.Add("Name", 2) = "Choice'
+    // So the only thing that actually works is to simply append: 
'ListEntires.Add("Name")'
+    // but I'll still keep the expected implementation for the broken case.
+    if (!nZIndex)
+        nZIndex = SAL_MAX_INT32;
+    else
+        --nZIndex;
+    m_rDropDown.AddContent(rName + "__allowDuplicates", &nZIndex);
+    m_rDropDown.ReplaceContent(&rName, &nZIndex);
+
+    return uno::Reference<word::XListEntry>(
+        new SwVbaFormFieldDropDownListEntry(mxParent, mxContext, m_rDropDown, 
nZIndex));
+}
+
+void SAL_CALL SwVbaFormFieldDropDownListEntries::Clear() { 
m_rDropDown.DelContent(); }
+
+sal_Int32 SwVbaFormFieldDropDownListEntries::getCount()
+{
+    return lcl_getListEntries(m_rDropDown).getLength();
+}
+
+// XEnumerationAccess
+uno::Type SwVbaFormFieldDropDownListEntries::getElementType()
+{
+    return cppu::UnoType<word::XListEntry>::get();
+}
+
+uno::Reference<container::XEnumeration> 
SwVbaFormFieldDropDownListEntries::createEnumeration()
+{
+    return new ListEntriesEnumWrapper(m_xIndexAccess);
+}
+
+// SwVbadropDownListEntries_BASE
+uno::Any SwVbaFormFieldDropDownListEntries::createCollectionObject(const 
uno::Any& aSource)
+{
+    return aSource;
+}
+
+OUString SwVbaFormFieldDropDownListEntries::getServiceImplName()
+{
+    return "SwVbaFormFieldDropDownListEntries";
+}
+
+uno::Sequence<OUString> SwVbaFormFieldDropDownListEntries::getServiceNames()
+{
+    static uno::Sequence<OUString> const sNames{ "ooo.vba.word.ListEntries" };
+    return sNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbaformfielddropdownlistentries.hxx 
b/sw/source/ui/vba/vbaformfielddropdownlistentries.hxx
new file mode 100644
index 000000000000..ef1339127021
--- /dev/null
+++ b/sw/source/ui/vba/vbaformfielddropdownlistentries.hxx
@@ -0,0 +1,49 @@
+/* -*- 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 <ooo/vba/word/XListEntries.hpp>
+#include <ooo/vba/word/XListEntry.hpp>
+
+#include <vbahelper/vbacollectionimpl.hxx>
+
+#include "vbaformfielddropdownlistentries.hxx"
+#include "vbaformfielddropdownlistentry.hxx"
+
+typedef CollTestImplHelper<ooo::vba::word::XListEntries> 
SwVbaFormFieldDropDownListEntries_BASE;
+
+class SwVbaFormFieldDropDownListEntries : public 
SwVbaFormFieldDropDownListEntries_BASE
+{
+private:
+    sw::mark::IDropdownFieldmark& m_rDropDown;
+
+public:
+    /// @throws css::uno::RuntimeException
+    SwVbaFormFieldDropDownListEntries(
+        const css::uno::Reference<ov::XHelperInterface>& xParent,
+        const css::uno::Reference<css::uno::XComponentContext>& xContext,
+        sw::mark::IDropdownFieldmark& m_rDropDown);
+
+    // XListEntries
+    css::uno::Reference<ooo::vba::word::XListEntry>
+        SAL_CALL Add(const OUString& rName, const css::uno::Any& rIndex) 
override;
+    void SAL_CALL Clear() override;
+    sal_Int32 SAL_CALL getCount() override;
+
+    // XEnumerationAccess
+    css::uno::Type SAL_CALL getElementType() override;
+    css::uno::Reference<css::container::XEnumeration> SAL_CALL 
createEnumeration() override;
+
+    // SwVbaFormFieldDropDownListEntries_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/vbaformfielddropdownlistentry.cxx 
b/sw/source/ui/vba/vbaformfielddropdownlistentry.cxx
new file mode 100644
index 000000000000..dd9c94dc727e
--- /dev/null
+++ b/sw/source/ui/vba/vbaformfielddropdownlistentry.cxx
@@ -0,0 +1,56 @@
+/* -*- 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 "vbaformfielddropdownlistentry.hxx"
+
+using namespace ::ooo::vba;
+using namespace ::com::sun::star;
+
+SwVbaFormFieldDropDownListEntry::SwVbaFormFieldDropDownListEntry(
+    const uno::Reference<ooo::vba::XHelperInterface>& rParent,
+    const uno::Reference<uno::XComponentContext>& rContext,
+    sw::mark::IDropdownFieldmark& rFormField, sal_Int32 nZIndex)
+    : SwVbaFormFieldDropDownListEntry_BASE(rParent, rContext)
+    , m_rDropDown(rFormField)
+    , m_nZIndex(nZIndex)
+{
+}
+
+SwVbaFormFieldDropDownListEntry::~SwVbaFormFieldDropDownListEntry() {}
+
+// XListEntry
+sal_Int32 SAL_CALL SwVbaFormFieldDropDownListEntry::getIndex() { return 
m_nZIndex + 1; }
+
+OUString SAL_CALL SwVbaFormFieldDropDownListEntry::getName()
+{
+    sal_Int32 nZIndex = m_nZIndex;
+    return m_rDropDown.GetContent(&nZIndex);
+}
+
+void SAL_CALL SwVbaFormFieldDropDownListEntry::setName(const OUString& rSet)
+{
+    sal_Int32 nZIndex = m_nZIndex;
+    m_rDropDown.ReplaceContent(&rSet, &nZIndex);
+}
+
+void SwVbaFormFieldDropDownListEntry::Delete() { 
m_rDropDown.DelContent(m_nZIndex); }
+
+// XHelperInterface
+OUString SwVbaFormFieldDropDownListEntry::getServiceImplName()
+{
+    return "SwVbaFormFieldDropDownListEntry";
+}
+
+uno::Sequence<OUString> SwVbaFormFieldDropDownListEntry::getServiceNames()
+{
+    static uno::Sequence<OUString> const aServiceNames{ 
"ooo.vba.word.ListEntry" };
+    return aServiceNames;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/vba/vbaformfielddropdownlistentry.hxx 
b/sw/source/ui/vba/vbaformfielddropdownlistentry.hxx
new file mode 100644
index 000000000000..bfdd4cf2d6db
--- /dev/null
+++ b/sw/source/ui/vba/vbaformfielddropdownlistentry.hxx
@@ -0,0 +1,48 @@
+/* -*- 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 <ooo/vba/word/XListEntry.hpp>
+
+#include <vbahelper/vbahelperinterface.hxx>
+
+#include <IDocumentMarkAccess.hxx>
+
+typedef InheritedHelperInterfaceWeakImpl<ooo::vba::word::XListEntry>
+    SwVbaFormFieldDropDownListEntry_BASE;
+
+class SwVbaFormFieldDropDownListEntry : public 
SwVbaFormFieldDropDownListEntry_BASE
+{
+private:
+    sw::mark::IDropdownFieldmark& m_rDropDown;
+    // All LO and internal functions are 0-based. Convert to 1-based when 
sending to VBA
+    const sal_Int32 m_nZIndex;
+
+public:
+    /// @throws css::uno::RuntimeException
+    SwVbaFormFieldDropDownListEntry(
+        const css::uno::Reference<ooo::vba::XHelperInterface>& rParent,
+        const css::uno::Reference<css::uno::XComponentContext>& rContext,
+        sw::mark::IDropdownFieldmark& rFormField, sal_Int32 nZIndex);
+    ~SwVbaFormFieldDropDownListEntry() override;
+
+    // XListEntry
+    sal_Int32 SAL_CALL getIndex() override;
+
+    OUString SAL_CALL getName() override;
+    void SAL_CALL setName(const OUString& sSet) override;
+
+    void SAL_CALL Delete() override;
+
+    // XHelperInterface
+    OUString getServiceImplName() override;
+    css::uno::Sequence<OUString> getServiceNames() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to