LibreOffice Module sw (master) 1
attrcontentcontrol.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21
22#include <libxml/xmlwriter.h>
23
24#include <sal/log.hxx>
27#include <svl/numformat.hxx>
28#include <vcl/keycod.hxx>
29
30#include <ndtxt.hxx>
32#include <doc.hxx>
33#include <unocontentcontrol.hxx>
34#include <wrtsh.hxx>
35
36using namespace com::sun::star;
37
38namespace
39{
40inline constexpr OUStringLiteral CURRENT_DATE_FORMAT = u"YYYY-MM-DD";
41}
42
44{
45 return new SwFormatContentControl(nWhich);
46}
47
49 : SfxPoolItem(nWhich)
50 , m_pTextAttr(nullptr)
51{
52}
53
55 const std::shared_ptr<SwContentControl>& pContentControl, sal_uInt16 nWhich)
56 : SfxPoolItem(nWhich)
57 , m_pContentControl(pContentControl)
58 , m_pTextAttr(nullptr)
59{
60 if (!pContentControl)
61 {
62 SAL_WARN("sw.core", "SwFormatContentControl ctor: no pContentControl?");
63 }
64 // Not calling m_pContentControl->SetFormatContentControl(this) here; only from SetTextAttr.
65}
66
68{
69 if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this))
70 {
71 NotifyChangeTextNode(nullptr);
72 m_pContentControl->SetFormatContentControl(nullptr);
73 }
74}
75
77{
78 return SfxPoolItem::operator==(rOther)
80 == static_cast<const SwFormatContentControl&>(rOther).m_pContentControl;
81}
82
84{
85 // If this is indeed a copy, then DoCopy will be called later.
87 {
89 }
90 else
91 {
92 return new SwFormatContentControl(Which());
93 }
94}
95
97{
98 if (m_pTextAttr && pTextAttr)
99 {
100 SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: already has a text attribute");
101 }
102 if (!m_pTextAttr && !pTextAttr)
103 {
104 SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: no attribute to remove");
105 }
106 m_pTextAttr = pTextAttr;
108 {
109 SAL_WARN("sw.core", "inserted SwFormatContentControl has no SwContentControl");
110 }
111 // The SwContentControl should be able to find the current text attribute.
113 {
114 if (pTextAttr)
115 {
116 m_pContentControl->SetFormatContentControl(this);
117 }
118 else if (m_pContentControl->GetFormatContentControl() == this)
119 {
120 // The text attribute is gone, so de-register from text node.
121 NotifyChangeTextNode(nullptr);
122 m_pContentControl->SetFormatContentControl(nullptr);
123 }
124 }
125}
126
128{
129 // Not deleting m_pTextAttr here, SwNodes::ChgNode() doesn't do that, either.
131 {
132 SAL_WARN("sw.core", "SwFormatContentControl::NotifyChangeTextNode: no content control?");
133 }
134 if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this))
135 {
136 // Not calling Modify, that would call SwXContentControl::SwClientNotify.
137 m_pContentControl->NotifyChangeTextNode(pTextNode);
138 }
139}
140
142{
144 {
145 return nullptr;
146 }
147
148 return m_pContentControl->GetTextNode();
149}
150
151// This SwFormatContentControl has been cloned and points at the same SwContentControl as the
152// source: this function copies the SwContentControl.
154{
156 {
157 SAL_WARN("sw.core", "SwFormatContentControl::DoCopy: called for SwFormatContentControl "
158 "with no SwContentControl.");
159 return;
160 }
161
162 m_pContentControl = std::make_shared<SwContentControl>(this);
163 m_pContentControl->NotifyChangeTextNode(&rTargetTextNode);
164}
165
167{
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);
172
174 {
175 m_pContentControl->dumpAsXml(pWriter);
176 }
177
178 (void)xmlTextWriterEndElement(pWriter);
179}
180
182 : sw::BroadcastingModify()
183 , m_pFormat(pFormat)
184 , m_pTextNode(nullptr)
185{
186 if (!pFormat)
187 {
188 return;
189 }
190
191 const std::shared_ptr<SwContentControl>& pOther = pFormat->GetContentControl();
192 if (!pOther)
193 {
194 return;
195 }
196
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);
222}
223
225
227{
228 m_wXContentControl = xContentControl.get();
229}
230
232{
233 return m_pFormat ? m_pFormat->GetTextAttr() : nullptr;
234}
235
237{
238 m_pTextNode = pTextNode;
239 if (m_pTextNode && (GetRegisteredIn() != m_pTextNode))
240 {
241 m_pTextNode->Add(this);
242 }
243 else if (!m_pTextNode)
244 {
245 EndListeningAll();
246 }
247 if (!pTextNode)
248 {
249 // If the text node is gone, then invalidate clients (e.g. UNO object).
250 GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
251 }
252}
253
255{
256 if (rHint.GetId() != SfxHintId::SwLegacyModify)
257 return;
258
259 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
260 CallSwClientNotify(rHint);
261 GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged));
262
263 if (pLegacy->GetWhich() == RES_REMOVE_UNO_OBJECT)
264 {
265 // Invalidate cached uno object.
266 SetXContentControl(nullptr);
267 GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
268 }
269}
270
271std::optional<size_t> SwContentControl::GetSelectedListItem(bool bCheckDocModel) const
272{
273 if (!bCheckDocModel || m_oSelectedListItem)
274 return m_oSelectedListItem;
275
276 const size_t nLen = GetListItems().size();
277 if (GetShowingPlaceHolder() || !nLen || !GetTextAttr())
278 return std::nullopt;
279
280 const OUString& rText = GetTextAttr()->ToString();
281 for (size_t i = 0; i < nLen; ++i)
282 {
283 if (GetTextAttr()[i].ToString() == rText)
284 return i;
285 }
286 assert(!GetDropDown() && "DropDowns must always have an associated list item");
287 return std::nullopt;
288}
289
290bool SwContentControl::AddListItem(size_t nZIndex, const OUString& rDisplayText,
291 const OUString& rValue)
292{
293 SwContentControlListItem aListItem;
294 if (rValue.isEmpty())
295 {
296 if (rDisplayText.isEmpty())
297 return false;
298 aListItem.m_aValue = rDisplayText;
299 }
300 else
301 {
302 aListItem.m_aValue = rValue;
303 aListItem.m_aDisplayText = rDisplayText;
304 }
305
306 // Avoid adding duplicates
307 for (auto& rListItem : GetListItems())
308 {
309 if (rListItem == aListItem)
310 return false;
311 }
312
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)
317 {
318 if (*oSelected < nLen)
319 SetSelectedListItem(*oSelected + 1);
320 }
321 std::vector<SwContentControlListItem> vListItems = GetListItems();
322 vListItems.insert(vListItems.begin() + nZIndex, aListItem);
323 SetListItems(vListItems);
324 return true;
325}
326
328{
329 if (nZIndex >= GetListItems().size())
330 return;
331
332 const std::optional<size_t> oSelected = GetSelectedListItem();
333 if (oSelected)
334 {
335 if (*oSelected == nZIndex)
336 {
337 SetSelectedListItem(std::nullopt);
338 if (m_bDropDown && GetTextAttr())
340 }
341 else if (*oSelected < nZIndex)
342 SetSelectedListItem(*oSelected - 1);
343 }
344
345 std::vector<SwContentControlListItem> vListItems = GetListItems();
346 vListItems.erase(vListItems.begin() + nZIndex);
347 SetListItems(vListItems);
348 return;
349}
350
352{
353 SetSelectedListItem(std::nullopt);
354 SetListItems(std::vector<SwContentControlListItem>());
355 if (m_bDropDown && GetTextAttr())
357}
358
360{
361 SwDoc& rDoc = m_pTextNode->GetDoc();
362 SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
363 sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(
364 m_aDateFormat, LanguageTag(m_aDateLanguage).getLanguageType());
365
366 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
367 {
368 // If not found, then create it.
369 sal_Int32 nCheckPos = 0;
371 OUString aFormat = m_aDateFormat;
372 pNumberFormatter->PutEntry(aFormat, nCheckPos, nType, nFormat,
373 LanguageTag(m_aDateLanguage).getLanguageType());
374 }
375
376 const Color* pColor = nullptr;
377 OUString aFormatted;
378 double fSelectedDate = 0;
379 if (m_oSelectedDate)
380 {
381 fSelectedDate = *m_oSelectedDate;
382 }
383 else
384 {
385 fSelectedDate = GetCurrentDateValue();
386 }
387
388 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
389 {
390 return OUString();
391 }
392
393 pNumberFormatter->GetOutputString(fSelectedDate, nFormat, aFormatted, &pColor, false);
394 return aFormatted;
395}
396
398{
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)
404 {
405 // If not found, then create it.
406 sal_Int32 nCheckPos = 0;
408 OUString sFormat = CURRENT_DATE_FORMAT;
409 pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
410 }
411
412 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
413 {
414 return;
415 }
416
417 const Color* pColor = nullptr;
418 pNumberFormatter->GetOutputString(fCurrentDate, nFormat, aFormatted, &pColor, false);
419 m_aCurrentDate = aFormatted + "T00:00:00Z";
420}
421
423{
424 if (m_aCurrentDate.isEmpty())
425 {
426 return 0;
427 }
428
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)
433 {
434 sal_Int32 nCheckPos = 0;
436 OUString sFormat = CURRENT_DATE_FORMAT;
437 pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
438 }
439
440 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
441 {
442 return 0;
443 }
444
445 double dCurrentDate = 0;
446 OUString aCurrentDate = m_aCurrentDate.replaceAll("T00:00:00Z", "");
447 (void)pNumberFormatter->IsNumberFormat(aCurrentDate, nFormat, dCurrentDate);
448 return dCurrentDate;
449}
450
452{
453 if (GetCheckbox())
454 {
455 return cCh == ' ';
456 }
457
458 if (GetPicture())
459 {
460 return cCh == '\r';
461 }
462
463 return false;
464}
465
467{
468 switch (GetType())
469 {
473 {
474 // Alt-down opens the popup.
475 return rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_DOWN;
476 }
477 default:
478 break;
479 }
480
481 return false;
482}
483
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.
490std::optional<bool> SwContentControl::GetLock(bool bControl) const
491{
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;
503
504 assert(oLock && "invalid or unknown lock state");
505 return oLock;
506}
507
508void SwContentControl::SetLock(bool bLockContent, bool bLockControl)
509{
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";
518}
519
521{
522 if (m_bCheckbox)
523 {
525 }
526
527 if (m_bComboBox)
528 {
530 }
531
532 if (m_bDropDown)
533 {
535 }
536
537 if (m_bPicture)
538 {
540 }
541
542 if (m_bDate)
543 {
545 }
546
547 if (m_bPlainText)
548 {
550 }
551
553}
554
556{
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()));
607
608 if (!m_aListItems.empty())
609 {
610 for (const auto& rListItem : m_aListItems)
611 {
612 rListItem.dumpAsXml(pWriter);
613 }
614 }
615
616 (void)xmlTextWriterEndElement(pWriter);
617}
618
620{
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()));
627
628 (void)xmlTextWriterEndElement(pWriter);
629}
630
632{
633 if (!m_aDisplayText.isEmpty())
634 {
635 return m_aDisplayText;
636 }
637
638 return m_aValue;
639}
640
642{
643 return m_aDisplayText == rOther.m_aDisplayText && m_aValue == rOther.m_aValue;
644}
645
646void SwContentControlListItem::ItemsToAny(const std::vector<SwContentControlListItem>& rItems,
647 uno::Any& rVal)
648{
649 uno::Sequence<uno::Sequence<beans::PropertyValue>> aRet(rItems.size());
650
651 uno::Sequence<beans::PropertyValue>* pRet = aRet.getArray();
652 for (size_t i = 0; i < rItems.size(); ++i)
653 {
654 const SwContentControlListItem& rItem = rItems[i];
655 uno::Sequence<beans::PropertyValue> aItem = {
656 comphelper::makePropertyValue("DisplayText", rItem.m_aDisplayText),
658 };
659 pRet[i] = aItem;
660 }
661
662 rVal <<= aRet;
663}
664
665std::vector<SwContentControlListItem>
667{
668 std::vector<SwContentControlListItem> aRet;
669
670 uno::Sequence<uno::Sequence<beans::PropertyValue>> aSequence;
671 rVal >>= aSequence;
672 for (const auto& rItem : aSequence)
673 {
676 auto it = aMap.find("DisplayText");
677 if (it != aMap.end())
678 {
679 it->second >>= aItem.m_aDisplayText;
680 }
681 it = aMap.find("Value");
682 if (it != aMap.end())
683 {
684 it->second >>= aItem.m_aValue;
685 }
686 aRet.push_back(aItem);
687 }
688
689 return aRet;
690}
691
693 SwTextNode* pTargetTextNode,
695 sal_Int32 nStart,
696 sal_Int32 nEnd, bool bIsCopy)
697{
698 if (bIsCopy)
699 {
700 // rAttr is already cloned, now call DoCopy to copy the SwContentControl
701 if (!pTargetTextNode)
702 {
703 SAL_WARN("sw.core",
704 "SwTextContentControl ctor: cannot copy content control without target node");
705 }
706 rAttr.DoCopy(*pTargetTextNode);
707 }
709 auto pTextContentControl(new SwTextContentControl(pManager, rAttr, nStart, nEnd));
710 return pTextContentControl;
711}
712
714 SwFormatContentControl& rAttr, sal_Int32 nStart,
715 sal_Int32 nEnd)
716 : SwTextAttr(rAttr, nStart)
717 , SwTextAttrNesting(rAttr, nStart, nEnd)
718 , m_pManager(pManager)
719{
720 rAttr.SetTextAttr(this);
721 SetHasDummyChar(true);
722 m_pManager->Insert(this);
723}
724
726{
727 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
728 if (rFormatContentControl.GetTextAttr() == this)
729 {
730 rFormatContentControl.SetTextAttr(nullptr);
731 }
732}
733
735{
736 auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
737 if (rFormatContentControl.GetTextAttr() == this)
738 {
739 rFormatContentControl.NotifyChangeTextNode(pNode);
740
741 if (pNode)
742 {
744 }
745 else
746 {
747 if (m_pManager)
748 {
749 m_pManager->Erase(this);
750 }
751 m_pManager = nullptr;
752 }
753 }
754}
755
756void SwTextContentControl::Delete(bool bSaveContents)
757{
758 if (!GetTextNode())
759 return;
760
761 SwPaM aPaM(*GetTextNode(), GetStart(), *GetTextNode(), *End());
762 if (bSaveContents)
763 GetTextNode()->GetDoc().ResetAttrs(aPaM, /*bTextAttr=*/true, { RES_TXTATR_CONTENTCONTROL });
764 else
766}
767
769{
770 auto& rFormatContentControl = static_cast<const SwFormatContentControl&>(GetAttr());
771 return rFormatContentControl.GetTextNode();
772}
773
775{
776 if (!GetTextNode())
777 return OUString();
778
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;
783
784 SwPaM aPaM(*GetTextNode(), nStart, *GetTextNode(), nEnd);
785 return aPaM.GetText();
786}
787
789{
790 SwDocShell* pDocShell = GetTextNode() ? GetTextNode()->GetDoc().GetDocShell() : nullptr;
791 if (!pDocShell || !pDocShell->GetWrtShell())
792 return;
793
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();
797
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);
802
804}
805
807{
808 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
809 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
810 SwTextAttr::dumpAsXml(pWriter);
811
812 (void)xmlTextWriterEndElement(pWriter);
813}
814
816
818{
819 m_aContentControls.push_back(pTextContentControl);
820}
821
823{
824 m_aContentControls.erase(
825 std::remove(m_aContentControls.begin(), m_aContentControls.end(), pTextContentControl),
826 m_aContentControls.end());
827}
828
830{
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)
838 {
839 return pLhs->GetStart() < pRhs->GetStart();
840 }
841
842 return nIdxLHS < nIdxRHS;
843 });
844
845 return m_aContentControls[nIndex];
846}
847
849{
850 assert(nIndex < m_aContentControls.size());
852}
853
855{
856 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlManager"));
857 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
858 for (const auto& pContentControl : m_aContentControls)
859 {
860 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
861 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pContentControl);
862 (void)xmlTextWriterEndElement(pWriter);
863 }
864
865 (void)xmlTextWriterEndElement(pWriter);
866}
867
868/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual bool DeleteAndJoin(SwPaM &, SwDeleteFlags flags=SwDeleteFlags::Default)=0
complete delete of a given PaM
SfxHintId GetId() const
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const
sal_uInt16 Which() const
virtual bool operator==(const SfxPoolItem &) const=0
bool PutEntry(OUString &rString, sal_Int32 &nCheckPos, SvNumFormatType &nType, sal_uInt32 &nKey, LanguageType eLnge=LANGUAGE_DONTKNOW, bool bReplaceBooleanEquivalent=true)
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, const Color **ppColor, bool bUseStarFormat=false)
sal_uInt32 GetEntryKey(std::u16string_view sStr, LanguageType eLnge=LANGUAGE_DONTKNOW)
bool IsNumberFormat(const OUString &sString, sal_uInt32 &F_Index, double &fOutNumber, SvNumInputOptions eInputOptions=SvNumInputOptions::NONE)
Represents one list item in a content control dropdown list.
void dumpAsXml(xmlTextWriterPtr pWriter) const
bool operator==(const SwContentControlListItem &rOther) const
OUString m_aValue
This must not be empty.
const OUString & ToString() const
static std::vector< SwContentControlListItem > ItemsFromAny(const css::uno::Any &rVal)
static void ItemsToAny(const std::vector< SwContentControlListItem > &rItems, css::uno::Any &rVal)
OUString m_aDisplayText
This may be empty, ToString() falls back to m_aValue.
Knows all the text content controls in the document.
std::vector< SwTextContentControl * > m_aContentControls
Non-owning reference to text content controls.
SwTextContentControl * Get(size_t nIndex)
void Insert(SwTextContentControl *pTextContentControl)
void Erase(SwTextContentControl *pTextContentControl)
void dumpAsXml(xmlTextWriterPtr pWriter) const
SwTextContentControl * UnsortedGet(size_t nIndex)
OUString m_aCheckedState
If m_bCheckbox is true, the value of a checked checkbox.
virtual ~SwContentControl() override
void SetShowingPlaceHolder(bool bShowingPlaceHolder)
OUString m_aDataBindingXpath
The data bindings's XPath: just remembered.
void NotifyChangeTextNode(SwTextNode *pTextNode)
void SetPicture(bool bPicture)
void SetPlaceholderDocPart(const OUString &rPlaceholderDocPart)
void SetXContentControl(const rtl::Reference< SwXContentControl > &xContentControl)
void SetDataBindingXpath(const OUString &rDataBindingXpath)
bool m_bDropDown
Same as combo box, but free-form input is not accepted.
const OUString & GetLock() const
SwFormatContentControl * m_pFormat
OUString m_aAppearance
The appearance: just remembered.
unotools::WeakReference< SwXContentControl > m_wXContentControl
OUString m_aDateLanguage
If m_bDate is true, the date's BCP 47 language tag.
void SetCurrentDate(const OUString &rCurrentDate)
SwContentControl(SwFormatContentControl *pFormat)
OUString GetDateString() const
Formats m_oSelectedDate, taking m_aDateFormat and m_aDateLanguage into account.
void SetDateFormat(const OUString &rDateFormat)
void SetDataBindingPrefixMappings(const OUString &rDataBindingPrefixMappings)
bool ShouldOpenPopup(const vcl::KeyCode &rKeyCode)
Given rKeyCode as a keyboard event, should a popup be opened for this content control?
void SetAlias(const OUString &rAlias)
bool m_bShowingPlaceHolder
Current content is placeholder text.
void SetCheckedState(const OUString &rCheckedState)
sal_uInt32 m_nTabIndex
The tabIndex: just remembered.
void SetListItems(const std::vector< SwContentControlListItem > &rListItems)
std::optional< size_t > m_oSelectedListItem
Stores a list item index, in case the doc model is not yet updated.
sal_Int32 m_nId
The id: just remembered.
SwTextContentControl * GetTextAttr() const
void SetPlainText(bool bPlainText)
void dumpAsXml(xmlTextWriterPtr pWriter) const
bool AddListItem(size_t nZIndex, const OUString &rDisplayText, const OUString &rValue)
virtual void SwClientNotify(const SwModify &, const SfxHint &) override
double GetCurrentDateValue() const
Parses m_aCurrentDate and returns it.
OUString m_aDataBindingPrefixMappings
The data bindings's prefix mappings: just remembered.
const std::vector< SwContentControlListItem > & GetListItems() const
void SetTag(const OUString &rTag)
void SetAppearance(const OUString &rAppearance)
const std::optional< size_t > & GetSelectedListItem() const
void SetLock(bool bLockContent, bool bLockControl)
bool m_bComboBox
Same as drop-down, but free-form input is also accepted.
void SetDateLanguage(const OUString &rDateLanguage)
OUString m_aLock
The control and content locks: mostly just remembered.
SwContentControlType GetType() const
void SetComboBox(bool bComboBox)
void SetChecked(bool bChecked)
void SetColor(const OUString &rColor)
OUString m_aDateFormat
If m_bDate is true, the date format in a syntax accepted by SvNumberFormatter::PutEntry().
void SetCurrentDateValue(double fCurrentDate)
Formats fCurrentDate and sets it.
bool m_bChecked
If m_bCheckbox is true, is the checkbox checked?
OUString m_aTag
The tag: just remembered.
std::optional< double > m_oSelectedDate
Stores a date timestamp, in case the doc model is not yet updated.
void SetUncheckedState(const OUString &rUncheckedState)
void SetDataBindingStoreItemID(const OUString &rDataBindingStoreItemID)
void SetDropDown(bool bDropDown)
bool m_bCheckbox
Display the content control as a checkbox.
OUString m_aAlias
The alias: just remembered.
SwTextNode * m_pTextNode
Can be nullptr if not in a document for undo purposes.
bool m_bPlainText
Plain text, i.e. not rich text.
std::vector< SwContentControlListItem > m_aListItems
OUString m_aUncheckedState
If m_bCheckbox is true, the value of an unchecked checkbox.
OUString m_aCurrentDate
Date in YYYY-MM-DDT00:00:00Z format.
void SetId(sal_Int32 nId)
void SetCheckbox(bool bCheckbox)
void SetDate(bool bDate)
OUString m_aPlaceholderDocPart
The placeholder's doc part: just remembered.
OUString m_aColor
The color: just remembered.
void SetSelectedListItem(std::optional< size_t > oSelectedListItem)
void DeleteListItem(size_t nZIndex)
OUString m_aDataBindingStoreItemID
The data bindings's store item ID: just remembered.
bool GetShowingPlaceHolder() const
bool IsInteractingCharacter(sal_Unicode cCh)
Should this character (during key input) interact with the content control?
void SetTabIndex(sal_uInt32 nTabIndex)
void Push()
store a copy of the current cursor on the cursor stack
Definition: crsrsh.cxx:2550
SwWrtShell * GetWrtShell()
Access to the SwWrtShell belonging to SwView.
Definition: docsh.hxx:225
Definition: doc.hxx:197
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:329
void ResetAttrs(const SwPaM &rRg, bool bTextAttr=true, const o3tl::sorted_vector< sal_uInt16 > &rAttrs=o3tl::sorted_vector< sal_uInt16 >(), const bool bSendDataChangedEvents=true, SwRootFrame const *pLayout=nullptr)
Reset attributes.
Definition: docfmt.cxx:249
SvNumberFormatter * GetNumberFormatter(bool bCreate=true)
Definition: doc.hxx:1429
SwDocShell * GetDocShell()
Definition: doc.hxx:1370
::SwContentControlManager & GetContentControlManager()
Definition: doc.cxx:142
SfxPoolItem subclass that wraps an SwContentControl.
void NotifyChangeTextNode(SwTextNode *pTextNode)
Notify clients registered at m_pContentControl that this content control is being (re-)moved.
std::shared_ptr< SwContentControl > m_pContentControl
const std::shared_ptr< SwContentControl > & GetContentControl() const
SwFormatContentControl(sal_uInt16 nWhich)
SwTextContentControl * m_pTextAttr
SwTextContentControl * GetTextAttr()
bool operator==(const SfxPoolItem &) const override
SfxPoolItem.
void dumpAsXml(xmlTextWriterPtr pWriter) const override
SwTextNode * GetTextNode() const
void DoCopy(SwTextNode &rTargetTextNode)
This method must be called when the hint is actually copied.
static SwFormatContentControl * CreatePoolDefault(sal_uInt16 nWhich)
void SetTextAttr(SwTextContentControl *pTextAttr)
SwFormatContentControl * Clone(SfxItemPool *pPool=nullptr) const override
SwDoc & GetDoc()
Definition: node.hxx:233
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:188
OUString GetText() const
Definition: pam.cxx:1305
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:167
const sal_Int32 * End() const
Definition: txatbase.hxx:156
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const
Definition: txatbase.cxx:89
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
void SetHasDummyChar(const bool bFlag)
Definition: txatbase.hxx:78
SwTextAttr subclass that tracks the location of the wrapped SwFormatContentControl.
SwTextContentControl(SwContentControlManager *pManager, SwFormatContentControl &rAttr, sal_Int32 nStart, sal_Int32 nEnd)
static SwTextContentControl * CreateTextContentControl(SwDoc &rDoc, SwTextNode *pTargetTextNode, SwFormatContentControl &rAttr, sal_Int32 nStart, sal_Int32 nEnd, bool bIsCopy)
void Delete(bool bSaveContents)
void ChgTextNode(SwTextNode *pNode)
OUString ToString() const
Get the current (potentially invalid) string from the doc.
SwTextNode * GetTextNode() const
SwContentControlManager * m_pManager
void dumpAsXml(xmlTextWriterPtr pWriter) const override
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
bool GotoContentControl(const SwFormatContentControl &rContentControl, bool bOnlyRefresh=false)
Definition: wrtsh3.cxx:96
bool Pop(SwCursorShell::PopMode, ::std::optional< SwCallLink > &roLink)
Definition: wrtsh1.cxx:2047
sal_uInt16 GetCode() const
bool IsMod2() const
struct _xmlTextWriter * xmlTextWriterPtr
float u
SwContentControlType
constexpr TypedWhichId< SwPtrMsgPoolItem > RES_REMOVE_UNO_OBJECT(181)
constexpr TypedWhichId< SwFormatContentControl > RES_TXTATR_CONTENTCONTROL(56)
sal_Int32 nIndex
constexpr sal_uInt16 KEY_DOWN
#define LANGUAGE_ENGLISH_US
#define SAL_WARN(area, stream)
size
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
Dialog to specify the properties of date form field.
HashMap_OWString_Interface aMap
SwNodeOffset min(const SwNodeOffset &a, const SwNodeOffset &b)
Definition: nodeoffset.hxx:35
QPRO_FUNC_TYPE nType
ToolBarManager * m_pManager
sal_uInt16 sal_Unicode
SvNumFormatType
constexpr sal_uInt32 NUMBERFORMAT_ENTRY_NOT_FOUND