tdf#151548 vba ContentControls: Add word::XContentControlListEntry

make CppunitTest_sw_macros_test CPPUNIT_TEST_NAME=testVba

This now allows MS Word's modern content control list boxes
(combobox and dropbox) to be controlled by VBA basic.
-allows getting and setting the selected list entry
-allows adding/deleting/renaming/moving list entries

  If .DropdownListEntries.Count <> 3 Then GoTo errorhandler:
  .DropdownListEntries.Item(2).Select
  .DropdownListEntries.Item(2).Delete

  If .DropdownListEntries.Item(2).Text <> "infinity"
  If .DropdownListEntries.Item(2).Value <> "infinity and beyond"

  'With .DropdownListEntries.Add("third", "3rd", 2)
  Dim LE As ContentControlListEntry
  Set LE = .DropdownListEntries.Add("third", "3rd", 2)
  With LE
    If LE.Index <> 2 Then GoTo errorhandler:
    If LE.Value <> "3rd" Then GoTo errorhandler:
    .MoveUp
    .MoveUp
    .MoveUp
    If .Index <> 1 Then GoTo errorhandler:
    .MoveDown
    .MoveDown
    If .Index <> 3 Then GoTo errorhandler:
    End With 'LE
  If .DropdownListEntries.Item(3).Text <> "third" Then GoTo errorhandler:
  End With 'Item 1
runOnceDropDown:

With ActiveDocument.ContentControls.Item(4)
  If .Type <> wdContentControlComboBox Then GoTo errorhandler:
  .DropdownListEntries.Clear
End With

Change-Id: Iffebb2bd69abec1cbcfaed05b58f940664852eae
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143082
Tested-by: Jenkins
Reviewed-by: Justin Luth <jluth@mail.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
This commit is contained in:
Justin Luth 2022-11-21 19:57:15 -05:00 committed by Justin Luth
parent 1e83197fdd
commit 432e5e6e33
12 changed files with 590 additions and 6 deletions

View file

