1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
31 #include <textcontentcontrol.hxx>
33 #include <unocontentcontrol.hxx>
34 #include <unoport.hxx>
37 using namespace com::sun::star
;
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
)
51 , m_pTextAttr(nullptr)
56 SwFormatContentControl::SwFormatContentControl(
57 const std::shared_ptr
<SwContentControl
>& pContentControl
, sal_uInt16 nWhich
)
59 , m_pContentControl(pContentControl
)
60 , m_pTextAttr(nullptr)
65 SAL_WARN("sw.core", "SwFormatContentControl ctor: no pContentControl?");
67 // Not calling m_pContentControl->SetFormatContentControl(this) here; only from SetTextAttr.
70 SwFormatContentControl::~SwFormatContentControl()
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
)
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());
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
)
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
)
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.");
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()
192 , m_pTextNode(nullptr)
199 const std::shared_ptr
<SwContentControl
>& pOther
= pFormat
->GetContentControl();
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
)
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())
293 const OUString aText
= GetTextAttr()->ToString();
294 for (size_t i
= 0; i
< nLen
; ++i
)
296 if (GetTextAttr()[i
].ToString() == aText
)
299 assert(!GetDropDown() && "DropDowns must always have an associated list item");
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())
311 aListItem
.m_aValue
= rDisplayText
;
315 aListItem
.m_aValue
= rValue
;
316 aListItem
.m_aDisplayText
= rDisplayText
;
319 // Avoid adding duplicates
320 for (auto& rListItem
: GetListItems())
322 if (rListItem
== aListItem
)
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
);
340 void SwContentControl::DeleteListItem(size_t nZIndex
)
342 if (nZIndex
>= GetListItems().size())
345 const std::optional
<size_t> oSelected
= GetSelectedListItem();
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
);
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;
391 double fSelectedDate
= 0;
394 fSelectedDate
= *m_oSelectedDate
;
398 fSelectedDate
= GetCurrentDateValue();
401 if (nFormat
== NUMBERFORMAT_ENTRY_NOT_FOUND
)
406 pNumberFormatter
->GetOutputString(fSelectedDate
, nFormat
, aFormatted
, &pColor
, false);
410 void SwContentControl::SetCurrentDateValue(double fCurrentDate
)
412 SwDoc
& rDoc
= m_pTextNode
->GetDoc();
413 SvNumberFormatter
* pNumberFormatter
= rDoc
.GetNumberFormatter();
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
)
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())
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
)
458 double dCurrentDate
= 0;
459 OUString aCurrentDate
= m_aCurrentDate
.replaceAll("T00:00:00Z", "");
460 (void)pNumberFormatter
->IsNumberFormat(aCurrentDate
, nFormat
, dCurrentDate
);
464 bool SwContentControl::IsInteractingCharacter(sal_Unicode cCh
)
479 bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode
& rKeyCode
)
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
;
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())
508 else if (m_aLock
.equalsIgnoreAsciiCase("sdtContentLocked"))
510 else if (m_aLock
.equalsIgnoreAsciiCase("unlocked"))
512 else if (m_aLock
.equalsIgnoreAsciiCase("sdtLocked"))
514 else if (m_aLock
.equalsIgnoreAsciiCase("contentLocked"))
517 assert(oLock
.has_value() && "invalid or unknown lock state");
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";
530 m_aLock
= "sdtLocked";
533 SwContentControlType
SwContentControl::GetType() const
537 return SwContentControlType::CHECKBOX
;
542 return SwContentControlType::COMBO_BOX
;
547 return SwContentControlType::DROP_DOWN_LIST
;
552 return SwContentControlType::PICTURE
;
557 return SwContentControlType::DATE
;
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
;
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
,
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
];
671 comphelper::makePropertyValue(u
"DisplayText"_ustr
, rItem
.m_aDisplayText
),
672 comphelper::makePropertyValue(u
"Value"_ustr
, rItem
.m_aValue
),
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
;
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
);
706 SwTextContentControl
*
707 SwTextContentControl::CreateTextContentControl(SwDoc
& rDoc
, SwTextNode
* pTargetTextNode
,
708 const SfxPoolItemHolder
& rHolder
, sal_Int32 nStart
,
709 sal_Int32 nEnd
, bool 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
,
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
);
757 m_pManager
= &pNode
->GetDoc().GetContentControlManager();
763 m_pManager
->Erase(this);
765 m_pManager
= nullptr;
770 void SwTextContentControl::Delete(bool bSaveContents
)
775 SwPaM
aPaM(*GetTextNode(), GetStart(), *GetTextNode(), *End());
777 GetTextNode()->GetDoc().ResetAttrs(aPaM
, /*bTextAttr=*/true, { RES_TXTATR_CONTENTCONTROL
});
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
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())
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: */