docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / source / core / txtnode / attrcontentcontrol.cxx
blob901fa17013a3aab99a759c37a691ed725208c876
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 <unoport.hxx>
35 #include <wrtsh.hxx>
37 using namespace com::sun::star;
39 namespace
41 inline constexpr OUString CURRENT_DATE_FORMAT = u"YYYY-MM-DD"_ustr;
44 SwFormatContentControl* SwFormatContentControl::CreatePoolDefault(sal_uInt16 nWhich)
46 return new SwFormatContentControl(nWhich);
49 SwFormatContentControl::SwFormatContentControl(sal_uInt16 nWhich)
50 : SfxPoolItem(nWhich)
51 , m_pTextAttr(nullptr)
53 setNonShareable();
56 SwFormatContentControl::SwFormatContentControl(
57 const std::shared_ptr<SwContentControl>& pContentControl, sal_uInt16 nWhich)
58 : SfxPoolItem(nWhich)
59 , m_pContentControl(pContentControl)
60 , m_pTextAttr(nullptr)
62 setNonShareable();
63 if (!pContentControl)
65 SAL_WARN("sw.core", "SwFormatContentControl ctor: no pContentControl?");
67 // Not calling m_pContentControl->SetFormatContentControl(this) here; only from SetTextAttr.
70 SwFormatContentControl::~SwFormatContentControl()
72 if (m_pContentControl
73 // SwFormatContentControl is not shareable, so ptr compare is OK
74 && areSfxPoolItemPtrsEqual(m_pContentControl->GetFormatContentControl(), this))
76 NotifyChangeTextNode(nullptr);
77 m_pContentControl->SetFormatContentControl(nullptr);
81 bool SwFormatContentControl::operator==(const SfxPoolItem& rOther) const
83 return SfxPoolItem::operator==(rOther)
84 && m_pContentControl
85 == static_cast<const SwFormatContentControl&>(rOther).m_pContentControl;
88 SwFormatContentControl* SwFormatContentControl::Clone(SfxItemPool* /*pPool*/) const
90 // If this is indeed a copy, then DoCopy will be called later.
91 if (m_pContentControl)
93 return new SwFormatContentControl(m_pContentControl, Which());
95 else
97 return new SwFormatContentControl(Which());
101 void SwFormatContentControl::SetTextAttr(SwTextContentControl* pTextAttr)
103 if (m_pTextAttr && pTextAttr)
105 SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: already has a text attribute");
107 if (!m_pTextAttr && !pTextAttr)
109 SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: no attribute to remove");
111 m_pTextAttr = pTextAttr;
112 if (!m_pContentControl)
114 SAL_WARN("sw.core", "inserted SwFormatContentControl has no SwContentControl");
116 // The SwContentControl should be able to find the current text attribute.
117 if (m_pContentControl)
119 if (pTextAttr)
121 m_pContentControl->SetFormatContentControl(this);
123 // SwFormatContentControl is not shareable, so ptr compare is OK
124 else if (areSfxPoolItemPtrsEqual(m_pContentControl->GetFormatContentControl(), this))
126 // The text attribute is gone, so de-register from text node.
127 NotifyChangeTextNode(nullptr);
128 m_pContentControl->SetFormatContentControl(nullptr);
133 void SwFormatContentControl::NotifyChangeTextNode(SwTextNode* pTextNode)
135 // Not deleting m_pTextAttr here, SwNodes::ChgNode() doesn't do that, either.
136 if (!m_pContentControl)
138 SAL_WARN("sw.core", "SwFormatContentControl::NotifyChangeTextNode: no content control?");
140 if (m_pContentControl
141 // SwFormatContentControl is not shareable, so ptr compare is OK
142 && areSfxPoolItemPtrsEqual(m_pContentControl->GetFormatContentControl(), this))
144 // Not calling Modify, that would call SwXContentControl::SwClientNotify.
145 m_pContentControl->NotifyChangeTextNode(pTextNode);
149 SwTextNode* SwFormatContentControl::GetTextNode() const
151 if (!m_pContentControl)
153 return nullptr;
156 return m_pContentControl->GetTextNode();
159 // This SwFormatContentControl has been cloned and points at the same SwContentControl as the
160 // source: this function copies the SwContentControl.
161 void SwFormatContentControl::DoCopy(SwTextNode& rTargetTextNode)
163 if (!m_pContentControl)
165 SAL_WARN("sw.core", "SwFormatContentControl::DoCopy: called for SwFormatContentControl "
166 "with no SwContentControl.");
167 return;
170 m_pContentControl = std::make_shared<SwContentControl>(this);
171 m_pContentControl->NotifyChangeTextNode(&rTargetTextNode);
174 void SwFormatContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
176 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatContentControl"));
177 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
178 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pTextAttr"), "%p", m_pTextAttr);
179 SfxPoolItem::dumpAsXml(pWriter);
181 if (m_pContentControl)
183 m_pContentControl->dumpAsXml(pWriter);
186 (void)xmlTextWriterEndElement(pWriter);
189 SwContentControl::SwContentControl(SwFormatContentControl* pFormat)
190 : sw::BroadcastingModify()
191 , m_pFormat(pFormat)
192 , m_pTextNode(nullptr)
194 if (!pFormat)
196 return;
199 const std::shared_ptr<SwContentControl>& pOther = pFormat->GetContentControl();
200 if (!pOther)
202 return;
205 SetShowingPlaceHolder(pOther->m_bShowingPlaceHolder);
206 SetCheckbox(pOther->m_bCheckbox);
207 SetChecked(pOther->m_bChecked);
208 SetCheckedState(pOther->m_aCheckedState);
209 SetUncheckedState(pOther->m_aUncheckedState);
210 SetListItems(pOther->m_aListItems);
211 SetPicture(pOther->m_bPicture);
212 SetDate(pOther->m_bDate);
213 SetDateFormat(pOther->m_aDateFormat);
214 SetDateLanguage(pOther->m_aDateLanguage);
215 SetCurrentDate(pOther->m_aCurrentDate);
216 SetPlainText(pOther->m_bPlainText);
217 SetComboBox(pOther->m_bComboBox);
218 SetDropDown(pOther->m_bDropDown);
219 SetPlaceholderDocPart(pOther->m_aPlaceholderDocPart);
220 SetDataBindingPrefixMappings(pOther->m_aDataBindingPrefixMappings);
221 SetDataBindingXpath(pOther->m_aDataBindingXpath);
222 SetDataBindingStoreItemID(pOther->m_aDataBindingStoreItemID);
223 SetColor(pOther->m_aColor);
224 SetAppearance(pOther->m_aAppearance);
225 SetAlias(pOther->m_aAlias);
226 SetTag(pOther->m_aTag);
227 SetId(pOther->m_nId);
228 SetTabIndex(pOther->m_nTabIndex);
229 SetLock(pOther->m_aLock);
230 SetMultiLine(pOther->m_aMultiLine);
233 SwContentControl::~SwContentControl() {}
235 void SwContentControl::SetXContentControl(const rtl::Reference<SwXContentControl>& xContentControl)
237 m_wXContentControl = xContentControl.get();
240 SwTextContentControl* SwContentControl::GetTextAttr() const
242 return m_pFormat ? m_pFormat->GetTextAttr() : nullptr;
245 void SwContentControl::NotifyChangeTextNode(SwTextNode* pTextNode)
247 m_pTextNode = pTextNode;
248 if (m_pTextNode && (GetRegisteredIn() != m_pTextNode))
250 m_pTextNode->Add(*this);
252 else if (!m_pTextNode)
254 EndListeningAll();
256 if (!pTextNode)
258 // If the text node is gone, then invalidate clients (e.g. UNO object).
259 GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
263 void SwContentControl::SwClientNotify(const SwModify&, const SfxHint& rHint)
265 if (SfxHintId::SwRemoveUnoObject == rHint.GetId())
267 CallSwClientNotify(rHint);
268 GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged));
269 // Invalidate cached uno object.
270 SetXContentControl(nullptr);
271 GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
273 else if (rHint.GetId() == SfxHintId::SwLegacyModify
274 || rHint.GetId() == SfxHintId::SwFormatChange
275 || rHint.GetId() == SfxHintId::SwAttrSetChange
276 || rHint.GetId() == SfxHintId::SwObjectDying
277 || rHint.GetId() == SfxHintId::SwUpdateAttr)
279 CallSwClientNotify(rHint);
280 GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged));
284 std::optional<size_t> SwContentControl::GetSelectedListItem(bool bCheckDocModel) const
286 if (!bCheckDocModel || m_oSelectedListItem)
287 return m_oSelectedListItem;
289 const size_t nLen = GetListItems().size();
290 if (GetShowingPlaceHolder() || !nLen || !GetTextAttr())
291 return std::nullopt;
293 const OUString aText = GetTextAttr()->ToString();
294 for (size_t i = 0; i < nLen; ++i)
296 if (GetTextAttr()[i].ToString() == aText)
297 return i;
299 assert(!GetDropDown() && "DropDowns must always have an associated list item");
300 return std::nullopt;
303 bool SwContentControl::AddListItem(size_t nZIndex, const OUString& rDisplayText,
304 const OUString& rValue)
306 SwContentControlListItem aListItem;
307 if (rValue.isEmpty())
309 if (rDisplayText.isEmpty())
310 return false;
311 aListItem.m_aValue = rDisplayText;
313 else
315 aListItem.m_aValue = rValue;
316 aListItem.m_aDisplayText = rDisplayText;
319 // Avoid adding duplicates
320 for (auto& rListItem : GetListItems())
322 if (rListItem == aListItem)
323 return false;
326 const size_t nLen = GetListItems().size();
327 nZIndex = std::min(nZIndex, nLen);
328 const std::optional<size_t> oSelected = GetSelectedListItem();
329 if (oSelected && *oSelected >= nZIndex)
331 if (*oSelected < nLen)
332 SetSelectedListItem(*oSelected + 1);
334 std::vector<SwContentControlListItem> vListItems = GetListItems();
335 vListItems.insert(vListItems.begin() + nZIndex, aListItem);
336 SetListItems(vListItems);
337 return true;
340 void SwContentControl::DeleteListItem(size_t nZIndex)
342 if (nZIndex >= GetListItems().size())
343 return;
345 const std::optional<size_t> oSelected = GetSelectedListItem();
346 if (oSelected)
348 if (*oSelected == nZIndex)
350 SetSelectedListItem(std::nullopt);
351 if (m_bDropDown && GetTextAttr())
352 GetTextAttr()->Invalidate();
354 else if (*oSelected < nZIndex)
355 SetSelectedListItem(*oSelected - 1);
358 std::vector<SwContentControlListItem> vListItems = GetListItems();
359 vListItems.erase(vListItems.begin() + nZIndex);
360 SetListItems(vListItems);
361 return;
364 void SwContentControl::ClearListItems()
366 SetSelectedListItem(std::nullopt);
367 SetListItems(std::vector<SwContentControlListItem>());
368 if (m_bDropDown && GetTextAttr())
369 GetTextAttr()->Invalidate();
372 OUString SwContentControl::GetDateString() const
374 SwDoc& rDoc = m_pTextNode->GetDoc();
375 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
376 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(
377 m_aDateFormat, LanguageTag(m_aDateLanguage).getLanguageType());
379 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
381 // If not found, then create it.
382 sal_Int32 nCheckPos = 0;
383 SvNumFormatType nType;
384 OUString aFormat = m_aDateFormat;
385 pNumberFormatter->PutEntry(aFormat, nCheckPos, nType, nFormat,
386 LanguageTag(m_aDateLanguage).getLanguageType());
389 const Color* pColor = nullptr;
390 OUString aFormatted;
391 double fSelectedDate = 0;
392 if (m_oSelectedDate)
394 fSelectedDate = *m_oSelectedDate;
396 else
398 fSelectedDate = GetCurrentDateValue();
401 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
403 return OUString();
406 pNumberFormatter->GetOutputString(fSelectedDate, nFormat, aFormatted, &pColor, false);
407 return aFormatted;
410 void SwContentControl::SetCurrentDateValue(double fCurrentDate)
412 SwDoc& rDoc = m_pTextNode->GetDoc();
413 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
414 OUString aFormatted;
415 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US);
416 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
418 // If not found, then create it.
419 sal_Int32 nCheckPos = 0;
420 SvNumFormatType nType;
421 OUString sFormat = CURRENT_DATE_FORMAT;
422 pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
425 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
427 return;
430 const Color* pColor = nullptr;
431 pNumberFormatter->GetOutputString(fCurrentDate, nFormat, aFormatted, &pColor, false);
432 m_aCurrentDate = aFormatted + "T00:00:00Z";
435 double SwContentControl::GetCurrentDateValue() const
437 if (m_aCurrentDate.isEmpty())
439 return 0;
442 SwDoc& rDoc = m_pTextNode->GetDoc();
443 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
444 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US);
445 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
447 sal_Int32 nCheckPos = 0;
448 SvNumFormatType nType;
449 OUString sFormat = CURRENT_DATE_FORMAT;
450 pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
453 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
455 return 0;
458 double dCurrentDate = 0;
459 OUString aCurrentDate = m_aCurrentDate.replaceAll("T00:00:00Z", "");
460 (void)pNumberFormatter->IsNumberFormat(aCurrentDate, nFormat, dCurrentDate);
461 return dCurrentDate;
464 bool SwContentControl::IsInteractingCharacter(sal_Unicode cCh)
466 if (GetCheckbox())
468 return cCh == ' ';
471 if (GetPicture())
473 return cCh == '\r';
476 return false;
479 bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& rKeyCode)
481 switch (GetType())
483 case SwContentControlType::DROP_DOWN_LIST:
484 case SwContentControlType::COMBO_BOX:
485 case SwContentControlType::DATE:
487 // Alt-down opens the popup.
488 return rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_DOWN;
490 default:
491 break;
494 return false;
497 // NOTE: call SetReadWrite separately to implement true (un)locking.
498 // This is mostly a theoretical function; the lock state is mainly kept for round-tripping purposes.
499 // It is implemented here primarily for pointless VBA control, but with the intention that it
500 // could be made functionally useful as well for checkboxes/dropdowns/pictures.
501 // Returns whether the content (bControl=false) cannot be modified,
502 // or if the control cannot be deleted.
503 std::optional<bool> SwContentControl::GetLock(bool bControl) const
505 std::optional<bool> oLock;
506 if (m_aLock.isEmpty())
507 return oLock;
508 else if (m_aLock.equalsIgnoreAsciiCase("sdtContentLocked"))
509 oLock = true;
510 else if (m_aLock.equalsIgnoreAsciiCase("unlocked"))
511 oLock = false;
512 else if (m_aLock.equalsIgnoreAsciiCase("sdtLocked"))
513 oLock = bControl;
514 else if (m_aLock.equalsIgnoreAsciiCase("contentLocked"))
515 oLock = !bControl;
517 assert(oLock.has_value() && "invalid or unknown lock state");
518 return oLock;
521 void SwContentControl::SetLock(bool bLockContent, bool bLockControl)
523 if (!bLockContent && !bLockControl)
524 m_aLock = "unlocked";
525 else if (bLockContent && bLockControl)
526 m_aLock = "sdtContentLocked";
527 else if (bLockContent)
528 m_aLock = "contentLocked";
529 else
530 m_aLock = "sdtLocked";
533 SwContentControlType SwContentControl::GetType() const
535 if (m_bCheckbox)
537 return SwContentControlType::CHECKBOX;
540 if (m_bComboBox)
542 return SwContentControlType::COMBO_BOX;
545 if (m_bDropDown)
547 return SwContentControlType::DROP_DOWN_LIST;
550 if (m_bPicture)
552 return SwContentControlType::PICTURE;
555 if (m_bDate)
557 return SwContentControlType::DATE;
560 if (m_bPlainText)
562 return SwContentControlType::PLAIN_TEXT;
565 return SwContentControlType::RICH_TEXT;
568 void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
570 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl"));
571 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
572 (void)xmlTextWriterWriteFormatAttribute(
573 pWriter, BAD_CAST("showing-place-holder"), "%s",
574 BAD_CAST(OString::boolean(m_bShowingPlaceHolder).getStr()));
575 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checkbox"), "%s",
576 BAD_CAST(OString::boolean(m_bCheckbox).getStr()));
577 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checked"), "%s",
578 BAD_CAST(OString::boolean(m_bChecked).getStr()));
579 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checked-state"), "%s",
580 BAD_CAST(m_aCheckedState.toUtf8().getStr()));
581 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("unchecked-state"), "%s",
582 BAD_CAST(m_aUncheckedState.toUtf8().getStr()));
583 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("picture"),
584 BAD_CAST(OString::boolean(m_bPicture).getStr()));
585 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"),
586 BAD_CAST(OString::boolean(m_bDate).getStr()));
587 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-format"),
588 BAD_CAST(m_aDateFormat.toUtf8().getStr()));
589 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-language"),
590 BAD_CAST(m_aDateLanguage.toUtf8().getStr()));
591 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("current-date"),
592 BAD_CAST(m_aCurrentDate.toUtf8().getStr()));
593 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("plain-text"),
594 BAD_CAST(OString::boolean(m_bPlainText).getStr()));
595 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("combo-box"),
596 BAD_CAST(OString::boolean(m_bComboBox).getStr()));
597 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("drop-down"),
598 BAD_CAST(OString::boolean(m_bDropDown).getStr()));
599 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("placeholder-doc-part"),
600 BAD_CAST(m_aPlaceholderDocPart.toUtf8().getStr()));
601 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-prefix-mappings"),
602 BAD_CAST(m_aDataBindingPrefixMappings.toUtf8().getStr()));
603 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-xpath"),
604 BAD_CAST(m_aDataBindingXpath.toUtf8().getStr()));
605 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-store-item-id"),
606 BAD_CAST(m_aDataBindingStoreItemID.toUtf8().getStr()));
607 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("color"),
608 BAD_CAST(m_aColor.toUtf8().getStr()));
609 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("appearance"),
610 BAD_CAST(m_aAppearance.toUtf8().getStr()));
611 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("alias"),
612 BAD_CAST(m_aAlias.toUtf8().getStr()));
613 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("tag"), BAD_CAST(m_aTag.toUtf8().getStr()));
614 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"),
615 BAD_CAST(OString::number(m_nId).getStr()));
616 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("tab-index"),
617 BAD_CAST(OString::number(m_nTabIndex).getStr()));
618 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("lock"),
619 BAD_CAST(m_aLock.toUtf8().getStr()));
620 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("multiline"),
621 BAD_CAST(m_aMultiLine.toUtf8().getStr()));
623 if (!m_aListItems.empty())
625 for (const auto& rListItem : m_aListItems)
627 rListItem.dumpAsXml(pWriter);
631 (void)xmlTextWriterEndElement(pWriter);
634 void SwContentControlListItem::dumpAsXml(xmlTextWriterPtr pWriter) const
636 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlListItem"));
637 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
638 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("display-text"),
639 BAD_CAST(m_aDisplayText.toUtf8().getStr()));
640 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
641 BAD_CAST(m_aValue.toUtf8().getStr()));
643 (void)xmlTextWriterEndElement(pWriter);
646 const OUString& SwContentControlListItem::ToString() const
648 if (!m_aDisplayText.isEmpty())
650 return m_aDisplayText;
653 return m_aValue;
656 bool SwContentControlListItem::operator==(const SwContentControlListItem& rOther) const
658 return m_aDisplayText == rOther.m_aDisplayText && m_aValue == rOther.m_aValue;
661 void SwContentControlListItem::ItemsToAny(const std::vector<SwContentControlListItem>& rItems,
662 uno::Any& rVal)
664 uno::Sequence<uno::Sequence<beans::PropertyValue>> aRet(rItems.size());
666 uno::Sequence<beans::PropertyValue>* pRet = aRet.getArray();
667 for (size_t i = 0; i < rItems.size(); ++i)
669 const SwContentControlListItem& rItem = rItems[i];
670 pRet[i] = {
671 comphelper::makePropertyValue(u"DisplayText"_ustr, rItem.m_aDisplayText),
672 comphelper::makePropertyValue(u"Value"_ustr, rItem.m_aValue),
676 rVal <<= aRet;
679 std::vector<SwContentControlListItem>
680 SwContentControlListItem::ItemsFromAny(const css::uno::Any& rVal)
682 std::vector<SwContentControlListItem> aRet;
684 uno::Sequence<uno::Sequence<beans::PropertyValue>> aSequence;
685 rVal >>= aSequence;
686 for (const auto& rItem : aSequence)
688 comphelper::SequenceAsHashMap aMap(rItem);
689 SwContentControlListItem aItem;
690 auto it = aMap.find(u"DisplayText"_ustr);
691 if (it != aMap.end())
693 it->second >>= aItem.m_aDisplayText;
695 it = aMap.find(u"Value"_ustr);
696 if (it != aMap.end())
698 it->second >>= aItem.m_aValue;
700 aRet.push_back(aItem);
703 return aRet;
706 SwTextContentControl*
707 SwTextContentControl::CreateTextContentControl(SwDoc& rDoc, SwTextNode* pTargetTextNode,
708 const SfxPoolItemHolder& rHolder, sal_Int32 nStart,
709 sal_Int32 nEnd, bool bIsCopy)
711 if (bIsCopy)
713 // the item in rHolder is already cloned, now call DoCopy to copy the SwContentControl
714 assert(pTargetTextNode
715 && "SwTextContentControl ctor: cannot copy content control without target node");
716 SwFormatContentControl* pSwFormatContentControl(
717 static_cast<SwFormatContentControl*>(const_cast<SfxPoolItem*>(rHolder.getItem())));
718 pSwFormatContentControl->DoCopy(*pTargetTextNode);
720 SwContentControlManager* pManager = &rDoc.GetContentControlManager();
721 auto pTextContentControl(new SwTextContentControl(pManager, rHolder, nStart, nEnd));
722 return pTextContentControl;
725 SwTextContentControl::SwTextContentControl(SwContentControlManager* pManager,
726 const SfxPoolItemHolder& rAttr, sal_Int32 nStart,
727 sal_Int32 nEnd)
728 : SwTextAttr(rAttr, nStart)
729 , SwTextAttrNesting(rAttr, nStart, nEnd)
730 , m_pManager(pManager)
732 SwFormatContentControl& rSwFormatContentControl(
733 static_cast<SwFormatContentControl&>(GetAttr()));
734 rSwFormatContentControl.SetTextAttr(this);
735 SetHasDummyChar(true);
736 m_pManager->Insert(this);
739 SwTextContentControl::~SwTextContentControl()
741 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
742 if (rFormatContentControl.GetTextAttr() == this)
744 rFormatContentControl.SetTextAttr(nullptr);
748 void SwTextContentControl::ChgTextNode(SwTextNode* pNode)
750 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
751 if (rFormatContentControl.GetTextAttr() == this)
753 rFormatContentControl.NotifyChangeTextNode(pNode);
755 if (pNode)
757 m_pManager = &pNode->GetDoc().GetContentControlManager();
759 else
761 if (m_pManager)
763 m_pManager->Erase(this);
765 m_pManager = nullptr;
770 void SwTextContentControl::Delete(bool bSaveContents)
772 if (!GetTextNode())
773 return;
775 SwPaM aPaM(*GetTextNode(), GetStart(), *GetTextNode(), *End());
776 if (bSaveContents)
777 GetTextNode()->GetDoc().ResetAttrs(aPaM, /*bTextAttr=*/true, { RES_TXTATR_CONTENTCONTROL });
778 else
779 GetTextNode()->GetDoc().getIDocumentContentOperations().DeleteAndJoin(aPaM);
782 SwTextNode* SwTextContentControl::GetTextNode() const
784 auto& rFormatContentControl = static_cast<const SwFormatContentControl&>(GetAttr());
785 return rFormatContentControl.GetTextNode();
788 OUString SwTextContentControl::ToString() const
790 if (!GetTextNode())
791 return OUString();
793 // Don't select the text attribute itself at the start.
794 sal_Int32 nStart = GetStart() + 1;
795 // Don't select the CH_TXTATR_BREAKWORD itself at the end.
796 sal_Int32 nEnd = *End() - 1;
798 SwPaM aPaM(*GetTextNode(), nStart, *GetTextNode(), nEnd);
799 return aPaM.GetText();
802 void SwTextContentControl::Invalidate()
804 SwDocShell* pDocShell = GetTextNode() ? GetTextNode()->GetDoc().GetDocShell() : nullptr;
805 if (!pDocShell || !pDocShell->GetWrtShell())
806 return;
808 // save the cursor
809 // NOTE: needs further testing to see if this is adequate (i.e. in auto-run macros...)
810 pDocShell->GetWrtShell()->Push();
812 // visit the control in the text (which makes any necessary visual changes)
813 // NOTE: simply going to a checkbox causes a toggle, unless bOnlyRefresh
814 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
815 pDocShell->GetWrtShell()->GotoContentControl(rFormatContentControl, /*bOnlyRefresh=*/true);
817 pDocShell->GetWrtShell()->Pop(SwCursorShell::PopMode::DeleteCurrent);
820 void SwTextContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
822 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
823 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
824 SwTextAttr::dumpAsXml(pWriter);
826 (void)xmlTextWriterEndElement(pWriter);
829 SwContentControlManager::SwContentControlManager() {}
831 void SwContentControlManager::Insert(SwTextContentControl* pTextContentControl)
833 m_aContentControls.push_back(pTextContentControl);
836 void SwContentControlManager::Erase(SwTextContentControl* pTextContentControl)
838 std::erase(m_aContentControls, pTextContentControl);
841 SwTextContentControl* SwContentControlManager::Get(size_t nIndex)
843 // Only sort now: the items may not have an associated text node by the time they are inserted
844 // into the container.
845 std::sort(m_aContentControls.begin(), m_aContentControls.end(),
846 [](SwTextContentControl*& pLhs, SwTextContentControl*& pRhs) -> bool {
847 SwNodeOffset nIdxLHS = pLhs->GetTextNode()->GetIndex();
848 SwNodeOffset nIdxRHS = pRhs->GetTextNode()->GetIndex();
849 if (nIdxLHS == nIdxRHS)
851 return pLhs->GetStart() < pRhs->GetStart();
854 return nIdxLHS < nIdxRHS;
857 return m_aContentControls[nIndex];
860 SwTextContentControl* SwContentControlManager::UnsortedGet(size_t nIndex)
862 assert(nIndex < m_aContentControls.size());
863 return m_aContentControls[nIndex];
866 void SwContentControlManager::dumpAsXml(xmlTextWriterPtr pWriter) const
868 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlManager"));
869 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
870 for (const auto& pContentControl : m_aContentControls)
872 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
873 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pContentControl);
874 (void)xmlTextWriterEndElement(pWriter);
877 (void)xmlTextWriterEndElement(pWriter);
880 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */