summaryrefslogtreecommitdiff
path: root/sw
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2022-05-03 09:44:01 +0200
committerMiklos Vajna <vmiklos@collabora.com>2022-05-03 14:04:51 +0200
commit0a415b92d3c1ea2c5befd30b4ac29442f422a41d (patch)
tree0c88c08e85e875b83bf4f75f99361e4997319d96 /sw
parent27588f9728eb9c6be19fcf69fd1c3a56285b4c1d (diff)
sw content controls, drop-down: add doc model & UNO API
Add a new property, which is a list of display-text / value pairs. If the list is non-empty, that implies that the type is a dropdown. This should be enough for the UI to be able to provide a list of choices & update dropdown state on click. Note that in contrast to dropdown field-marks, here each entry has a user-readable string and a machine-readable value. Fieldmarks only had a single value. Change-Id: I22b9f554e2e1a9e84cc7eb7e17772ea1a5775316 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133742 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
Diffstat (limited to 'sw')
-rw-r--r--sw/inc/formatcontentcontrol.hxx26
-rw-r--r--sw/inc/unoprnms.hxx1
-rw-r--r--sw/qa/core/unocore/unocore.cxx51
-rw-r--r--sw/source/core/txtnode/attrcontentcontrol.cxx69
-rw-r--r--sw/source/core/unocore/unocontentcontrol.cxx30
-rw-r--r--sw/source/core/unocore/unomap1.cxx1
-rw-r--r--sw/source/uibase/wrtsh/wrtsh1.cxx1
7 files changed, 178 insertions, 1 deletions
diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index 8419446f59c0..0cd65b66d711 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -20,6 +20,7 @@
#pragma once
#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
#include <cppuhelper/weakref.hxx>
#include <sal/types.h>
@@ -36,6 +37,7 @@ enum class SwContentControlType
{
RICH_TEXT,
CHECKBOX,
+ DROP_DOWN_LIST,
};
/// SfxPoolItem subclass that wraps an SwContentControl.
@@ -73,6 +75,21 @@ public:
void dumpAsXml(xmlTextWriterPtr pWriter) const override;
};
+/// Represents one list item in a content control dropdown list.
+class SwContentControlListItem
+{
+public:
+ OUString m_aDisplayText;
+ OUString m_aValue;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ static void ItemsToAny(const std::vector<SwContentControlListItem>& rItems,
+ css::uno::Any& rVal);
+
+ static std::vector<SwContentControlListItem> ItemsFromAny(const css::uno::Any& rVal);
+};
+
/// Stores the properties of a content control.
class SAL_DLLPUBLIC_RTTI SwContentControl : public sw::BroadcastingModify
{
@@ -98,6 +115,8 @@ class SAL_DLLPUBLIC_RTTI SwContentControl : public sw::BroadcastingModify
/// If m_bCheckbox is true, the value of an unchecked checkbox.
OUString m_aUncheckedState;
+ std::vector<SwContentControlListItem> m_aListItems;
+
public:
SwTextContentControl* GetTextAttr() const;
@@ -148,6 +167,13 @@ public:
OUString GetUncheckedState() const { return m_aUncheckedState; }
+ std::vector<SwContentControlListItem> GetListItems() const { return m_aListItems; }
+
+ void SetListItems(const std::vector<SwContentControlListItem>& rListItems)
+ {
+ m_aListItems = rListItems;
+ }
+
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const;
};
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index bfc3a28aeecc..187491905d77 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -876,6 +876,7 @@
#define UNO_NAME_CHECKED "Checked"
#define UNO_NAME_CHECKED_STATE "CheckedState"
#define UNO_NAME_UNCHECKED_STATE "UncheckedState"
+#define UNO_NAME_LIST_ITEMS "ListItems"
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 0f1b6a5e7623..f941c60c4aaf 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -456,6 +456,57 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlCheckbox)
CPPUNIT_ASSERT_EQUAL(OUString(u"☐"), pContentControl->GetUncheckedState());
}
+CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDropdown)
+{
+ // Given an empty document:
+ SwDoc* pDoc = createSwDoc();
+
+ // When inserting a dropdown content control:
+ uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "test", /*bAbsorb=*/false);
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<text::XTextContent> xContentControl(
+ xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY);
+ {
+ uno::Sequence<beans::PropertyValues> aListItems = {
+ {
+ comphelper::makePropertyValue("DisplayText", uno::makeAny(OUString("red"))),
+ comphelper::makePropertyValue("Value", uno::makeAny(OUString("R"))),
+ },
+ {
+ comphelper::makePropertyValue("DisplayText", uno::makeAny(OUString("green"))),
+ comphelper::makePropertyValue("Value", uno::makeAny(OUString("G"))),
+ },
+ {
+ comphelper::makePropertyValue("DisplayText", uno::makeAny(OUString("blue"))),
+ comphelper::makePropertyValue("Value", uno::makeAny(OUString("B"))),
+ },
+ };
+ // Without the accompanying fix in place, this test would have failed with:
+ // An uncaught exception of type com.sun.star.beans.UnknownPropertyException
+ xContentControlProps->setPropertyValue("ListItems", uno::makeAny(aListItems));
+ }
+ xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true);
+
+ // Then make sure that the specified properties are set:
+ SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+ SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ auto& rFormatContentControl
+ = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+ SwContentControl* pContentControl = rFormatContentControl.GetContentControl();
+ std::vector<SwContentControlListItem> aListItems = pContentControl->GetListItems();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aListItems.size());
+ CPPUNIT_ASSERT_EQUAL(OUString("red"), aListItems[0].m_aDisplayText);
+ CPPUNIT_ASSERT_EQUAL(OUString("R"), aListItems[0].m_aValue);
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx
index e469bab96e1d..c28e686ce6fb 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -22,6 +22,8 @@
#include <libxml/xmlwriter.h>
#include <sal/log.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
#include <ndtxt.hxx>
#include <textcontentcontrol.hxx>
@@ -220,9 +222,76 @@ void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
BAD_CAST(m_aCheckedState.toUtf8().getStr()));
(void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("unchecked-state"), "%s",
BAD_CAST(m_aUncheckedState.toUtf8().getStr()));
+
+ if (!m_aListItems.empty())
+ {
+ for (const auto& rListItem : m_aListItems)
+ {
+ rListItem.dumpAsXml(pWriter);
+ }
+ }
+
(void)xmlTextWriterEndElement(pWriter);
}
+void SwContentControlListItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlListItem"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("display-text"),
+ BAD_CAST(m_aDisplayText.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(m_aValue.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwContentControlListItem::ItemsToAny(const std::vector<SwContentControlListItem>& rItems,
+ uno::Any& rVal)
+{
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aRet(rItems.size());
+
+ uno::Sequence<beans::PropertyValue>* pRet = aRet.getArray();
+ for (size_t i = 0; i < rItems.size(); ++i)
+ {
+ const SwContentControlListItem& rItem = rItems[i];
+ uno::Sequence<beans::PropertyValue> aItem = {
+ comphelper::makePropertyValue("DisplayText", rItem.m_aDisplayText),
+ comphelper::makePropertyValue("Value", rItem.m_aValue),
+ };
+ pRet[i] = aItem;
+ }
+
+ rVal <<= aRet;
+}
+
+std::vector<SwContentControlListItem>
+SwContentControlListItem::ItemsFromAny(const css::uno::Any& rVal)
+{
+ std::vector<SwContentControlListItem> aRet;
+
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aSequence;
+ rVal >>= aSequence;
+ for (const auto& rItem : aSequence)
+ {
+ comphelper::SequenceAsHashMap aMap(rItem);
+ SwContentControlListItem aItem;
+ auto it = aMap.find("DisplayText");
+ if (it != aMap.end())
+ {
+ it->second >>= aItem.m_aDisplayText;
+ }
+ it = aMap.find("Value");
+ if (it != aMap.end())
+ {
+ it->second >>= aItem.m_aValue;
+ }
+ aRet.push_back(aItem);
+ }
+
+ return aRet;
+}
+
SwTextContentControl* SwTextContentControl::CreateTextContentControl(SwTextNode* pTargetTextNode,
SwFormatContentControl& rAttr,
sal_Int32 nStart,
diff --git a/sw/source/core/unocore/unocontentcontrol.cxx b/sw/source/core/unocore/unocontentcontrol.cxx
index 81ccc9fd088c..19e5e60176d3 100644
--- a/sw/source/core/unocore/unocontentcontrol.cxx
+++ b/sw/source/core/unocore/unocontentcontrol.cxx
@@ -160,6 +160,7 @@ public:
bool m_bChecked;
OUString m_aCheckedState;
OUString m_aUncheckedState;
+ std::vector<SwContentControlListItem> m_aListItems;
Impl(SwXContentControl& rThis, SwDoc& rDoc, SwContentControl* pContentControl,
const uno::Reference<text::XText>& xParentText,
@@ -516,6 +517,7 @@ void SwXContentControl::AttachImpl(const uno::Reference<text::XTextRange>& xText
pContentControl->SetChecked(m_pImpl->m_bChecked);
pContentControl->SetCheckedState(m_pImpl->m_aCheckedState);
pContentControl->SetUncheckedState(m_pImpl->m_aUncheckedState);
+ pContentControl->SetListItems(m_pImpl->m_aListItems);
SwFormatContentControl aContentControl(pContentControl, nWhich);
bool bSuccess
@@ -524,7 +526,7 @@ void SwXContentControl::AttachImpl(const uno::Reference<text::XTextRange>& xText
if (!bSuccess)
{
throw lang::IllegalArgumentException(
- "SwXContentControl::AttachImpl(): cannot create meta: range invalid?",
+ "SwXContentControl::AttachImpl(): cannot create content control: invalid range",
static_cast<::cppu::OWeakObject*>(this), 1);
}
if (!pTextAttr)
@@ -742,6 +744,19 @@ void SAL_CALL SwXContentControl::setPropertyValue(const OUString& rPropertyName,
}
}
}
+ else if (rPropertyName == UNO_NAME_LIST_ITEMS)
+ {
+ std::vector<SwContentControlListItem> aItems
+ = SwContentControlListItem::ItemsFromAny(rValue);
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aListItems = aItems;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetListItems(aItems);
+ }
+ }
else
{
throw beans::UnknownPropertyException();
@@ -808,6 +823,19 @@ uno::Any SAL_CALL SwXContentControl::getPropertyValue(const OUString& rPropertyN
aRet <<= m_pImpl->m_pContentControl->GetUncheckedState();
}
}
+ else if (rPropertyName == UNO_NAME_LIST_ITEMS)
+ {
+ std::vector<SwContentControlListItem> aItems;
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aItems = m_pImpl->m_aListItems;
+ }
+ else
+ {
+ aItems = m_pImpl->m_pContentControl->GetListItems();
+ }
+ SwContentControlListItem::ItemsToAny(aItems, aRet);
+ }
else
{
throw beans::UnknownPropertyException();
diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx
index 3da6c8097051..b580ed2cc982 100644
--- a/sw/source/core/unocore/unomap1.cxx
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -1027,6 +1027,7 @@ const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetContentControlProper
{ u"" UNO_NAME_CHECKED, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
{ u"" UNO_NAME_CHECKED_STATE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
{ u"" UNO_NAME_UNCHECKED_STATE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_LIST_ITEMS, 0, cppu::UnoType<uno::Sequence<uno::Sequence<beans::PropertyValue>>>::get(), PROPERTY_NONE, 0 },
{ u"", 0, css::uno::Type(), 0, 0 }
};
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx
index a0cd400b5145..32d515de2217 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -1030,6 +1030,7 @@ void SwWrtShell::InsertContentControl(SwContentControlType eType)
switch (eType)
{
case SwContentControlType::RICH_TEXT:
+ case SwContentControlType::DROP_DOWN_LIST:
{
pContentControl->SetShowingPlaceHolder(true);
if (!HasSelection())