@ -1060,6 +1060,8 @@ $(eval $(call gb_UnoApi_add_idlfiles,oovbaapi,ooo/vba/word,\
XFont \
XContentControl \
XContentControls \
XContentControlListEntry \
XContentControlListEntries \
XFormField \
XFormFields \
XFrame \

View file

@ -0,0 +1,30 @@
/* -*- 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 XContentControlListEntry;
interface XContentControlListEntries
{
interface ooo::vba::XCollection;
/// Adds a new list item to a drop-down list or combo box content control
/// and returns a ContentControlListEntry object.
/// Entries must have a unique display Name,
/// Value is optional - uses Name if not specified.
/// Index is optional. It inserts at the end if not specified, otherwise inserted into list.
XContentControlListEntry Add( [in] string Name, [in] /*optional*/ any Value, [in] /*optional*/ any Index );
/// Remove all items from the dropdown list
void Clear();
};
}; }; };
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -0,0 +1,43 @@
/* -*- 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 XContentControlListEntry
{
interface ooo::vba::XHelperInterface;
/// Returns or sets the ordinal position of a list item in the collection of list items.
[attribute] long Index;
/// Returns or sets a String that represents the display text of the list item.
[attribute] string Text;
/// Returns or sets a String that represents the programmatic value of the list item.
[attribute] string Value;
/// Deletes the specified item in a combo box or drop-down list content control.
void Delete();
/// Moves an item in a drop-down list or combo box content control down one item,
/// so that it is after the item that originally followed it.
void MoveDown();
/// Moves an item in a drop-down list or combo box content control up one item,
/// so that it is before the item that originally preceded it.
void MoveUp();
/// Selects the list entry in a drop-down list or combo box content control
/// and sets the text of the content control to the value of the item.
void Select();
};
}; }; };
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -74,6 +74,8 @@ $(eval $(call gb_Library_add_exception_objects,vbaswobj,\
sw/source/ui/vba/vbacolumns \
sw/source/ui/vba/vbacontentcontrol \
sw/source/ui/vba/vbacontentcontrols \
sw/source/ui/vba/vbacontentcontrollistentry \
sw/source/ui/vba/vbacontentcontrollistentries \
sw/source/ui/vba/vbaformfield \
sw/source/ui/vba/vbaformfields \
sw/source/ui/vba/vbaformfieldcheckbox \

View file

@ -245,6 +245,10 @@ public:
m_aListItems = rListItems;
}
bool AddListItem(size_t nZIndex, const OUString& rDisplayText, const OUString& rValue);
void DeleteListItem(size_t nZIndex);
void ClearListItems();
void SetPicture(bool bPicture) { m_bPicture = bPicture; }
bool GetPicture() const { return m_bPicture; }

View file

@ -231,6 +231,72 @@ void SwContentControl::SwClientNotify(const SwModify&, const SfxHint& rHint)
}
}
bool SwContentControl::AddListItem(size_t nZIndex, const OUString& rDisplayText,
const OUString& rValue)
{
SwContentControlListItem aListItem;
if (rValue.isEmpty())
{
if (rDisplayText.isEmpty())
return false;
aListItem.m_aValue = rDisplayText;
}
else
{
aListItem.m_aValue = rValue;
aListItem.m_aDisplayText = rDisplayText;
}
// Avoid adding duplicates
for (auto& rListItem : GetListItems())
{
if (rListItem == aListItem)
return false;
}
const size_t nLen = GetListItems().size();
nZIndex = std::min(nZIndex, nLen);
const std::optional<size_t> oSelected = GetSelectedListItem();
if (oSelected && *oSelected >= nZIndex)
{
if (*oSelected < nLen)
SetSelectedListItem(*oSelected + 1);
}
std::vector<SwContentControlListItem> vListItems = GetListItems();
vListItems.insert(vListItems.begin() + nZIndex, aListItem);
SetListItems(vListItems);
return true;
}
void SwContentControl::DeleteListItem(size_t nZIndex)
{
if (nZIndex >= GetListItems().size())
return;
const std::optional<size_t> oSelected = GetSelectedListItem();
if (oSelected)
{
if (*oSelected == nZIndex)
{
SetSelectedListItem(std::nullopt);
//Invalidate();
}
else if (*oSelected < nZIndex)
SetSelectedListItem(*oSelected - 1);
}
std::vector<SwContentControlListItem> vListItems = GetListItems();
vListItems.erase(vListItems.begin() + nZIndex);
SetListItems(vListItems);
return;
}
void SwContentControl::ClearListItems()
{
SetSelectedListItem(std::nullopt);
SetListItems(std::vector<SwContentControlListItem>());
}
OUString SwContentControl::GetDateString() const
{
SwDoc& rDoc = m_pTextNode->GetDoc();

View file

@ -17,7 +17,7 @@
#include <ndtxt.hxx>
#include "vbacontentcontrol.hxx"
//#include "vbacontentcontroldropdownlistentries.hxx"
#include "vbacontentcontrollistentries.hxx"
using namespace ::ooo::vba;
using namespace ::com::sun::star;
@ -463,11 +463,11 @@ sal_Int32 SwVbaContentControl::getDateDisplayLocale()
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();
if (!pCC->GetDropDown() && !pCC->GetComboBox())
return uno::Any();
return uno::Any(
uno::Reference<XCollection>(new SwVbaContentControlListEntries(this, mxContext, m_rCC)));
}
OUString SwVbaContentControl::getID()

View file

@ -0,0 +1,176 @@
/* -*- 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 "vbacontentcontrollistentries.hxx"
using namespace ::ooo::vba;
using namespace ::com::sun::star;
namespace
{
class ContentControlListEntriesEnumWrapper : public EnumerationHelper_BASE
{
uno::Reference<container::XIndexAccess> mxIndexAccess;
sal_Int32 nIndex;
public:
explicit ContentControlListEntriesEnumWrapper(
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 ContentControlListEntryCollectionHelper
: public ::cppu::WeakImplHelper<container::XIndexAccess, container::XEnumerationAccess>
{
private:
uno::Reference<XHelperInterface> mxParent;
uno::Reference<uno::XComponentContext> mxContext;
SwTextContentControl& m_rCC;
public:
/// @throws css::uno::RuntimeException
ContentControlListEntryCollectionHelper(uno::Reference<ov::XHelperInterface> xParent,
uno::Reference<uno::XComponentContext> xContext,
SwTextContentControl& rCC)
: mxParent(xParent)
, mxContext(xContext)
, m_rCC(rCC)
{
}
sal_Int32 SAL_CALL getCount() override
{
return m_rCC.GetContentControl().GetContentControl()->GetListItems().size();
}
uno::Any SAL_CALL getByIndex(sal_Int32 Index) override
{
if (Index < 0 || Index >= getCount())
throw lang::IndexOutOfBoundsException();
return uno::Any(uno::Reference<word::XContentControlListEntry>(
new SwVbaContentControlListEntry(mxParent, mxContext, m_rCC, Index)));
}
uno::Type SAL_CALL getElementType() override
{
return cppu::UnoType<word::XContentControlListEntry>::get();
}
sal_Bool SAL_CALL hasElements() override { return getCount() != 0; }
// XEnumerationAccess
uno::Reference<container::XEnumeration> SAL_CALL createEnumeration() override
{
return new ContentControlListEntriesEnumWrapper(this);
}
};
}
/**
* DropDownLists and ComboBoxes contain a list of name/value pairs to chose from.
* Use of DropDownListEntries from any other control is invalid.
*/
SwVbaContentControlListEntries::SwVbaContentControlListEntries(
const uno::Reference<XHelperInterface>& xParent,
const uno::Reference<uno::XComponentContext>& xContext, SwTextContentControl& rCC)
: SwVbaContentControlListEntries_BASE(
xParent, xContext,
uno::Reference<container::XIndexAccess>(
new ContentControlListEntryCollectionHelper(xParent, xContext, rCC)))
, m_rCC(rCC)
{
}
uno::Reference<word::XContentControlListEntry>
SwVbaContentControlListEntries::Add(const OUString& rName, const uno::Any& rValue,
const uno::Any& rIndex)
{
// No duplicate Names allowed in VBA
std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
for (auto& rListItem : pCC->GetListItems())
{
if (rListItem.ToString() == rName)
return uno::Reference<word::XContentControlListEntry>();
}
sal_Int32 nZIndex = SAL_MAX_INT32;
rIndex >>= nZIndex;
// rIndex is 1-based, nZIndex is 0-based. If rIndex is not given, then add as the last choice.
assert(nZIndex > 0);
--nZIndex;
nZIndex = std::min(static_cast<size_t>(nZIndex), pCC->GetListItems().size());
OUString sValue;
rValue >>= sValue;
if (pCC->AddListItem(nZIndex, rName, sValue))
{
return uno::Reference<word::XContentControlListEntry>(
new SwVbaContentControlListEntry(mxParent, mxContext, m_rCC, nZIndex));
}
return uno::Reference<word::XContentControlListEntry>();
}
void SwVbaContentControlListEntries::Clear()
{
m_rCC.GetContentControl().GetContentControl()->ClearListItems();
}
sal_Int32 SwVbaContentControlListEntries::getCount()
{
return m_rCC.GetContentControl().GetContentControl()->GetListItems().size();
}
// XEnumerationAccess
uno::Type SwVbaContentControlListEntries::getElementType()
{
return cppu::UnoType<word::XContentControlListEntry>::get();
}
uno::Reference<container::XEnumeration> SwVbaContentControlListEntries::createEnumeration()
{
return new ContentControlListEntriesEnumWrapper(m_xIndexAccess);
}
// SwVbaContentControlListEntries_BASE
uno::Any SwVbaContentControlListEntries::createCollectionObject(const uno::Any& aSource)
{
return aSource;
}
OUString SwVbaContentControlListEntries::getServiceImplName()
{
return "SwVbaContentControlListEntries";
}
uno::Sequence<OUString> SwVbaContentControlListEntries::getServiceNames()
{
static uno::Sequence<OUString> const sNames{ "ooo.vba.word.ContentControlListEntries" };
return sNames;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -0,0 +1,51 @@
/* -*- 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/XContentControlListEntries.hpp>
#include <ooo/vba/word/XContentControlListEntry.hpp>
#include <vbahelper/vbacollectionimpl.hxx>
#include <textcontentcontrol.hxx>
#include "vbacontentcontrollistentries.hxx"
#include "vbacontentcontrollistentry.hxx"
typedef CollTestImplHelper<ooo::vba::word::XContentControlListEntries>
SwVbaContentControlListEntries_BASE;
class SwVbaContentControlListEntries : public SwVbaContentControlListEntries_BASE
{
private:
SwTextContentControl& m_rCC;
public:
/// @throws css::uno::RuntimeException
SwVbaContentControlListEntries(const css::uno::Reference<ov::XHelperInterface>& xParent,
const css::uno::Reference<css::uno::XComponentContext>& xContext,
SwTextContentControl& rCC);
// XContentControlListEntries
css::uno::Reference<ooo::vba::word::XContentControlListEntry> SAL_CALL
Add(const OUString& rName, const css::uno::Any& rValue, 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;
// SwVbaContentControlListEntries_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: */

View file

@ -0,0 +1,156 @@
/* -*- 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 "vbacontentcontrollistentry.hxx"
using namespace ::ooo::vba;
using namespace ::com::sun::star;
SwVbaContentControlListEntry::SwVbaContentControlListEntry(
const uno::Reference<ooo::vba::XHelperInterface>& rParent,
const uno::Reference<uno::XComponentContext>& rContext, SwTextContentControl& rCC,
size_t nZIndex)
: SwVbaContentControlListEntry_BASE(rParent, rContext)
, m_rCC(rCC)
, m_nZIndex(nZIndex)
{
}
SwVbaContentControlListEntry::~SwVbaContentControlListEntry() {}
sal_Int32 SwVbaContentControlListEntry::getIndex() { return m_nZIndex + 1; }
void SwVbaContentControlListEntry::setIndex(sal_Int32 nSet)
{
if (nSet < 1 || static_cast<size_t>(nSet) == m_nZIndex + 1)
return;
std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
// Given a one-based index to set to
size_t nIndex = std::min(static_cast<size_t>(nSet), pCC->GetListItems().size());
// change to zero-based index
--nIndex;
while (nIndex < m_nZIndex)
MoveUp();
while (m_nZIndex < nIndex)
MoveDown();
}
OUString SwVbaContentControlListEntry::getText()
{
const std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
assert(m_nZIndex < pCC->GetListItems().size());
const SwContentControlListItem& rListItem = pCC->GetListItems()[m_nZIndex];
return rListItem.ToString();
}
void SwVbaContentControlListEntry::setText(const OUString& rSet)
{
const std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
assert(m_nZIndex < vListItems.size());
// prevent duplicates
for (size_t i = 0; i < vListItems.size(); ++i)
{
if (vListItems[i].ToString() == rSet)
return;
}
vListItems[m_nZIndex].m_aDisplayText = rSet;
pCC->SetListItems(vListItems);
//pCC->Invalidate();
}
OUString SwVbaContentControlListEntry::getValue()
{
const std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
assert(m_nZIndex < pCC->GetListItems().size());
const SwContentControlListItem& rListItem = pCC->GetListItems()[m_nZIndex];
return rListItem.m_aValue;
}
void SwVbaContentControlListEntry::setValue(const OUString& rSet)
{
const std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
assert(m_nZIndex < pCC->GetListItems().size());
std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
vListItems[m_nZIndex].m_aValue = rSet;
pCC->SetListItems(vListItems);
}
void SwVbaContentControlListEntry::Delete()
{
std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
pCC->DeleteListItem(m_nZIndex);
}
void SwVbaContentControlListEntry::MoveDown()
{
std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
// if already at last positin, can't move down
if (m_nZIndex >= pCC->GetListItems().size() - 1)
return;
const std::optional<size_t>& oSelected = pCC->GetSelectedListItem();
if (oSelected)
{
if (*oSelected == m_nZIndex)
pCC->SetSelectedListItem(m_nZIndex + 1);
else if (*oSelected == m_nZIndex + 1)
pCC->SetSelectedListItem(*oSelected - 1);
}
std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
std::swap(vListItems[m_nZIndex], vListItems[m_nZIndex + 1]);
pCC->SetListItems(vListItems);
++m_nZIndex;
}
void SwVbaContentControlListEntry::MoveUp()
{
std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
// if already at position 0, can't move up
if (!m_nZIndex || m_nZIndex >= pCC->GetListItems().size())
return;
const std::optional<size_t>& oSelected = pCC->GetSelectedListItem();
if (oSelected)
{
if (*oSelected == m_nZIndex)
pCC->SetSelectedListItem(m_nZIndex - 1);
else if (*oSelected == m_nZIndex - 1)
pCC->SetSelectedListItem(*oSelected + 1);
}
std::vector<SwContentControlListItem> vListItems = pCC->GetListItems();
std::swap(vListItems[m_nZIndex], vListItems[m_nZIndex - 1]);
pCC->SetListItems(vListItems);
--m_nZIndex;
}
void SwVbaContentControlListEntry::Select()
{
std::shared_ptr<SwContentControl> pCC = m_rCC.GetContentControl().GetContentControl();
assert(m_nZIndex < pCC->GetListItems().size());
pCC->SetSelectedListItem(m_nZIndex);
//pCC->Invalidate();
}
// XHelperInterface
OUString SwVbaContentControlListEntry::getServiceImplName()
{
return "SwVbaContentControlListEntry";
}
uno::Sequence<OUString> SwVbaContentControlListEntry::getServiceNames()
{
static uno::Sequence<OUString> const aServiceNames{ "ooo.vba.word.ContentControlListEntry" };
return aServiceNames;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -0,0 +1,54 @@
/* -*- 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/XContentControlListEntry.hpp>
#include <vbahelper/vbahelperinterface.hxx>
#include <textcontentcontrol.hxx>
typedef InheritedHelperInterfaceWeakImpl<ooo::vba::word::XContentControlListEntry>
SwVbaContentControlListEntry_BASE;
class SwVbaContentControlListEntry : public SwVbaContentControlListEntry_BASE
{
private:
SwTextContentControl& m_rCC;
// All LO and internal UNO functions are 0-based. Convert to 1-based when sending to VBA
size_t m_nZIndex;
public:
/// @throws css::uno::RuntimeException
SwVbaContentControlListEntry(const css::uno::Reference<ooo::vba::XHelperInterface>& rParent,
const css::uno::Reference<css::uno::XComponentContext>& rContext,
SwTextContentControl& rCC, size_t nZIndex);
~SwVbaContentControlListEntry() override;
// XContentControlListEntry
sal_Int32 SAL_CALL getIndex() override;
void SAL_CALL setIndex(sal_Int32 nSet) override;
OUString SAL_CALL getText() override;
void SAL_CALL setText(const OUString& sSet) override;
OUString SAL_CALL getValue() override;
void SAL_CALL setValue(const OUString& sSet) override;
void SAL_CALL Delete() override;
void SAL_CALL MoveDown() override;
void SAL_CALL MoveUp() override;
void SAL_CALL Select() override;
// XHelperInterface
OUString getServiceImplName() override;
css::uno::Sequence<OUString> getServiceNames() override;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */