Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / crsr / bookmark.cxx
blob7e792f805b8ee80ed14eca7cfae895512a1283b9
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 <memory>
21 #include <bookmark.hxx>
22 #include <IDocumentUndoRedo.hxx>
23 #include <IDocumentLinksAdministration.hxx>
24 #include <IDocumentState.hxx>
25 #include <doc.hxx>
26 #include <ndtxt.hxx>
27 #include <pam.hxx>
28 #include <swserv.hxx>
29 #include <sfx2/linkmgr.hxx>
30 #include <sfx2/viewsh.hxx>
31 #include <UndoBookmark.hxx>
32 #include <unobookmark.hxx>
33 #include <utility>
34 #include <xmloff/odffields.hxx>
35 #include <libxml/xmlwriter.h>
36 #include <comphelper/random.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <comphelper/anytostring.hxx>
39 #include <sal/log.hxx>
40 #include <svl/numformat.hxx>
41 #include <svl/zforlist.hxx>
42 #include <edtwin.hxx>
43 #include <DateFormFieldButton.hxx>
44 #include <DropDownFormFieldButton.hxx>
45 #include <DocumentContentOperationsManager.hxx>
46 #include <comphelper/lok.hxx>
47 #include <txtfrm.hxx>
48 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
49 #include <rtl/strbuf.hxx>
50 #include <strings.hrc>
51 #include <tools/json_writer.hxx>
53 using namespace ::sw::mark;
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::uno;
57 namespace sw::mark
60 SwPosition FindFieldSep(IFieldmark const& rMark)
62 SwPosition const& rStartPos(rMark.GetMarkStart());
63 SwPosition const& rEndPos(rMark.GetMarkEnd());
64 SwNodes const& rNodes(rStartPos.GetNodes());
65 SwNodeOffset const nStartNode(rStartPos.GetNodeIndex());
66 SwNodeOffset const nEndNode(rEndPos.GetNodeIndex());
67 int nFields(0);
68 std::optional<SwPosition> ret;
69 for (SwNodeOffset n = nEndNode; nStartNode <= n; --n)
71 SwNode *const pNode(rNodes[n]);
72 if (pNode->IsTextNode())
74 SwTextNode & rTextNode(*pNode->GetTextNode());
75 sal_Int32 const nStart(n == nStartNode
76 ? rStartPos.GetContentIndex() + 1
77 : 0);
78 sal_Int32 const nEnd(n == nEndNode
79 // subtract 1 to ignore the end char
80 ? rEndPos.GetContentIndex() - 1
81 : rTextNode.Len());
82 for (sal_Int32 i = nEnd; nStart < i; --i)
84 const sal_Unicode c(rTextNode.GetText()[i - 1]);
85 switch (c)
87 case CH_TXT_ATR_FIELDSTART:
88 --nFields;
89 assert(0 <= nFields);
90 break;
91 case CH_TXT_ATR_FIELDEND:
92 ++nFields;
93 // fields in field result could happen by manual
94 // editing, although the field update deletes them
95 break;
96 case CH_TXT_ATR_FIELDSEP:
97 if (nFields == 0)
99 assert(!ret); // one per field
100 ret.emplace(rTextNode, i - 1);
101 #ifndef DBG_UTIL
102 return *ret;
103 #endif
105 break;
109 else if (pNode->IsEndNode() && !pNode->StartOfSectionNode()->IsSectionNode())
111 assert(nStartNode <= pNode->StartOfSectionIndex());
112 // fieldmark cannot overlap node section, unless it's a section
113 n = pNode->StartOfSectionIndex();
115 else
117 assert(pNode->IsNoTextNode() || pNode->IsSectionNode()
118 || (pNode->IsEndNode() && pNode->StartOfSectionNode()->IsSectionNode()));
121 assert(ret); // must have found it
122 return *ret;
124 } // namespace sw::mark
126 namespace
128 void lcl_FixPosition(SwPosition& rPos)
130 // make sure the position has 1) the proper node, and 2) a proper index
131 SwTextNode* pTextNode = rPos.GetNode().GetTextNode();
132 if(pTextNode == nullptr && rPos.GetContentIndex() > 0)
134 SAL_INFO(
135 "sw.core",
136 "illegal position: " << rPos.GetContentIndex()
137 << " without proper TextNode");
138 rPos.nContent.Assign(nullptr, 0);
140 else if(pTextNode != nullptr && rPos.GetContentIndex() > pTextNode->Len())
142 SAL_INFO(
143 "sw.core",
144 "illegal position: " << rPos.GetContentIndex()
145 << " is beyond " << pTextNode->Len());
146 rPos.nContent.Assign(pTextNode, pTextNode->Len());
150 void lcl_AssertFieldMarksSet(const Fieldmark& rField,
151 const sal_Unicode aStartMark,
152 const sal_Unicode aEndMark)
154 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
156 SwPosition const& rStart(rField.GetMarkStart());
157 assert(rStart.GetNode().GetTextNode()->GetText()[rStart.GetContentIndex()] == aStartMark); (void) rStart; (void) aStartMark;
158 SwPosition const sepPos(sw::mark::FindFieldSep(rField));
159 assert(sepPos.GetNode().GetTextNode()->GetText()[sepPos.GetContentIndex()] == CH_TXT_ATR_FIELDSEP); (void) sepPos;
161 else
162 { // must be m_pPos1 < m_pPos2 because of asymmetric SplitNode update
163 assert(rField.GetMarkPos().GetContentIndex() + 1 == rField.GetOtherMarkPos().GetContentIndex());
165 SwPosition const& rEnd(rField.GetMarkEnd());
166 assert(rEnd.GetNode().GetTextNode()->GetText()[rEnd.GetContentIndex() - 1] == aEndMark); (void) rEnd;
169 void lcl_SetFieldMarks(Fieldmark& rField,
170 SwDoc& io_rDoc,
171 const sal_Unicode aStartMark,
172 const sal_Unicode aEndMark,
173 SwPosition const*const pSepPos)
175 io_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr);
176 OUString startChar(aStartMark);
177 if (aEndMark != CH_TXT_ATR_FORMELEMENT
178 && rField.GetMarkStart() == rField.GetMarkEnd())
180 // do only 1 InsertString call - to expand existing bookmarks at the
181 // position over the whole field instead of just aStartMark
182 startChar += OUStringChar(CH_TXT_ATR_FIELDSEP) + OUStringChar(aEndMark);
185 SwPosition start = rField.GetMarkStart();
186 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
188 SwPaM aStartPaM(start);
189 io_rDoc.getIDocumentContentOperations().InsertString(aStartPaM, startChar);
190 start.AdjustContent( -startChar.getLength() ); // restore, it was moved by InsertString
191 // do not manipulate via reference directly but call SetMarkStartPos
192 // which works even if start and end pos were the same
193 rField.SetMarkStartPos( start );
194 SwPosition& rEnd = rField.GetMarkEnd(); // note: retrieve after
195 // setting start, because if start==end it can go stale, see SetMarkPos()
196 assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd));
197 if (startChar.getLength() == 1)
199 *aStartPaM.GetPoint() = pSepPos ? *pSepPos : rEnd;
200 io_rDoc.getIDocumentContentOperations().InsertString(aStartPaM, OUString(CH_TXT_ATR_FIELDSEP));
201 if (!pSepPos || rEnd < *pSepPos)
202 { // rEnd is not moved automatically if it's same as insert pos
203 rEnd.AdjustContent(1);
206 assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd));
208 else
210 assert(pSepPos == nullptr);
213 SwPosition& rEnd = rField.GetMarkEnd();
214 if (aEndMark && startChar.getLength() == 1)
216 SwPaM aEndPaM(rEnd);
217 io_rDoc.getIDocumentContentOperations().InsertString(aEndPaM, OUString(aEndMark));
218 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
220 rEnd.AdjustContent(1); // InsertString didn't move non-empty mark
222 else
223 { // InsertString moved the mark's end, not its start
224 assert(rField.GetMarkPos().GetContentIndex() + 1 == rField.GetOtherMarkPos().GetContentIndex());
227 lcl_AssertFieldMarksSet(rField, aStartMark, aEndMark);
229 io_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr);
232 void lcl_RemoveFieldMarks(const Fieldmark& rField,
233 SwDoc& io_rDoc,
234 const sal_Unicode aStartMark,
235 const sal_Unicode aEndMark)
237 io_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr);
239 const SwPosition& rStart = rField.GetMarkStart();
240 SwTextNode const*const pStartTextNode = rStart.GetNode().GetTextNode();
241 assert(pStartTextNode);
242 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
244 (void) pStartTextNode;
245 // check this before start / end because of the +1 / -1 ...
246 SwPosition const sepPos(sw::mark::FindFieldSep(rField));
247 io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(rStart, aStartMark);
248 io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(sepPos, CH_TXT_ATR_FIELDSEP);
251 const SwPosition& rEnd = rField.GetMarkEnd();
252 SwTextNode *const pEndTextNode = rEnd.GetNode().GetTextNode();
253 assert(pEndTextNode);
254 const sal_Int32 nEndPos = (rEnd == rStart)
255 ? rEnd.GetContentIndex()
256 : rEnd.GetContentIndex() - 1;
257 assert(pEndTextNode->GetText()[nEndPos] == aEndMark);
258 SwPosition const aEnd(*pEndTextNode, nEndPos);
259 io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(aEnd, aEndMark);
261 io_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr);
264 auto InvalidatePosition(SwPosition const& rPos) -> void
266 SwUpdateAttr const aHint(rPos.GetContentIndex(), rPos.GetContentIndex(), 0);
267 rPos.GetNode().GetTextNode()->CallSwClientNotify(sw::LegacyModifyHint(&aHint, &aHint));
271 namespace sw::mark
273 MarkBase::MarkBase(const SwPaM& aPaM,
274 OUString aName)
275 : m_oPos1(*aPaM.GetPoint())
276 , m_aName(std::move(aName))
278 m_oPos1->SetMark(this);
279 lcl_FixPosition(*m_oPos1);
280 if (aPaM.HasMark() && (*aPaM.GetMark() != *aPaM.GetPoint()))
282 MarkBase::SetOtherMarkPos(*(aPaM.GetMark()));
283 lcl_FixPosition(*m_oPos2);
287 void MarkBase::SetXBookmark(rtl::Reference<SwXBookmark> const& xBkmk)
288 { m_wXBookmark = xBkmk.get(); }
290 // For fieldmarks, the CH_TXT_ATR_FIELDSTART and CH_TXT_ATR_FIELDEND
291 // themselves are part of the covered range. This is guaranteed by
292 // TextFieldmark::InitDoc/lcl_AssureFieldMarksSet.
293 bool MarkBase::IsCoveringPosition(const SwPosition& rPos) const
295 return GetMarkStart() <= rPos && rPos < GetMarkEnd();
298 void MarkBase::SetMarkPos(const SwPosition& rNewPos)
300 m_oPos1.emplace(rNewPos);
301 m_oPos1->SetMark(this);
304 void MarkBase::SetOtherMarkPos(const SwPosition& rNewPos)
306 m_oPos2.emplace(rNewPos);
307 m_oPos2->SetMark(this);
310 OUString MarkBase::ToString( ) const
312 return "Mark: ( Name, [ Node1, Index1 ] ): ( " + m_aName + ", [ "
313 + OUString::number( sal_Int32(GetMarkPos().GetNodeIndex()) ) + ", "
314 + OUString::number( GetMarkPos().GetContentIndex( ) ) + " ] )";
317 void MarkBase::dumpAsXml(xmlTextWriterPtr pWriter) const
319 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkBase"));
320 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(m_aName.toUtf8().getStr()));
321 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("markPos"));
322 GetMarkPos().dumpAsXml(pWriter);
323 (void)xmlTextWriterEndElement(pWriter);
324 if (IsExpanded())
326 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("otherMarkPos"));
327 GetOtherMarkPos().dumpAsXml(pWriter);
328 (void)xmlTextWriterEndElement(pWriter);
330 (void)xmlTextWriterEndElement(pWriter);
333 MarkBase::~MarkBase()
336 OUString MarkBase::GenerateNewName(std::u16string_view rPrefix)
338 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
340 if (bHack)
342 static sal_Int64 nIdCounter = SAL_CONST_INT64(6000000000);
343 return rPrefix + OUString::number(nIdCounter++);
345 else
347 static OUString sUniquePostfix;
348 static sal_Int32 nCount = SAL_MAX_INT32;
349 if(nCount == SAL_MAX_INT32)
351 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
352 std::numeric_limits<unsigned int>::max()));
353 sUniquePostfix = "_" + OUString::number(n);
354 nCount = 0;
356 // putting the counter in front of the random parts will speed up string comparisons
357 return rPrefix + OUString::number(nCount++) + sUniquePostfix;
361 void MarkBase::SwClientNotify(const SwModify&, const SfxHint& rHint)
363 CallSwClientNotify(rHint);
364 if (rHint.GetId() != SfxHintId::SwLegacyModify)
365 return;
366 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
367 if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
368 { // invalidate cached uno object
369 SetXBookmark(nullptr);
373 auto MarkBase::InvalidateFrames() -> void
377 NavigatorReminder::NavigatorReminder(const SwPaM& rPaM)
378 : MarkBase(rPaM, MarkBase::GenerateNewName(u"__NavigatorReminder__"))
381 UnoMark::UnoMark(const SwPaM& aPaM)
382 : MarkBase(aPaM, MarkBase::GenerateNewName(u"__UnoMark__"))
385 DdeBookmark::DdeBookmark(const SwPaM& aPaM)
386 : MarkBase(aPaM, MarkBase::GenerateNewName(u"__DdeLink__"))
389 void DdeBookmark::SetRefObject(SwServerObject* pObj)
391 m_aRefObj = pObj;
394 void DdeBookmark::DeregisterFromDoc(SwDoc& rDoc)
396 if(m_aRefObj.is())
397 rDoc.getIDocumentLinksAdministration().GetLinkManager().RemoveServer(m_aRefObj.get());
400 DdeBookmark::~DdeBookmark()
402 if( m_aRefObj.is() )
404 if(m_aRefObj->HasDataLinks())
406 ::sfx2::SvLinkSource* p = m_aRefObj.get();
407 p->SendDataChanged();
409 m_aRefObj->SetNoServer();
413 Bookmark::Bookmark(const SwPaM& aPaM,
414 const vcl::KeyCode& rCode,
415 const OUString& rName)
416 : DdeBookmark(aPaM)
417 , m_aCode(rCode)
418 , m_bHidden(false)
420 m_aName = rName;
423 void Bookmark::sendLOKDeleteCallback()
425 if (!comphelper::LibreOfficeKit::isActive() || GetMarkPos().GetDoc().IsClipBoard())
426 return;
428 SfxViewShell* pViewShell = SfxViewShell::Current();
429 if (!pViewShell)
430 return;
432 OUString fieldCommand = GetName();
433 tools::JsonWriter aJson;
434 aJson.put("commandName", ".uno:DeleteBookmark");
435 aJson.put("success", true);
437 auto result = aJson.startNode("result");
438 aJson.put("DeleteBookmark", fieldCommand);
441 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
444 void Bookmark::InitDoc(SwDoc& io_rDoc,
445 sw::mark::InsertMode const, SwPosition const*const)
447 if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
449 io_rDoc.GetIDocumentUndoRedo().AppendUndo(
450 std::make_unique<SwUndoInsBookmark>(*this));
452 io_rDoc.getIDocumentState().SetModified();
453 InvalidateFrames();
456 void Bookmark::DeregisterFromDoc(SwDoc& io_rDoc)
458 DdeBookmark::DeregisterFromDoc(io_rDoc);
460 if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
462 io_rDoc.GetIDocumentUndoRedo().AppendUndo(
463 std::make_unique<SwUndoDeleteBookmark>(*this));
465 io_rDoc.getIDocumentState().SetModified();
466 InvalidateFrames();
469 // invalidate text frames in case it's hidden or Formatting Marks enabled
470 auto Bookmark::InvalidateFrames() -> void
472 InvalidatePosition(GetMarkPos());
473 if (IsExpanded())
475 InvalidatePosition(GetOtherMarkPos());
479 void Bookmark::Hide(bool const isHide)
481 if (isHide != m_bHidden)
483 m_bHidden = isHide;
484 InvalidateFrames();
488 void Bookmark::SetHideCondition(OUString const& rHideCondition)
490 if (m_sHideCondition != rHideCondition)
492 m_sHideCondition = rHideCondition;
493 // don't eval condition here yet - probably only needed for
494 // UI editing condition and that doesn't exist yet
498 ::sfx2::IXmlIdRegistry& Bookmark::GetRegistry()
500 SwDoc& rDoc( GetMarkPos().GetDoc() );
501 return rDoc.GetXmlIdRegistry();
504 bool Bookmark::IsInClipboard() const
506 SwDoc& rDoc( GetMarkPos().GetDoc() );
507 return rDoc.IsClipBoard();
510 bool Bookmark::IsInUndo() const
512 return false;
515 bool Bookmark::IsInContent() const
517 SwDoc& rDoc( GetMarkPos().GetDoc() );
518 return !rDoc.IsInHeaderFooter( GetMarkPos().GetNode() );
521 uno::Reference< rdf::XMetadatable > Bookmark::MakeUnoObject()
523 SwDoc& rDoc( GetMarkPos().GetDoc() );
524 const uno::Reference< rdf::XMetadatable> xMeta(
525 SwXBookmark::CreateXBookmark(rDoc, this) );
526 return xMeta;
529 Fieldmark::Fieldmark(const SwPaM& rPaM)
530 : MarkBase(rPaM, MarkBase::GenerateNewName(u"__Fieldmark__"))
532 if(!IsExpanded())
533 SetOtherMarkPos(GetMarkPos());
536 void Fieldmark::SetMarkStartPos( const SwPosition& rNewStartPos )
538 if ( GetMarkPos( ) <= GetOtherMarkPos( ) )
539 return SetMarkPos( rNewStartPos );
540 else
541 return SetOtherMarkPos( rNewStartPos );
544 OUString Fieldmark::ToString( ) const
546 return "Fieldmark: ( Name, Type, [ Nd1, Id1 ], [ Nd2, Id2 ] ): ( " + m_aName + ", "
547 + m_aFieldname + ", [ " + OUString::number( sal_Int32(GetMarkPos().GetNodeIndex( )) )
548 + ", " + OUString::number( GetMarkPos( ).GetContentIndex( ) ) + " ], ["
549 + OUString::number( sal_Int32(GetOtherMarkPos().GetNodeIndex( )) ) + ", "
550 + OUString::number( GetOtherMarkPos( ).GetContentIndex( ) ) + " ] ) ";
553 void Fieldmark::Invalidate( )
555 // TODO: Does exist a better solution to trigger a format of the
556 // fieldmark portion? If yes, please use it.
557 SwPaM aPaM( GetMarkPos(), GetOtherMarkPos() );
558 aPaM.InvalidatePaM();
561 void Fieldmark::dumpAsXml(xmlTextWriterPtr pWriter) const
563 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Fieldmark"));
564 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldname"), BAD_CAST(m_aFieldname.toUtf8().getStr()));
565 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldHelptext"), BAD_CAST(m_aFieldHelptext.toUtf8().getStr()));
566 MarkBase::dumpAsXml(pWriter);
567 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("parameters"));
568 for (auto& rParam : m_vParams)
570 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("parameter"));
571 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(rParam.first.toUtf8().getStr()));
572 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(comphelper::anyToString(rParam.second).toUtf8().getStr()));
573 (void)xmlTextWriterEndElement(pWriter);
575 (void)xmlTextWriterEndElement(pWriter);
576 (void)xmlTextWriterEndElement(pWriter);
579 TextFieldmark::TextFieldmark(const SwPaM& rPaM, const OUString& rName)
580 : Fieldmark(rPaM)
581 , m_pDocumentContentOperationsManager(nullptr)
583 if ( !rName.isEmpty() )
584 m_aName = rName;
587 TextFieldmark::~TextFieldmark()
589 if (!comphelper::LibreOfficeKit::isActive() || GetMarkPos().GetDoc().IsClipBoard())
590 return;
592 SfxViewShell* pViewShell = SfxViewShell::Current();
593 if (!pViewShell)
594 return;
596 OUString fieldCommand;
597 (*GetParameters())[OUString(ODF_CODE_PARAM)] >>= fieldCommand;
598 tools::JsonWriter aJson;
599 aJson.put("commandName", ".uno:DeleteTextFormField");
600 aJson.put("success", true);
602 auto result = aJson.startNode("result");
603 aJson.put("DeleteTextFormField", fieldCommand);
606 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString());
609 void TextFieldmark::InitDoc(SwDoc& io_rDoc,
610 sw::mark::InsertMode const eMode, SwPosition const*const pSepPos)
612 m_pDocumentContentOperationsManager = &io_rDoc.GetDocumentContentOperationsManager();
613 if (eMode == sw::mark::InsertMode::New)
615 lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos);
617 else
619 lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
623 void TextFieldmark::ReleaseDoc(SwDoc& rDoc)
625 IDocumentUndoRedo & rIDUR(rDoc.GetIDocumentUndoRedo());
626 if (rIDUR.DoesUndo())
628 rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this));
630 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
631 lcl_RemoveFieldMarks(*this, rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
632 // notify layouts to unhide - for the entire fieldmark, as in InitDoc()
633 SwPaM const tmp(GetMarkPos(), GetOtherMarkPos());
634 sw::UpdateFramesForRemoveDeleteRedline(rDoc, tmp);
637 OUString TextFieldmark::GetContent() const
639 const SwTextNode& rTextNode = *GetMarkEnd().GetNode().GetTextNode();
640 SwPosition const sepPos(sw::mark::FindFieldSep(*this));
641 const sal_Int32 nStart(sepPos.GetContentIndex());
642 const sal_Int32 nEnd(GetMarkEnd().GetContentIndex());
644 OUString sContent;
645 const sal_Int32 nLen = rTextNode.GetText().getLength();
646 if (nStart + 1 < nLen && nEnd <= nLen && nEnd > nStart + 2)
647 sContent = rTextNode.GetText().copy(nStart + 1, nEnd - nStart - 2);
649 return sContent;
652 void TextFieldmark::ReplaceContent(const OUString& sNewContent)
654 if (!m_pDocumentContentOperationsManager)
655 return;
657 SwPosition const sepPos(sw::mark::FindFieldSep(*this));
658 const sal_Int32 nStart(sepPos.GetContentIndex());
659 const sal_Int32 nEnd(GetMarkEnd().GetContentIndex());
661 const sal_Int32 nLen = GetMarkEnd().GetNode().GetTextNode()->GetText().getLength();
662 if (nStart + 1 < nLen && nEnd <= nLen && nEnd > nStart + 2)
664 SwPaM aFieldPam(GetMarkStart().GetNode(), nStart + 1,
665 GetMarkStart().GetNode(), nEnd - 1);
666 m_pDocumentContentOperationsManager->ReplaceRange(aFieldPam, sNewContent, false);
668 else
670 SwPaM aFieldStartPam(GetMarkStart().GetNode(), nStart + 1);
671 m_pDocumentContentOperationsManager->InsertString(aFieldStartPam, sNewContent);
673 Invalidate();
676 NonTextFieldmark::NonTextFieldmark(const SwPaM& rPaM)
677 : Fieldmark(rPaM)
680 void NonTextFieldmark::InitDoc(SwDoc& io_rDoc,
681 sw::mark::InsertMode const eMode, SwPosition const*const pSepPos)
683 assert(pSepPos == nullptr);
684 if (eMode == sw::mark::InsertMode::New)
686 lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT, pSepPos);
688 else
690 lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT);
694 void NonTextFieldmark::ReleaseDoc(SwDoc& rDoc)
696 IDocumentUndoRedo & rIDUR(rDoc.GetIDocumentUndoRedo());
697 if (rIDUR.DoesUndo())
699 rIDUR.AppendUndo(std::make_unique<SwUndoDelNoTextFieldmark>(*this));
701 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
702 lcl_RemoveFieldMarks(*this, rDoc,
703 CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT);
707 CheckboxFieldmark::CheckboxFieldmark(const SwPaM& rPaM, const OUString& rName)
708 : NonTextFieldmark(rPaM)
710 if (!rName.isEmpty())
711 m_aName = rName;
714 void CheckboxFieldmark::SetChecked(bool checked)
716 if ( IsChecked() != checked )
718 (*GetParameters())[OUString(ODF_FORMCHECKBOX_RESULT)] <<= checked;
719 // mark document as modified
720 SwDoc& rDoc( GetMarkPos().GetDoc() );
721 rDoc.getIDocumentState().SetModified();
725 bool CheckboxFieldmark::IsChecked() const
727 bool bResult = false;
728 parameter_map_t::const_iterator pResult = GetParameters()->find(OUString(ODF_FORMCHECKBOX_RESULT));
729 if(pResult != GetParameters()->end())
730 pResult->second >>= bResult;
731 return bResult;
734 OUString CheckboxFieldmark::GetContent() const
736 return IsChecked() ? "1" : "0";
739 void CheckboxFieldmark::ReplaceContent(const OUString& sNewContent)
741 SetChecked(sNewContent.toBoolean());
742 Invalidate();
745 FieldmarkWithDropDownButton::FieldmarkWithDropDownButton(const SwPaM& rPaM)
746 : NonTextFieldmark(rPaM)
747 , m_pButton(nullptr)
751 FieldmarkWithDropDownButton::~FieldmarkWithDropDownButton()
753 m_pButton.disposeAndClear();
756 void FieldmarkWithDropDownButton::RemoveButton()
758 if(m_pButton)
759 m_pButton.disposeAndClear();
762 void FieldmarkWithDropDownButton::LaunchPopup()
764 if (!m_pButton)
765 return;
767 m_pButton->Invalidate();
768 m_pButton->LaunchPopup();
771 DropDownFieldmark::DropDownFieldmark(const SwPaM& rPaM, const OUString& rName)
772 : FieldmarkWithDropDownButton(rPaM)
774 if (!rName.isEmpty())
775 m_aName = rName;
778 DropDownFieldmark::~DropDownFieldmark()
782 void DropDownFieldmark::ShowButton(SwEditWin* pEditWin)
784 if(pEditWin)
786 if(!m_pButton)
787 m_pButton = VclPtr<DropDownFormFieldButton>::Create(pEditWin, *this);
788 m_pButton->CalcPosAndSize(m_aPortionPaintArea);
789 m_pButton->Show();
793 void DropDownFieldmark::RemoveButton()
795 FieldmarkWithDropDownButton::RemoveButton();
798 /** GetContent
799 * @param pIndex The zero-based index to retrieve
800 * [in] if pIndex is null or negative, return the listbox's chosen result,
801 * else return the indicated entry (or last entry for invalid choice).
802 * [out] the index of the returned result or -1 if error
804 OUString DropDownFieldmark::GetContent(sal_Int32* pIndex) const
806 sal_Int32 nIndex = pIndex ? *pIndex : -1;
807 auto rParameters = *GetParameters();
808 if (nIndex < 0)
809 rParameters[ODF_FORMDROPDOWN_RESULT] >>= nIndex;
811 uno::Sequence<OUString> aSeq;
812 rParameters[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
813 nIndex = std::min(nIndex, aSeq.getLength() - 1);
815 if (nIndex < 0)
817 if (pIndex)
818 *pIndex = -1;
819 return OUString();
822 if (pIndex)
823 *pIndex = nIndex;
825 return aSeq[nIndex];
828 OUString DropDownFieldmark::GetContent() const
830 return GetContent(nullptr);
833 /** AddContent : INSERTS a new choice
834 * @param rText: The choice to add to the list choices.
836 * @param pIndex [optional]
837 * [in] If pIndex is null or invalid, append to the end of the list.
838 * [out] Modified to point to the position of the choice if it already exists.
840 void DropDownFieldmark::AddContent(const OUString& rText, sal_Int32* pIndex)
842 uno::Sequence<OUString> aSeq;
843 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
844 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
846 // no duplicates: if it already exists, modify the given index to point to it
847 const sal_Int32 nCurrentTextPos = comphelper::findValue(aSeq, rText);
848 if (nCurrentTextPos != -1)
850 if (pIndex)
851 *pIndex = nCurrentTextPos;
852 return;
855 const sal_Int32 nLen = aSeq.getLength();
856 const sal_Int32 nNewPos = pIndex && *pIndex > -1 ? std::min(*pIndex, nLen) : nLen;
858 // need to shift list result index up if adding new entry before it
859 sal_Int32 nResultIndex = -1;
860 (*pParameters)[ODF_FORMDROPDOWN_RESULT] >>= nResultIndex;
861 if (nNewPos <= nResultIndex)
862 (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= nResultIndex + 1;
864 auto aList = comphelper::sequenceToContainer<std::vector<OUString>>(aSeq);
865 if (nNewPos < nLen)
866 aList.insert(aList.begin() + nNewPos, rText);
867 else
869 if (pIndex)
870 *pIndex = nLen;
871 aList.push_back(rText);
874 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= comphelper::containerToSequence(aList);
875 Invalidate();
879 * ReplaceContent : changes the list result index or renames the existing choices
880 * @param pText
881 * [in] If pIndex is null, change the list result index to this provided choice
882 * (but do nothing if pText is an invalid choice)
883 * else rename that entry.
885 * @param pIndex
886 * [in] If pText is null, change the list result index to this provided Index
887 * (or the last position if it is an invalid choice)
888 * else rename this entry (doing nothing for invalid indexes).
889 * [out] If pIndex is invalid, it is modified to use the last position.
891 * This function allows duplicate entries - which is also allowed in MS Word.
893 void DropDownFieldmark::ReplaceContent(const OUString* pText, sal_Int32* pIndex)
895 if (!pIndex && !pText)
896 return;
898 uno::Sequence<OUString> aSeq;
899 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
900 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
901 const sal_Int32 nLen = aSeq.getLength();
903 if (!pText)
905 if (*pIndex < 0 || *pIndex >= nLen)
906 *pIndex = nLen - 1;
908 // select pIndex as the new value for the list box
909 (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= *pIndex;
910 Invalidate();
911 return;
914 if (!pIndex)
916 const sal_Int32 nNewPos = comphelper::findValue(aSeq, *pText);
917 if (nNewPos != -1)
919 (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= nNewPos;
920 Invalidate();
922 return;
925 if (*pIndex > -1 && *pIndex < nLen)
927 auto aList = comphelper::sequenceToContainer<std::vector<OUString>>(aSeq);
928 aList[*pIndex] = *pText;
929 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= comphelper::containerToSequence(aList);
930 Invalidate();
934 void DropDownFieldmark::ReplaceContent(const OUString& rNewContent)
936 ReplaceContent(&rNewContent, nullptr);
940 * Remove everything if the given index is negative, else remove the given index (if valid).
941 * If deleting the currently selected choice, reset the selection to the first choice.
943 void DropDownFieldmark::DelContent(sal_Int32 nDelIndex)
945 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
946 uno::Sequence<OUString> aSeq;
947 if (nDelIndex < 0)
949 pParameters->erase(ODF_FORMDROPDOWN_RESULT);
950 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= aSeq;
951 Invalidate();
952 return;
955 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] >>= aSeq;
956 if (nDelIndex >= aSeq.getLength())
957 return;
959 // If deleting the current choice, select the first entry instead
960 // else need to shift list result index down if deleting an entry before it
961 sal_Int32 nResultIndex = -1;
962 (*pParameters)[ODF_FORMDROPDOWN_RESULT] >>= nResultIndex;
963 if (nDelIndex == nResultIndex)
964 nResultIndex = 0;
965 else if (nDelIndex < nResultIndex)
966 --nResultIndex;
968 comphelper::removeElementAt(aSeq, nDelIndex);
969 if (nResultIndex != -1)
970 (*pParameters)[ODF_FORMDROPDOWN_RESULT] <<= nResultIndex;
971 (*pParameters)[ODF_FORMDROPDOWN_LISTENTRY] <<= aSeq;
972 Invalidate();
975 void DropDownFieldmark::SetPortionPaintArea(const SwRect& rPortionPaintArea)
977 m_aPortionPaintArea = rPortionPaintArea;
978 if(m_pButton)
980 m_pButton->Show();
981 m_pButton->CalcPosAndSize(m_aPortionPaintArea);
985 void DropDownFieldmark::SendLOKShowMessage(const SfxViewShell* pViewShell)
987 if (!comphelper::LibreOfficeKit::isActive())
988 return;
990 if (!pViewShell || pViewShell->isLOKMobilePhone())
991 return;
993 if (m_aPortionPaintArea.IsEmpty())
994 return;
996 OStringBuffer sPayload;
997 sPayload = OString::Concat("{\"action\": \"show\","
998 " \"type\": \"drop-down\", \"textArea\": \"") +
999 m_aPortionPaintArea.SVRect().toString() + "\",";
1000 // Add field params to the message
1001 sPayload.append(" \"params\": { \"items\": [");
1003 // List items
1004 auto pParameters = this->GetParameters();
1005 auto pListEntriesIter = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
1006 css::uno::Sequence<OUString> vListEntries;
1007 if (pListEntriesIter != pParameters->end())
1009 pListEntriesIter->second >>= vListEntries;
1010 for (const OUString& sItem : std::as_const(vListEntries))
1011 sPayload.append("\"" + OUStringToOString(sItem, RTL_TEXTENCODING_UTF8) + "\", ");
1012 sPayload.setLength(sPayload.getLength() - 2);
1014 sPayload.append("], ");
1016 // Selected item
1017 auto pSelectedItemIter = pParameters->find(ODF_FORMDROPDOWN_RESULT);
1018 sal_Int32 nSelection = -1;
1019 if (pSelectedItemIter != pParameters->end())
1021 pSelectedItemIter->second >>= nSelection;
1023 sPayload.append("\"selected\": \"" + OString::number(nSelection) + "\", ");
1025 // Placeholder text
1026 sPayload.append("\"placeholderText\": \"" + OUStringToOString(SwResId(STR_DROP_DOWN_EMPTY_LIST), RTL_TEXTENCODING_UTF8) + "\"}}");
1027 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON, sPayload.toString());
1030 void DropDownFieldmark::SendLOKHideMessage(const SfxViewShell* pViewShell)
1032 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON,
1033 "{\"action\": \"hide\", \"type\": \"drop-down\"}");
1036 DateFieldmark::DateFieldmark(const SwPaM& rPaM)
1037 : FieldmarkWithDropDownButton(rPaM)
1038 , m_pNumberFormatter(nullptr)
1039 , m_pDocumentContentOperationsManager(nullptr)
1043 DateFieldmark::~DateFieldmark()
1047 void DateFieldmark::InitDoc(SwDoc& io_rDoc,
1048 sw::mark::InsertMode eMode, SwPosition const*const pSepPos)
1050 m_pNumberFormatter = io_rDoc.GetNumberFormatter();
1051 m_pDocumentContentOperationsManager = &io_rDoc.GetDocumentContentOperationsManager();
1052 if (eMode == sw::mark::InsertMode::New)
1054 lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos);
1056 else
1058 lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
1062 void DateFieldmark::ReleaseDoc(SwDoc& rDoc)
1064 IDocumentUndoRedo & rIDUR(rDoc.GetIDocumentUndoRedo());
1065 if (rIDUR.DoesUndo())
1067 // TODO does this need a 3rd Undo class?
1068 rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this));
1070 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
1071 lcl_RemoveFieldMarks(*this, rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
1072 // notify layouts to unhide - for the entire fieldmark, as in InitDoc()
1073 SwPaM const tmp(GetMarkPos(), GetOtherMarkPos());
1074 sw::UpdateFramesForRemoveDeleteRedline(rDoc, tmp);
1077 void DateFieldmark::ShowButton(SwEditWin* pEditWin)
1079 if(pEditWin)
1081 if(!m_pButton)
1082 m_pButton = VclPtr<DateFormFieldButton>::Create(pEditWin, *this, m_pNumberFormatter);
1083 SwRect aPaintArea(m_aPaintAreaStart.TopLeft(), m_aPaintAreaEnd.BottomRight());
1084 m_pButton->CalcPosAndSize(aPaintArea);
1085 m_pButton->Show();
1089 void DateFieldmark::SetPortionPaintAreaStart(const SwRect& rPortionPaintArea)
1091 if (rPortionPaintArea.IsEmpty())
1092 return;
1094 m_aPaintAreaStart = rPortionPaintArea;
1095 InvalidateCurrentDateParam();
1098 void DateFieldmark::SetPortionPaintAreaEnd(const SwRect& rPortionPaintArea)
1100 if (rPortionPaintArea.IsEmpty())
1101 return;
1103 if(m_aPaintAreaEnd == rPortionPaintArea &&
1104 m_pButton && m_pButton->IsVisible())
1105 return;
1107 m_aPaintAreaEnd = rPortionPaintArea;
1108 if(m_pButton)
1110 m_pButton->Show();
1111 SwRect aPaintArea(m_aPaintAreaStart.TopLeft(), m_aPaintAreaEnd.BottomRight());
1112 m_pButton->CalcPosAndSize(aPaintArea);
1113 m_pButton->Invalidate();
1115 InvalidateCurrentDateParam();
1118 OUString DateFieldmark::GetContent() const
1120 const SwTextNode* const pTextNode = GetMarkEnd().GetNode().GetTextNode();
1121 SwPosition const sepPos(sw::mark::FindFieldSep(*this));
1122 const sal_Int32 nStart(sepPos.GetContentIndex());
1123 const sal_Int32 nEnd (GetMarkEnd().GetContentIndex());
1125 OUString sContent;
1126 if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() &&
1127 nEnd > nStart + 2)
1128 sContent = pTextNode->GetText().copy(nStart + 1, nEnd - nStart - 2);
1129 return sContent;
1132 void DateFieldmark::ReplaceContent(const OUString& sNewContent)
1134 if(!m_pDocumentContentOperationsManager)
1135 return;
1137 const SwTextNode* const pTextNode = GetMarkEnd().GetNode().GetTextNode();
1138 SwPosition const sepPos(sw::mark::FindFieldSep(*this));
1139 const sal_Int32 nStart(sepPos.GetContentIndex());
1140 const sal_Int32 nEnd (GetMarkEnd().GetContentIndex());
1142 if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() &&
1143 nEnd > nStart + 2)
1145 SwPaM aFieldPam(GetMarkStart().GetNode(), nStart + 1,
1146 GetMarkStart().GetNode(), nEnd - 1);
1147 m_pDocumentContentOperationsManager->ReplaceRange(aFieldPam, sNewContent, false);
1149 else
1151 SwPaM aFieldStartPam(GetMarkStart().GetNode(), nStart + 1);
1152 m_pDocumentContentOperationsManager->InsertString(aFieldStartPam, sNewContent);
1157 std::pair<bool, double> DateFieldmark::GetCurrentDate() const
1159 // Check current date param first
1160 std::pair<bool, double> aResult = ParseCurrentDateParam();
1161 if(aResult.first)
1162 return aResult;
1164 const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
1165 bool bFoundValidDate = false;
1166 double dCurrentDate = 0;
1167 OUString sDateFormat;
1168 auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
1169 if (pResult != pParameters->end())
1171 pResult->second >>= sDateFormat;
1174 OUString sLang;
1175 pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
1176 if (pResult != pParameters->end())
1178 pResult->second >>= sLang;
1181 // Get current content of the field
1182 OUString sContent = GetContent();
1184 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType());
1185 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
1187 sal_Int32 nCheckPos = 0;
1188 SvNumFormatType nType;
1189 m_pNumberFormatter->PutEntry(sDateFormat,
1190 nCheckPos,
1191 nType,
1192 nFormat,
1193 LanguageTag(sLang).getLanguageType());
1196 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
1198 bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sContent, nFormat, dCurrentDate);
1200 return std::pair<bool, double>(bFoundValidDate, dCurrentDate);
1203 void DateFieldmark::SetCurrentDate(double fDate)
1205 // Replace current content with the selected date
1206 ReplaceContent(GetDateInCurrentDateFormat(fDate));
1208 // Also save the current date in a standard format
1209 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
1210 (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= GetDateInStandardDateFormat(fDate);
1213 OUString DateFieldmark::GetDateInStandardDateFormat(double fDate) const
1215 OUString sCurrentDate;
1216 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(ODF_FORMDATE_CURRENTDATE_FORMAT, ODF_FORMDATE_CURRENTDATE_LANGUAGE);
1217 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
1219 sal_Int32 nCheckPos = 0;
1220 SvNumFormatType nType;
1221 OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT;
1222 m_pNumberFormatter->PutEntry(sFormat,
1223 nCheckPos,
1224 nType,
1225 nFormat,
1226 ODF_FORMDATE_CURRENTDATE_LANGUAGE);
1229 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
1231 const Color* pCol = nullptr;
1232 m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentDate, &pCol, false);
1234 return sCurrentDate;
1237 std::pair<bool, double> DateFieldmark::ParseCurrentDateParam() const
1239 bool bFoundValidDate = false;
1240 double dCurrentDate = 0;
1242 const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
1243 auto pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE);
1244 OUString sCurrentDate;
1245 if (pResult != pParameters->end())
1247 pResult->second >>= sCurrentDate;
1249 if(!sCurrentDate.isEmpty())
1251 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(ODF_FORMDATE_CURRENTDATE_FORMAT, ODF_FORMDATE_CURRENTDATE_LANGUAGE);
1252 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
1254 sal_Int32 nCheckPos = 0;
1255 SvNumFormatType nType;
1256 OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT;
1257 m_pNumberFormatter->PutEntry(sFormat,
1258 nCheckPos,
1259 nType,
1260 nFormat,
1261 ODF_FORMDATE_CURRENTDATE_LANGUAGE);
1264 if(nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
1266 bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sCurrentDate, nFormat, dCurrentDate);
1269 return std::pair<bool, double>(bFoundValidDate, dCurrentDate);
1273 OUString DateFieldmark::GetDateInCurrentDateFormat(double fDate) const
1275 // Get current date format and language
1276 OUString sDateFormat;
1277 const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
1278 auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
1279 if (pResult != pParameters->end())
1281 pResult->second >>= sDateFormat;
1284 OUString sLang;
1285 pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
1286 if (pResult != pParameters->end())
1288 pResult->second >>= sLang;
1291 // Fill the content with the specified format
1292 OUString sCurrentContent;
1293 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType());
1294 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
1296 sal_Int32 nCheckPos = 0;
1297 SvNumFormatType nType;
1298 OUString sFormat = sDateFormat;
1299 m_pNumberFormatter->PutEntry(sFormat,
1300 nCheckPos,
1301 nType,
1302 nFormat,
1303 LanguageTag(sLang).getLanguageType());
1306 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
1308 const Color* pCol = nullptr;
1309 m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentContent, &pCol, false);
1311 return sCurrentContent;
1314 void DateFieldmark::InvalidateCurrentDateParam()
1316 std::pair<bool, double> aResult = ParseCurrentDateParam();
1317 if(!aResult.first)
1318 return;
1320 // Current date became invalid
1321 if(GetDateInCurrentDateFormat(aResult.second) != GetContent())
1323 sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
1324 (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= OUString();
1329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */