Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / txtnode / attrcontentcontrol.cxx
blobc167f8da33e9adad15ba96e53b57c676a96cc61a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <formatcontentcontrol.hxx>
22 #include <libxml/xmlwriter.h>
24 #include <sal/log.hxx>
25 #include <comphelper/propertyvalue.hxx>
26 #include <comphelper/sequenceashashmap.hxx>
27 #include <svl/numformat.hxx>
28 #include <vcl/keycod.hxx>
30 #include <ndtxt.hxx>
31 #include <textcontentcontrol.hxx>
32 #include <doc.hxx>
33 #include <unocontentcontrol.hxx>
34 #include <wrtsh.hxx>
36 using namespace com::sun::star;
38 namespace
40 inline constexpr OUStringLiteral CURRENT_DATE_FORMAT = u"YYYY-MM-DD";
43 SwFormatContentControl* SwFormatContentControl::CreatePoolDefault(sal_uInt16 nWhich)
45 return new SwFormatContentControl(nWhich);
48 SwFormatContentControl::SwFormatContentControl(sal_uInt16 nWhich)
49 : SfxPoolItem(nWhich)
50 , m_pTextAttr(nullptr)
54 SwFormatContentControl::SwFormatContentControl(
55 const std::shared_ptr<SwContentControl>& pContentControl, sal_uInt16 nWhich)
56 : SfxPoolItem(nWhich)
57 , m_pContentControl(pContentControl)
58 , m_pTextAttr(nullptr)
60 if (!pContentControl)
62 SAL_WARN("sw.core", "SwFormatContentControl ctor: no pContentControl?");
64 // Not calling m_pContentControl->SetFormatContentControl(this) here; only from SetTextAttr.
67 SwFormatContentControl::~SwFormatContentControl()
69 if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this))
71 NotifyChangeTextNode(nullptr);
72 m_pContentControl->SetFormatContentControl(nullptr);
76 bool SwFormatContentControl::operator==(const SfxPoolItem& rOther) const
78 return SfxPoolItem::operator==(rOther)
79 && m_pContentControl
80 == static_cast<const SwFormatContentControl&>(rOther).m_pContentControl;
83 SwFormatContentControl* SwFormatContentControl::Clone(SfxItemPool* /*pPool*/) const
85 // If this is indeed a copy, then DoCopy will be called later.
86 if (m_pContentControl)
88 return new SwFormatContentControl(m_pContentControl, Which());
90 else
92 return new SwFormatContentControl(Which());
96 void SwFormatContentControl::SetTextAttr(SwTextContentControl* pTextAttr)
98 if (m_pTextAttr && pTextAttr)
100 SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: already has a text attribute");
102 if (!m_pTextAttr && !pTextAttr)
104 SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: no attribute to remove");
106 m_pTextAttr = pTextAttr;
107 if (!m_pContentControl)
109 SAL_WARN("sw.core", "inserted SwFormatContentControl has no SwContentControl");
111 // The SwContentControl should be able to find the current text attribute.
112 if (m_pContentControl)
114 if (pTextAttr)
116 m_pContentControl->SetFormatContentControl(this);
118 else if (m_pContentControl->GetFormatContentControl() == this)
120 // The text attribute is gone, so de-register from text node.
121 NotifyChangeTextNode(nullptr);
122 m_pContentControl->SetFormatContentControl(nullptr);
127 void SwFormatContentControl::NotifyChangeTextNode(SwTextNode* pTextNode)
129 // Not deleting m_pTextAttr here, SwNodes::ChgNode() doesn't do that, either.
130 if (!m_pContentControl)
132 SAL_WARN("sw.core", "SwFormatContentControl::NotifyChangeTextNode: no content control?");
134 if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this))
136 // Not calling Modify, that would call SwXContentControl::SwClientNotify.
137 m_pContentControl->NotifyChangeTextNode(pTextNode);
141 SwTextNode* SwFormatContentControl::GetTextNode() const
143 if (!m_pContentControl)
145 return nullptr;
148 return m_pContentControl->GetTextNode();
151 // This SwFormatContentControl has been cloned and points at the same SwContentControl as the
152 // source: this function copies the SwContentControl.
153 void SwFormatContentControl::DoCopy(SwTextNode& rTargetTextNode)
155 if (!m_pContentControl)
157 SAL_WARN("sw.core", "SwFormatContentControl::DoCopy: called for SwFormatContentControl "
158 "with no SwContentControl.");
159 return;
162 m_pContentControl = std::make_shared<SwContentControl>(this);
163 m_pContentControl->NotifyChangeTextNode(&rTargetTextNode);
166 void SwFormatContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
168 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatContentControl"));
169 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
170 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pTextAttr"), "%p", m_pTextAttr);
171 SfxPoolItem::dumpAsXml(pWriter);
173 if (m_pContentControl)
175 m_pContentControl->dumpAsXml(pWriter);
178 (void)xmlTextWriterEndElement(pWriter);
181 SwContentControl::SwContentControl(SwFormatContentControl* pFormat)
182 : sw::BroadcastingModify()
183 , m_pFormat(pFormat)
184 , m_pTextNode(nullptr)
186 if (!pFormat)
188 return;
191 const std::shared_ptr<SwContentControl>& pOther = pFormat->GetContentControl();
192 if (!pOther)
194 return;
197 SetShowingPlaceHolder(pOther->m_bShowingPlaceHolder);
198 SetCheckbox(pOther->m_bCheckbox);
199 SetChecked(pOther->m_bChecked);
200 SetCheckedState(pOther->m_aCheckedState);
201 SetUncheckedState(pOther->m_aUncheckedState);
202 SetListItems(pOther->m_aListItems);
203 SetPicture(pOther->m_bPicture);
204 SetDate(pOther->m_bDate);
205 SetDateFormat(pOther->m_aDateFormat);
206 SetDateLanguage(pOther->m_aDateLanguage);
207 SetCurrentDate(pOther->m_aCurrentDate);
208 SetPlainText(pOther->m_bPlainText);
209 SetComboBox(pOther->m_bComboBox);
210 SetDropDown(pOther->m_bDropDown);
211 SetPlaceholderDocPart(pOther->m_aPlaceholderDocPart);
212 SetDataBindingPrefixMappings(pOther->m_aDataBindingPrefixMappings);
213 SetDataBindingXpath(pOther->m_aDataBindingXpath);
214 SetDataBindingStoreItemID(pOther->m_aDataBindingStoreItemID);
215 SetColor(pOther->m_aColor);
216 SetAppearance(pOther->m_aAppearance);
217 SetAlias(pOther->m_aAlias);
218 SetTag(pOther->m_aTag);
219 SetId(pOther->m_nId);
220 SetTabIndex(pOther->m_nTabIndex);
221 SetLock(pOther->m_aLock);
224 SwContentControl::~SwContentControl() {}
226 void SwContentControl::SetXContentControl(const rtl::Reference<SwXContentControl>& xContentControl)
228 m_wXContentControl = xContentControl.get();
231 SwTextContentControl* SwContentControl::GetTextAttr() const
233 return m_pFormat ? m_pFormat->GetTextAttr() : nullptr;
236 void SwContentControl::NotifyChangeTextNode(SwTextNode* pTextNode)
238 m_pTextNode = pTextNode;
239 if (m_pTextNode && (GetRegisteredIn() != m_pTextNode))
241 m_pTextNode->Add(this);
243 else if (!m_pTextNode)
245 EndListeningAll();
247 if (!pTextNode)
249 // If the text node is gone, then invalidate clients (e.g. UNO object).
250 GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
254 void SwContentControl::SwClientNotify(const SwModify&, const SfxHint& rHint)
256 if (rHint.GetId() != SfxHintId::SwLegacyModify)
257 return;
259 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
260 CallSwClientNotify(rHint);
261 GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged));
263 if (pLegacy->GetWhich() == RES_REMOVE_UNO_OBJECT)
265 // Invalidate cached uno object.
266 SetXContentControl(nullptr);
267 GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
271 std::optional<size_t> SwContentControl::GetSelectedListItem(bool bCheckDocModel) const
273 if (!bCheckDocModel || m_oSelectedListItem)
274 return m_oSelectedListItem;
276 const size_t nLen = GetListItems().size();
277 if (GetShowingPlaceHolder() || !nLen || !GetTextAttr())
278 return std::nullopt;
280 const OUString& rText = GetTextAttr()->ToString();
281 for (size_t i = 0; i < nLen; ++i)
283 if (GetTextAttr()[i].ToString() == rText)
284 return i;
286 assert(!GetDropDown() && "DropDowns must always have an associated list item");
287 return std::nullopt;
290 bool SwContentControl::AddListItem(size_t nZIndex, const OUString& rDisplayText,
291 const OUString& rValue)
293 SwContentControlListItem aListItem;
294 if (rValue.isEmpty())
296 if (rDisplayText.isEmpty())
297 return false;
298 aListItem.m_aValue = rDisplayText;
300 else
302 aListItem.m_aValue = rValue;
303 aListItem.m_aDisplayText = rDisplayText;
306 // Avoid adding duplicates
307 for (auto& rListItem : GetListItems())
309 if (rListItem == aListItem)
310 return false;
313 const size_t nLen = GetListItems().size();
314 nZIndex = std::min(nZIndex, nLen);
315 const std::optional<size_t> oSelected = GetSelectedListItem();
316 if (oSelected && *oSelected >= nZIndex)
318 if (*oSelected < nLen)
319 SetSelectedListItem(*oSelected + 1);
321 std::vector<SwContentControlListItem> vListItems = GetListItems();
322 vListItems.insert(vListItems.begin() + nZIndex, aListItem);
323 SetListItems(vListItems);
324 return true;
327 void SwContentControl::DeleteListItem(size_t nZIndex)
329 if (nZIndex >= GetListItems().size())
330 return;
332 const std::optional<size_t> oSelected = GetSelectedListItem();
333 if (oSelected)
335 if (*oSelected == nZIndex)
337 SetSelectedListItem(std::nullopt);
338 if (m_bDropDown && GetTextAttr())
339 GetTextAttr()->Invalidate();
341 else if (*oSelected < nZIndex)
342 SetSelectedListItem(*oSelected - 1);
345 std::vector<SwContentControlListItem> vListItems = GetListItems();
346 vListItems.erase(vListItems.begin() + nZIndex);
347 SetListItems(vListItems);
348 return;
351 void SwContentControl::ClearListItems()
353 SetSelectedListItem(std::nullopt);
354 SetListItems(std::vector<SwContentControlListItem>());
355 if (m_bDropDown && GetTextAttr())
356 GetTextAttr()->Invalidate();
359 OUString SwContentControl::GetDateString() const
361 SwDoc& rDoc = m_pTextNode->GetDoc();
362 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
363 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(
364 m_aDateFormat, LanguageTag(m_aDateLanguage).getLanguageType());
366 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
368 // If not found, then create it.
369 sal_Int32 nCheckPos = 0;
370 SvNumFormatType nType;
371 OUString aFormat = m_aDateFormat;
372 pNumberFormatter->PutEntry(aFormat, nCheckPos, nType, nFormat,
373 LanguageTag(m_aDateLanguage).getLanguageType());
376 const Color* pColor = nullptr;
377 OUString aFormatted;
378 double fSelectedDate = 0;
379 if (m_oSelectedDate)
381 fSelectedDate = *m_oSelectedDate;
383 else
385 fSelectedDate = GetCurrentDateValue();
388 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
390 return OUString();
393 pNumberFormatter->GetOutputString(fSelectedDate, nFormat, aFormatted, &pColor, false);
394 return aFormatted;
397 void SwContentControl::SetCurrentDateValue(double fCurrentDate)
399 SwDoc& rDoc = m_pTextNode->GetDoc();
400 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
401 OUString aFormatted;
402 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US);
403 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
405 // If not found, then create it.
406 sal_Int32 nCheckPos = 0;
407 SvNumFormatType nType;
408 OUString sFormat = CURRENT_DATE_FORMAT;
409 pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
412 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
414 return;
417 const Color* pColor = nullptr;
418 pNumberFormatter->GetOutputString(fCurrentDate, nFormat, aFormatted, &pColor, false);
419 m_aCurrentDate = aFormatted + "T00:00:00Z";
422 double SwContentControl::GetCurrentDateValue() const
424 if (m_aCurrentDate.isEmpty())
426 return 0;
429 SwDoc& rDoc = m_pTextNode->GetDoc();
430 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
431 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US);
432 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
434 sal_Int32 nCheckPos = 0;
435 SvNumFormatType nType;
436 OUString sFormat = CURRENT_DATE_FORMAT;
437 pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
440 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
442 return 0;
445 double dCurrentDate = 0;
446 OUString aCurrentDate = m_aCurrentDate.replaceAll("T00:00:00Z", "");
447 (void)pNumberFormatter->IsNumberFormat(aCurrentDate, nFormat, dCurrentDate);
448 return dCurrentDate;
451 bool SwContentControl::IsInteractingCharacter(sal_Unicode cCh)
453 if (GetCheckbox())
455 return cCh == ' ';
458 if (GetPicture())
460 return cCh == '\r';
463 return false;
466 bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& rKeyCode)
468 switch (GetType())
470 case SwContentControlType::DROP_DOWN_LIST:
471 case SwContentControlType::COMBO_BOX:
472 case SwContentControlType::DATE:
474 // Alt-down opens the popup.
475 return rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_DOWN;
477 default:
478 break;
481 return false;
484 // NOTE: call SetReadWrite separately to implement true (un)locking.
485 // This is mostly a theoretical function; the lock state is mainly kept for round-tripping purposes.
486 // It is implemented here primarily for pointless VBA control, but with the intention that it
487 // could be made functionally useful as well for checkboxes/dropdowns/pictures.
488 // Returns whether the content (bControl=false) cannot be modified,
489 // or if the control cannot be deleted.
490 std::optional<bool> SwContentControl::GetLock(bool bControl) const
492 std::optional<bool> oLock;
493 if (m_aLock.isEmpty())
494 return oLock;
495 else if (m_aLock.equalsIgnoreAsciiCase("sdtContentLocked"))
496 oLock = true;
497 else if (m_aLock.equalsIgnoreAsciiCase("unlocked"))
498 oLock = false;
499 else if (m_aLock.equalsIgnoreAsciiCase("sdtLocked"))
500 oLock = bControl;
501 else if (m_aLock.equalsIgnoreAsciiCase("contentLocked"))
502 oLock = !bControl;
504 assert(oLock && "invalid or unknown lock state");
505 return oLock;
508 void SwContentControl::SetLock(bool bLockContent, bool bLockControl)
510 if (!bLockContent && !bLockControl)
511 m_aLock = "unlocked";
512 else if (bLockContent && bLockControl)
513 m_aLock = "sdtContentLocked";
514 else if (bLockContent)
515 m_aLock = "contentLocked";
516 else
517 m_aLock = "sdtLocked";
520 SwContentControlType SwContentControl::GetType() const
522 if (m_bCheckbox)
524 return SwContentControlType::CHECKBOX;
527 if (m_bComboBox)
529 return SwContentControlType::COMBO_BOX;
532 if (m_bDropDown)
534 return SwContentControlType::DROP_DOWN_LIST;
537 if (m_bPicture)
539 return SwContentControlType::PICTURE;
542 if (m_bDate)
544 return SwContentControlType::DATE;
547 if (m_bPlainText)
549 return SwContentControlType::PLAIN_TEXT;
552 return SwContentControlType::RICH_TEXT;
555 void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
557 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl"));
558 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
559 (void)xmlTextWriterWriteFormatAttribute(
560 pWriter, BAD_CAST("showing-place-holder"), "%s",
561 BAD_CAST(OString::boolean(m_bShowingPlaceHolder).getStr()));
562 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checkbox"), "%s",
563 BAD_CAST(OString::boolean(m_bCheckbox).getStr()));
564 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checked"), "%s",
565 BAD_CAST(OString::boolean(m_bChecked).getStr()));
566 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checked-state"), "%s",
567 BAD_CAST(m_aCheckedState.toUtf8().getStr()));
568 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("unchecked-state"), "%s",
569 BAD_CAST(m_aUncheckedState.toUtf8().getStr()));
570 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("picture"),
571 BAD_CAST(OString::boolean(m_bPicture).getStr()));
572 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"),
573 BAD_CAST(OString::boolean(m_bDate).getStr()));
574 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-format"),
575 BAD_CAST(m_aDateFormat.toUtf8().getStr()));
576 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-language"),
577 BAD_CAST(m_aDateLanguage.toUtf8().getStr()));
578 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("current-date"),
579 BAD_CAST(m_aCurrentDate.toUtf8().getStr()));
580 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("plain-text"),
581 BAD_CAST(OString::boolean(m_bPlainText).getStr()));
582 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("combo-box"),
583 BAD_CAST(OString::boolean(m_bComboBox).getStr()));
584 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("drop-down"),
585 BAD_CAST(OString::boolean(m_bDropDown).getStr()));
586 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("placeholder-doc-part"),
587 BAD_CAST(m_aPlaceholderDocPart.toUtf8().getStr()));
588 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-prefix-mappings"),
589 BAD_CAST(m_aDataBindingPrefixMappings.toUtf8().getStr()));
590 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-xpath"),
591 BAD_CAST(m_aDataBindingXpath.toUtf8().getStr()));
592 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-store-item-id"),
593 BAD_CAST(m_aDataBindingStoreItemID.toUtf8().getStr()));
594 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("color"),
595 BAD_CAST(m_aColor.toUtf8().getStr()));
596 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("appearance"),
597 BAD_CAST(m_aAppearance.toUtf8().getStr()));
598 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("alias"),
599 BAD_CAST(m_aAlias.toUtf8().getStr()));
600 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("tag"), BAD_CAST(m_aTag.toUtf8().getStr()));
601 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"),
602 BAD_CAST(OString::number(m_nId).getStr()));
603 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("tab-index"),
604 BAD_CAST(OString::number(m_nTabIndex).getStr()));
605 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("lock"),
606 BAD_CAST(m_aLock.toUtf8().getStr()));
608 if (!m_aListItems.empty())
610 for (const auto& rListItem : m_aListItems)
612 rListItem.dumpAsXml(pWriter);
616 (void)xmlTextWriterEndElement(pWriter);
619 void SwContentControlListItem::dumpAsXml(xmlTextWriterPtr pWriter) const
621 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlListItem"));
622 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
623 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("display-text"),
624 BAD_CAST(m_aDisplayText.toUtf8().getStr()));
625 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
626 BAD_CAST(m_aValue.toUtf8().getStr()));
628 (void)xmlTextWriterEndElement(pWriter);
631 const OUString& SwContentControlListItem::ToString() const
633 if (!m_aDisplayText.isEmpty())
635 return m_aDisplayText;
638 return m_aValue;
641 bool SwContentControlListItem::operator==(const SwContentControlListItem& rOther) const
643 return m_aDisplayText == rOther.m_aDisplayText && m_aValue == rOther.m_aValue;
646 void SwContentControlListItem::ItemsToAny(const std::vector<SwContentControlListItem>& rItems,
647 uno::Any& rVal)
649 uno::Sequence<uno::Sequence<beans::PropertyValue>> aRet(rItems.size());
651 uno::Sequence<beans::PropertyValue>* pRet = aRet.getArray();
652 for (size_t i = 0; i < rItems.size(); ++i)
654 const SwContentControlListItem& rItem = rItems[i];
655 uno::Sequence<beans::PropertyValue> aItem = {
656 comphelper::makePropertyValue("DisplayText", rItem.m_aDisplayText),
657 comphelper::makePropertyValue("Value", rItem.m_aValue),
659 pRet[i] = aItem;
662 rVal <<= aRet;
665 std::vector<SwContentControlListItem>
666 SwContentControlListItem::ItemsFromAny(const css::uno::Any& rVal)
668 std::vector<SwContentControlListItem> aRet;
670 uno::Sequence<uno::Sequence<beans::PropertyValue>> aSequence;
671 rVal >>= aSequence;
672 for (const auto& rItem : aSequence)
674 comphelper::SequenceAsHashMap aMap(rItem);
675 SwContentControlListItem aItem;
676 auto it = aMap.find("DisplayText");
677 if (it != aMap.end())
679 it->second >>= aItem.m_aDisplayText;
681 it = aMap.find("Value");
682 if (it != aMap.end())
684 it->second >>= aItem.m_aValue;
686 aRet.push_back(aItem);
689 return aRet;
692 SwTextContentControl* SwTextContentControl::CreateTextContentControl(SwDoc& rDoc,
693 SwTextNode* pTargetTextNode,
694 SwFormatContentControl& rAttr,
695 sal_Int32 nStart,
696 sal_Int32 nEnd, bool bIsCopy)
698 if (bIsCopy)
700 // rAttr is already cloned, now call DoCopy to copy the SwContentControl
701 if (!pTargetTextNode)
703 SAL_WARN("sw.core",
704 "SwTextContentControl ctor: cannot copy content control without target node");
706 rAttr.DoCopy(*pTargetTextNode);
708 SwContentControlManager* pManager = &rDoc.GetContentControlManager();
709 auto pTextContentControl(new SwTextContentControl(pManager, rAttr, nStart, nEnd));
710 return pTextContentControl;
713 SwTextContentControl::SwTextContentControl(SwContentControlManager* pManager,
714 SwFormatContentControl& rAttr, sal_Int32 nStart,
715 sal_Int32 nEnd)
716 : SwTextAttr(rAttr, nStart)
717 , SwTextAttrNesting(rAttr, nStart, nEnd)
718 , m_pManager(pManager)
720 rAttr.SetTextAttr(this);
721 SetHasDummyChar(true);
722 m_pManager->Insert(this);
725 SwTextContentControl::~SwTextContentControl()
727 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
728 if (rFormatContentControl.GetTextAttr() == this)
730 rFormatContentControl.SetTextAttr(nullptr);
734 void SwTextContentControl::ChgTextNode(SwTextNode* pNode)
736 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
737 if (rFormatContentControl.GetTextAttr() == this)
739 rFormatContentControl.NotifyChangeTextNode(pNode);
741 if (pNode)
743 m_pManager = &pNode->GetDoc().GetContentControlManager();
745 else
747 if (m_pManager)
749 m_pManager->Erase(this);
751 m_pManager = nullptr;
756 void SwTextContentControl::Delete(bool bSaveContents)
758 if (!GetTextNode())
759 return;
761 SwPaM aPaM(*GetTextNode(), GetStart(), *GetTextNode(), *End());
762 if (bSaveContents)
763 GetTextNode()->GetDoc().ResetAttrs(aPaM, /*bTextAttr=*/true, { RES_TXTATR_CONTENTCONTROL });
764 else
765 GetTextNode()->GetDoc().getIDocumentContentOperations().DeleteAndJoin(aPaM);
768 SwTextNode* SwTextContentControl::GetTextNode() const
770 auto& rFormatContentControl = static_cast<const SwFormatContentControl&>(GetAttr());
771 return rFormatContentControl.GetTextNode();
774 OUString SwTextContentControl::ToString() const
776 if (!GetTextNode())
777 return OUString();
779 // Don't select the text attribute itself at the start.
780 sal_Int32 nStart = GetStart() + 1;
781 // Don't select the CH_TXTATR_BREAKWORD itself at the end.
782 sal_Int32 nEnd = *End() - 1;
784 SwPaM aPaM(*GetTextNode(), nStart, *GetTextNode(), nEnd);
785 return aPaM.GetText();
788 void SwTextContentControl::Invalidate()
790 SwDocShell* pDocShell = GetTextNode() ? GetTextNode()->GetDoc().GetDocShell() : nullptr;
791 if (!pDocShell || !pDocShell->GetWrtShell())
792 return;
794 // save the cursor
795 // NOTE: needs further testing to see if this is adequate (i.e. in auto-run macros...)
796 pDocShell->GetWrtShell()->Push();
798 // visit the control in the text (which makes any necessary visual changes)
799 // NOTE: simply going to a checkbox causes a toggle, unless bOnlyRefresh
800 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
801 pDocShell->GetWrtShell()->GotoContentControl(rFormatContentControl, /*bOnlyRefresh=*/true);
803 pDocShell->GetWrtShell()->Pop(SwCursorShell::PopMode::DeleteCurrent);
806 void SwTextContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
808 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
809 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
810 SwTextAttr::dumpAsXml(pWriter);
812 (void)xmlTextWriterEndElement(pWriter);
815 SwContentControlManager::SwContentControlManager() {}
817 void SwContentControlManager::Insert(SwTextContentControl* pTextContentControl)
819 m_aContentControls.push_back(pTextContentControl);
822 void SwContentControlManager::Erase(SwTextContentControl* pTextContentControl)
824 m_aContentControls.erase(
825 std::remove(m_aContentControls.begin(), m_aContentControls.end(), pTextContentControl),
826 m_aContentControls.end());
829 SwTextContentControl* SwContentControlManager::Get(size_t nIndex)
831 // Only sort now: the items may not have an associated text node by the time they are inserted
832 // into the container.
833 std::sort(m_aContentControls.begin(), m_aContentControls.end(),
834 [](SwTextContentControl*& pLhs, SwTextContentControl*& pRhs) -> bool {
835 SwNodeOffset nIdxLHS = pLhs->GetTextNode()->GetIndex();
836 SwNodeOffset nIdxRHS = pRhs->GetTextNode()->GetIndex();
837 if (nIdxLHS == nIdxRHS)
839 return pLhs->GetStart() < pRhs->GetStart();
842 return nIdxLHS < nIdxRHS;
845 return m_aContentControls[nIndex];
848 SwTextContentControl* SwContentControlManager::UnsortedGet(size_t nIndex)
850 assert(nIndex < m_aContentControls.size());
851 return m_aContentControls[nIndex];
854 void SwContentControlManager::dumpAsXml(xmlTextWriterPtr pWriter) const
856 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlManager"));
857 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
858 for (const auto& pContentControl : m_aContentControls)
860 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
861 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pContentControl);
862 (void)xmlTextWriterEndElement(pWriter);
865 (void)xmlTextWriterEndElement(pWriter);
868 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */