LibreOffice Module sw (master) 1
core/crsr/bookmark.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
20#include <memory>
21#include <bookmark.hxx>
22#include <IDocumentUndoRedo.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>
38#include <sal/log.hxx>
39#include <svl/numformat.hxx>
40#include <svl/zforlist.hxx>
41#include <edtwin.hxx>
45#include <comphelper/lok.hxx>
46#include <txtfrm.hxx>
47#include <LibreOfficeKit/LibreOfficeKitEnums.h>
48#include <rtl/strbuf.hxx>
49#include <strings.hrc>
50
51using namespace ::sw::mark;
52using namespace ::com::sun::star;
53using namespace ::com::sun::star::uno;
54
55namespace sw::mark
56{
57
59 {
60 SwPosition const& rStartPos(rMark.GetMarkStart());
61 SwPosition const& rEndPos(rMark.GetMarkEnd());
62 SwNodes const& rNodes(rStartPos.GetNodes());
63 SwNodeOffset const nStartNode(rStartPos.GetNodeIndex());
64 SwNodeOffset const nEndNode(rEndPos.GetNodeIndex());
65 int nFields(0);
66 std::optional<SwPosition> ret;
67 for (SwNodeOffset n = nEndNode; nStartNode <= n; --n)
68 {
69 SwNode *const pNode(rNodes[n]);
70 if (pNode->IsTextNode())
71 {
72 SwTextNode & rTextNode(*pNode->GetTextNode());
73 sal_Int32 const nStart(n == nStartNode
74 ? rStartPos.GetContentIndex() + 1
75 : 0);
76 sal_Int32 const nEnd(n == nEndNode
77 // subtract 1 to ignore the end char
78 ? rEndPos.GetContentIndex() - 1
79 : rTextNode.Len());
80 for (sal_Int32 i = nEnd; nStart < i; --i)
81 {
82 const sal_Unicode c(rTextNode.GetText()[i - 1]);
83 switch (c)
84 {
86 --nFields;
87 assert(0 <= nFields);
88 break;
90 ++nFields;
91 // fields in field result could happen by manual
92 // editing, although the field update deletes them
93 break;
95 if (nFields == 0)
96 {
97 assert(!ret); // one per field
98 ret.emplace(rTextNode, i - 1);
99#ifndef DBG_UTIL
100 return *ret;
101#endif
102 }
103 break;
104 }
105 }
106 }
107 else if (pNode->IsEndNode() && !pNode->StartOfSectionNode()->IsSectionNode())
108 {
109 assert(nStartNode <= pNode->StartOfSectionIndex());
110 // fieldmark cannot overlap node section, unless it's a section
111 n = pNode->StartOfSectionIndex();
112 }
113 else
114 {
115 assert(pNode->IsNoTextNode() || pNode->IsSectionNode());
116 }
117 }
118 assert(ret); // must have found it
119 return *ret;
120 }
121} // namespace sw::mark
122
123namespace
124{
125 void lcl_FixPosition(SwPosition& rPos)
126 {
127 // make sure the position has 1) the proper node, and 2) a proper index
128 SwTextNode* pTextNode = rPos.GetNode().GetTextNode();
129 if(pTextNode == nullptr && rPos.GetContentIndex() > 0)
130 {
131 SAL_INFO(
132 "sw.core",
133 "illegal position: " << rPos.GetContentIndex()
134 << " without proper TextNode");
135 rPos.nContent.Assign(nullptr, 0);
136 }
137 else if(pTextNode != nullptr && rPos.GetContentIndex() > pTextNode->Len())
138 {
139 SAL_INFO(
140 "sw.core",
141 "illegal position: " << rPos.GetContentIndex()
142 << " is beyond " << pTextNode->Len());
143 rPos.nContent.Assign(pTextNode, pTextNode->Len());
144 }
145 }
146
147 void lcl_AssertFieldMarksSet(const Fieldmark& rField,
148 const sal_Unicode aStartMark,
149 const sal_Unicode aEndMark)
150 {
151 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
152 {
153 SwPosition const& rStart(rField.GetMarkStart());
154 assert(rStart.GetNode().GetTextNode()->GetText()[rStart.GetContentIndex()] == aStartMark); (void) rStart; (void) aStartMark;
155 SwPosition const sepPos(sw::mark::FindFieldSep(rField));
156 assert(sepPos.GetNode().GetTextNode()->GetText()[sepPos.GetContentIndex()] == CH_TXT_ATR_FIELDSEP); (void) sepPos;
157 }
158 else
159 { // must be m_pPos1 < m_pPos2 because of asymmetric SplitNode update
160 assert(rField.GetMarkPos().GetContentIndex() + 1 == rField.GetOtherMarkPos().GetContentIndex());
161 }
162 SwPosition const& rEnd(rField.GetMarkEnd());
163 assert(rEnd.GetNode().GetTextNode()->GetText()[rEnd.GetContentIndex() - 1] == aEndMark); (void) rEnd;
164 }
165
166 void lcl_SetFieldMarks(Fieldmark& rField,
167 SwDoc& io_rDoc,
168 const sal_Unicode aStartMark,
169 const sal_Unicode aEndMark,
170 SwPosition const*const pSepPos)
171 {
173 OUString startChar(aStartMark);
174 if (aEndMark != CH_TXT_ATR_FORMELEMENT
175 && rField.GetMarkStart() == rField.GetMarkEnd())
176 {
177 // do only 1 InsertString call - to expand existing bookmarks at the
178 // position over the whole field instead of just aStartMark
179 startChar += OUStringChar(CH_TXT_ATR_FIELDSEP) + OUStringChar(aEndMark);
180 }
181
182 SwPosition start = rField.GetMarkStart();
183 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
184 {
185 SwPaM aStartPaM(start);
186 io_rDoc.getIDocumentContentOperations().InsertString(aStartPaM, startChar);
187 start.AdjustContent( -startChar.getLength() ); // restore, it was moved by InsertString
188 // do not manipulate via reference directly but call SetMarkStartPos
189 // which works even if start and end pos were the same
190 rField.SetMarkStartPos( start );
191 SwPosition& rEnd = rField.GetMarkEnd(); // note: retrieve after
192 // setting start, because if start==end it can go stale, see SetMarkPos()
193 assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd));
194 if (startChar.getLength() == 1)
195 {
196 *aStartPaM.GetPoint() = pSepPos ? *pSepPos : rEnd;
197 io_rDoc.getIDocumentContentOperations().InsertString(aStartPaM, OUString(CH_TXT_ATR_FIELDSEP));
198 if (!pSepPos || rEnd < *pSepPos)
199 { // rEnd is not moved automatically if it's same as insert pos
200 rEnd.AdjustContent(1);
201 }
202 }
203 assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd));
204 }
205 else
206 {
207 assert(pSepPos == nullptr);
208 }
209
210 SwPosition& rEnd = rField.GetMarkEnd();
211 if (aEndMark && startChar.getLength() == 1)
212 {
213 SwPaM aEndPaM(rEnd);
214 io_rDoc.getIDocumentContentOperations().InsertString(aEndPaM, OUString(aEndMark));
215 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
216 {
217 rEnd.AdjustContent(1); // InsertString didn't move non-empty mark
218 }
219 else
220 { // InsertString moved the mark's end, not its start
221 assert(rField.GetMarkPos().GetContentIndex() + 1 == rField.GetOtherMarkPos().GetContentIndex());
222 }
223 }
224 lcl_AssertFieldMarksSet(rField, aStartMark, aEndMark);
225
227 }
228
229 void lcl_RemoveFieldMarks(const Fieldmark& rField,
230 SwDoc& io_rDoc,
231 const sal_Unicode aStartMark,
232 const sal_Unicode aEndMark)
233 {
235
236 const SwPosition& rStart = rField.GetMarkStart();
237 SwTextNode const*const pStartTextNode = rStart.GetNode().GetTextNode();
238 assert(pStartTextNode);
239 if (aEndMark != CH_TXT_ATR_FORMELEMENT)
240 {
241 (void) pStartTextNode;
242 // check this before start / end because of the +1 / -1 ...
243 SwPosition const sepPos(sw::mark::FindFieldSep(rField));
244 io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(rStart, aStartMark);
246 }
247
248 const SwPosition& rEnd = rField.GetMarkEnd();
249 SwTextNode *const pEndTextNode = rEnd.GetNode().GetTextNode();
250 assert(pEndTextNode);
251 const sal_Int32 nEndPos = (rEnd == rStart)
252 ? rEnd.GetContentIndex()
253 : rEnd.GetContentIndex() - 1;
254 assert(pEndTextNode->GetText()[nEndPos] == aEndMark);
255 SwPosition const aEnd(*pEndTextNode, nEndPos);
257
259 }
260
261 auto InvalidatePosition(SwPosition const& rPos) -> void
262 {
263 SwUpdateAttr const aHint(rPos.GetContentIndex(), rPos.GetContentIndex(), 0);
265 }
266}
267
268namespace sw::mark
269{
271 OUString aName)
272 : m_oPos1(*aPaM.GetPoint())
273 , m_aName(std::move(aName))
274 {
275 m_oPos1->SetMark(this);
276 lcl_FixPosition(*m_oPos1);
277 if (aPaM.HasMark() && (*aPaM.GetMark() != *aPaM.GetPoint()))
278 {
280 lcl_FixPosition(*m_oPos2);
281 }
282 }
283
285 { m_wXBookmark = xBkmk.get(); }
286
287 // For fieldmarks, the CH_TXT_ATR_FIELDSTART and CH_TXT_ATR_FIELDEND
288 // themselves are part of the covered range. This is guaranteed by
289 // TextFieldmark::InitDoc/lcl_AssureFieldMarksSet.
291 {
292 return GetMarkStart() <= rPos && rPos < GetMarkEnd();
293 }
294
295 void MarkBase::SetMarkPos(const SwPosition& rNewPos)
296 {
297 m_oPos1.emplace(rNewPos);
298 m_oPos1->SetMark(this);
299 }
300
302 {
303 m_oPos2.emplace(rNewPos);
304 m_oPos2->SetMark(this);
305 }
306
307 OUString MarkBase::ToString( ) const
308 {
309 return "Mark: ( Name, [ Node1, Index1 ] ): ( " + m_aName + ", [ "
310 + OUString::number( sal_Int32(GetMarkPos().GetNodeIndex()) ) + ", "
311 + OUString::number( GetMarkPos().GetContentIndex( ) ) + " ] )";
312 }
313
315 {
316 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkBase"));
317 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(m_aName.toUtf8().getStr()));
318 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("markPos"));
319 GetMarkPos().dumpAsXml(pWriter);
320 (void)xmlTextWriterEndElement(pWriter);
321 if (IsExpanded())
322 {
323 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("otherMarkPos"));
324 GetOtherMarkPos().dumpAsXml(pWriter);
325 (void)xmlTextWriterEndElement(pWriter);
326 }
327 (void)xmlTextWriterEndElement(pWriter);
328 }
329
331 { }
332
333 OUString MarkBase::GenerateNewName(std::u16string_view rPrefix)
334 {
335 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
336
337 if (bHack)
338 {
339 static sal_Int64 nIdCounter = SAL_CONST_INT64(6000000000);
340 return rPrefix + OUString::number(nIdCounter++);
341 }
342 else
343 {
344 static OUString sUniquePostfix;
345 static sal_Int32 nCount = SAL_MAX_INT32;
346 if(nCount == SAL_MAX_INT32)
347 {
349 std::numeric_limits<unsigned int>::max()));
350 sUniquePostfix = "_" + OUString::number(n);
351 nCount = 0;
352 }
353 // putting the counter in front of the random parts will speed up string comparisons
354 return rPrefix + OUString::number(nCount++) + sUniquePostfix;
355 }
356 }
357
358 void MarkBase::SwClientNotify(const SwModify&, const SfxHint& rHint)
359 {
360 CallSwClientNotify(rHint);
361 if (rHint.GetId() != SfxHintId::SwLegacyModify)
362 return;
363 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
364 if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
365 { // invalidate cached uno object
366 SetXBookmark(nullptr);
367 }
368 }
369
371 {
372 }
373
375 : MarkBase(rPaM, MarkBase::GenerateNewName(u"__NavigatorReminder__"))
376 { }
377
379 : MarkBase(aPaM, MarkBase::GenerateNewName(u"__UnoMark__"))
380 { }
381
383 : MarkBase(aPaM, MarkBase::GenerateNewName(u"__DdeLink__"))
384 { }
385
387 {
388 m_aRefObj = pObj;
389 }
390
392 {
393 if(m_aRefObj.is())
395 }
396
398 {
399 if( m_aRefObj.is() )
400 {
401 if(m_aRefObj->HasDataLinks())
402 {
404 p->SendDataChanged();
405 }
406 m_aRefObj->SetNoServer();
407 }
408 }
409
411 const vcl::KeyCode& rCode,
412 const OUString& rName)
413 : DdeBookmark(aPaM)
414 , m_aCode(rCode)
415 , m_bHidden(false)
416 {
417 m_aName = rName;
418 }
419
421 sw::mark::InsertMode const, SwPosition const*const)
422 {
423 if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
424 {
426 std::make_unique<SwUndoInsBookmark>(*this));
427 }
428 io_rDoc.getIDocumentState().SetModified();
430 }
431
433 {
435
436 if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
437 {
439 std::make_unique<SwUndoDeleteBookmark>(*this));
440 }
441 io_rDoc.getIDocumentState().SetModified();
443 }
444
445 // invalidate text frames in case it's hidden or Formatting Marks enabled
447 {
448 InvalidatePosition(GetMarkPos());
449 if (IsExpanded())
450 {
451 InvalidatePosition(GetOtherMarkPos());
452 }
453 }
454
455 void Bookmark::Hide(bool const isHide)
456 {
457 if (isHide != m_bHidden)
458 {
459 m_bHidden = isHide;
461 }
462 }
463
464 void Bookmark::SetHideCondition(OUString const& rHideCondition)
465 {
466 if (m_sHideCondition != rHideCondition)
467 {
468 m_sHideCondition = rHideCondition;
469 // don't eval condition here yet - probably only needed for
470 // UI editing condition and that doesn't exist yet
471 }
472 }
473
475 {
476 SwDoc& rDoc( GetMarkPos().GetDoc() );
477 return rDoc.GetXmlIdRegistry();
478 }
479
481 {
482 SwDoc& rDoc( GetMarkPos().GetDoc() );
483 return rDoc.IsClipBoard();
484 }
485
487 {
488 return false;
489 }
490
492 {
493 SwDoc& rDoc( GetMarkPos().GetDoc() );
494 return !rDoc.IsInHeaderFooter( GetMarkPos().GetNode() );
495 }
496
497 uno::Reference< rdf::XMetadatable > Bookmark::MakeUnoObject()
498 {
499 SwDoc& rDoc( GetMarkPos().GetDoc() );
500 const uno::Reference< rdf::XMetadatable> xMeta(
501 SwXBookmark::CreateXBookmark(rDoc, this) );
502 return xMeta;
503 }
504
506 : MarkBase(rPaM, MarkBase::GenerateNewName(u"__Fieldmark__"))
507 {
508 if(!IsExpanded())
510 }
511
512 void Fieldmark::SetMarkStartPos( const SwPosition& rNewStartPos )
513 {
514 if ( GetMarkPos( ) <= GetOtherMarkPos( ) )
515 return SetMarkPos( rNewStartPos );
516 else
517 return SetOtherMarkPos( rNewStartPos );
518 }
519
520 OUString Fieldmark::ToString( ) const
521 {
522 return "Fieldmark: ( Name, Type, [ Nd1, Id1 ], [ Nd2, Id2 ] ): ( " + m_aName + ", "
523 + m_aFieldname + ", [ " + OUString::number( sal_Int32(GetMarkPos().GetNodeIndex( )) )
524 + ", " + OUString::number( GetMarkPos( ).GetContentIndex( ) ) + " ], ["
525 + OUString::number( sal_Int32(GetOtherMarkPos().GetNodeIndex( )) ) + ", "
526 + OUString::number( GetOtherMarkPos( ).GetContentIndex( ) ) + " ] ) ";
527 }
528
530 {
531 // TODO: Does exist a better solution to trigger a format of the
532 // fieldmark portion? If yes, please use it.
533 SwPaM aPaM( GetMarkPos(), GetOtherMarkPos() );
534 aPaM.InvalidatePaM();
535 }
536
538 {
539 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Fieldmark"));
540 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldname"), BAD_CAST(m_aFieldname.toUtf8().getStr()));
541 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldHelptext"), BAD_CAST(m_aFieldHelptext.toUtf8().getStr()));
542 MarkBase::dumpAsXml(pWriter);
543 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("parameters"));
544 for (auto& rParam : m_vParams)
545 {
546 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("parameter"));
547 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(rParam.first.toUtf8().getStr()));
548 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(comphelper::anyToString(rParam.second).toUtf8().getStr()));
549 (void)xmlTextWriterEndElement(pWriter);
550 }
551 (void)xmlTextWriterEndElement(pWriter);
552 (void)xmlTextWriterEndElement(pWriter);
553 }
554
555 TextFieldmark::TextFieldmark(const SwPaM& rPaM, const OUString& rName)
556 : Fieldmark(rPaM)
557 {
558 if ( !rName.isEmpty() )
559 m_aName = rName;
560 }
561
563 sw::mark::InsertMode const eMode, SwPosition const*const pSepPos)
564 {
566 {
567 lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos);
568 }
569 else
570 {
571 lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
572 }
573 }
574
576 {
578 if (rIDUR.DoesUndo())
579 {
580 rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this));
581 }
582 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
583 lcl_RemoveFieldMarks(*this, rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
584 // notify layouts to unhide - for the entire fieldmark, as in InitDoc()
585 SwPaM const tmp(GetMarkPos(), GetOtherMarkPos());
587 }
588
590 : Fieldmark(rPaM)
591 { }
592
594 sw::mark::InsertMode const eMode, SwPosition const*const pSepPos)
595 {
596 assert(pSepPos == nullptr);
598 {
599 lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT, pSepPos);
600 }
601 else
602 {
603 lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT);
604 }
605 }
606
608 {
610 if (rIDUR.DoesUndo())
611 {
612 rIDUR.AppendUndo(std::make_unique<SwUndoDelNoTextFieldmark>(*this));
613 }
614 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
615 lcl_RemoveFieldMarks(*this, rDoc,
617 }
618
619
621 : NonTextFieldmark(rPaM)
622 { }
623
625 {
626 if ( IsChecked() != checked )
627 {
628 (*GetParameters())[OUString(ODF_FORMCHECKBOX_RESULT)] <<= checked;
629 // mark document as modified
630 SwDoc& rDoc( GetMarkPos().GetDoc() );
632 }
633 }
634
636 {
637 bool bResult = false;
638 parameter_map_t::const_iterator pResult = GetParameters()->find(OUString(ODF_FORMCHECKBOX_RESULT));
639 if(pResult != GetParameters()->end())
640 pResult->second >>= bResult;
641 return bResult;
642 }
643
645 : NonTextFieldmark(rPaM)
646 , m_pButton(nullptr)
647 {
648 }
649
651 {
653 }
654
656 {
657 if(m_pButton)
659 }
660
663 {
664 }
665
667 {
668 }
669
671 {
672 if(pEditWin)
673 {
674 if(!m_pButton)
676 m_pButton->CalcPosAndSize(m_aPortionPaintArea);
677 m_pButton->Show();
678 }
679 }
680
682 {
684 }
685
686 void DropDownFieldmark::SetPortionPaintArea(const SwRect& rPortionPaintArea)
687 {
688 m_aPortionPaintArea = rPortionPaintArea;
689 if(m_pButton)
690 {
691 m_pButton->Show();
692 m_pButton->CalcPosAndSize(m_aPortionPaintArea);
693 }
694 }
695
697 {
699 return;
700
701 if (!pViewShell || pViewShell->isLOKMobilePhone())
702 return;
703
705 return;
706
707 OStringBuffer sPayload;
708 sPayload = OString::Concat("{\"action\": \"show\","
709 " \"type\": \"drop-down\", \"textArea\": \"") +
711 // Add field params to the message
712 sPayload.append(" \"params\": { \"items\": [");
713
714 // List items
715 auto pParameters = this->GetParameters();
716 auto pListEntriesIter = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
717 css::uno::Sequence<OUString> vListEntries;
718 if (pListEntriesIter != pParameters->end())
719 {
720 pListEntriesIter->second >>= vListEntries;
721 for (const OUString& sItem : std::as_const(vListEntries))
722 sPayload.append("\"" + OUStringToOString(sItem, RTL_TEXTENCODING_UTF8) + "\", ");
723 sPayload.setLength(sPayload.getLength() - 2);
724 }
725 sPayload.append("], ");
726
727 // Selected item
728 auto pSelectedItemIter = pParameters->find(ODF_FORMDROPDOWN_RESULT);
729 sal_Int32 nSelection = -1;
730 if (pSelectedItemIter != pParameters->end())
731 {
732 pSelectedItemIter->second >>= nSelection;
733 }
734 sPayload.append("\"selected\": \"" + OString::number(nSelection) + "\", ");
735
736 // Placeholder text
737 sPayload.append("\"placeholderText\": \"" + OUStringToOString(SwResId(STR_DROP_DOWN_EMPTY_LIST), RTL_TEXTENCODING_UTF8) + "\"}}");
738 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON, sPayload.toString().getStr());
739 }
740
742 {
743 OString sPayload = "{\"action\": \"hide\", \"type\": \"drop-down\"}";
744 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON, sPayload.getStr());
745 }
746
749 , m_pNumberFormatter(nullptr)
750 , m_pDocumentContentOperationsManager(nullptr)
751 {
752 }
753
755 {
756 }
757
759 sw::mark::InsertMode eMode, SwPosition const*const pSepPos)
760 {
764 {
765 lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos);
766 }
767 else
768 {
769 lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
770 }
771 }
772
774 {
776 if (rIDUR.DoesUndo())
777 {
778 // TODO does this need a 3rd Undo class?
779 rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this));
780 }
781 ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
782 lcl_RemoveFieldMarks(*this, rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
783 // notify layouts to unhide - for the entire fieldmark, as in InitDoc()
784 SwPaM const tmp(GetMarkPos(), GetOtherMarkPos());
786 }
787
789 {
790 if(pEditWin)
791 {
792 if(!m_pButton)
795 m_pButton->CalcPosAndSize(aPaintArea);
796 m_pButton->Show();
797 }
798 }
799
800 void DateFieldmark::SetPortionPaintAreaStart(const SwRect& rPortionPaintArea)
801 {
802 if (rPortionPaintArea.IsEmpty())
803 return;
804
805 m_aPaintAreaStart = rPortionPaintArea;
807 }
808
809 void DateFieldmark::SetPortionPaintAreaEnd(const SwRect& rPortionPaintArea)
810 {
811 if (rPortionPaintArea.IsEmpty())
812 return;
813
814 if(m_aPaintAreaEnd == rPortionPaintArea &&
815 m_pButton && m_pButton->IsVisible())
816 return;
817
818 m_aPaintAreaEnd = rPortionPaintArea;
819 if(m_pButton)
820 {
821 m_pButton->Show();
823 m_pButton->CalcPosAndSize(aPaintArea);
824 m_pButton->Invalidate();
825 }
827 }
828
830 {
831 const SwTextNode* const pTextNode = GetMarkEnd().GetNode().GetTextNode();
832 SwPosition const sepPos(sw::mark::FindFieldSep(*this));
833 const sal_Int32 nStart(sepPos.GetContentIndex());
834 const sal_Int32 nEnd (GetMarkEnd().GetContentIndex());
835
836 OUString sContent;
837 if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() &&
838 nEnd > nStart + 2)
839 sContent = pTextNode->GetText().copy(nStart + 1, nEnd - nStart - 2);
840 return sContent;
841 }
842
843 void DateFieldmark::ReplaceContent(const OUString& sNewContent)
844 {
846 return;
847
848 const SwTextNode* const pTextNode = GetMarkEnd().GetNode().GetTextNode();
849 SwPosition const sepPos(sw::mark::FindFieldSep(*this));
850 const sal_Int32 nStart(sepPos.GetContentIndex());
851 const sal_Int32 nEnd (GetMarkEnd().GetContentIndex());
852
853 if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() &&
854 nEnd > nStart + 2)
855 {
856 SwPaM aFieldPam(GetMarkStart().GetNode(), nStart + 1,
857 GetMarkStart().GetNode(), nEnd - 1);
858 m_pDocumentContentOperationsManager->ReplaceRange(aFieldPam, sNewContent, false);
859 }
860 else
861 {
862 SwPaM aFieldStartPam(GetMarkStart().GetNode(), nStart + 1);
863 m_pDocumentContentOperationsManager->InsertString(aFieldStartPam, sNewContent);
864 }
865
866 }
867
868 std::pair<bool, double> DateFieldmark::GetCurrentDate() const
869 {
870 // Check current date param first
871 std::pair<bool, double> aResult = ParseCurrentDateParam();
872 if(aResult.first)
873 return aResult;
874
876 bool bFoundValidDate = false;
877 double dCurrentDate = 0;
878 OUString sDateFormat;
879 auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
880 if (pResult != pParameters->end())
881 {
882 pResult->second >>= sDateFormat;
883 }
884
885 OUString sLang;
886 pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
887 if (pResult != pParameters->end())
888 {
889 pResult->second >>= sLang;
890 }
891
892 // Get current content of the field
893 OUString sContent = GetContent();
894
895 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType());
896 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
897 {
898 sal_Int32 nCheckPos = 0;
900 m_pNumberFormatter->PutEntry(sDateFormat,
901 nCheckPos,
902 nType,
903 nFormat,
904 LanguageTag(sLang).getLanguageType());
905 }
906
907 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
908 {
909 bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sContent, nFormat, dCurrentDate);
910 }
911 return std::pair<bool, double>(bFoundValidDate, dCurrentDate);
912 }
913
915 {
916 // Replace current content with the selected date
918
919 // Also save the current date in a standard format
921 (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= GetDateInStandardDateFormat(fDate);
922 }
923
925 {
926 OUString sCurrentDate;
928 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
929 {
930 sal_Int32 nCheckPos = 0;
932 OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT;
934 nCheckPos,
935 nType,
936 nFormat,
938 }
939
940 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
941 {
942 const Color* pCol = nullptr;
943 m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentDate, &pCol, false);
944 }
945 return sCurrentDate;
946 }
947
948 std::pair<bool, double> DateFieldmark::ParseCurrentDateParam() const
949 {
950 bool bFoundValidDate = false;
951 double dCurrentDate = 0;
952
954 auto pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE);
955 OUString sCurrentDate;
956 if (pResult != pParameters->end())
957 {
958 pResult->second >>= sCurrentDate;
959 }
960 if(!sCurrentDate.isEmpty())
961 {
963 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
964 {
965 sal_Int32 nCheckPos = 0;
967 OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT;
969 nCheckPos,
970 nType,
971 nFormat,
973 }
974
975 if(nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
976 {
977 bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sCurrentDate, nFormat, dCurrentDate);
978 }
979 }
980 return std::pair<bool, double>(bFoundValidDate, dCurrentDate);
981 }
982
983
984 OUString DateFieldmark::GetDateInCurrentDateFormat(double fDate) const
985 {
986 // Get current date format and language
987 OUString sDateFormat;
989 auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
990 if (pResult != pParameters->end())
991 {
992 pResult->second >>= sDateFormat;
993 }
994
995 OUString sLang;
996 pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
997 if (pResult != pParameters->end())
998 {
999 pResult->second >>= sLang;
1000 }
1001
1002 // Fill the content with the specified format
1003 OUString sCurrentContent;
1004 sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType());
1005 if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
1006 {
1007 sal_Int32 nCheckPos = 0;
1009 OUString sFormat = sDateFormat;
1011 nCheckPos,
1012 nType,
1013 nFormat,
1014 LanguageTag(sLang).getLanguageType());
1015 }
1016
1017 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
1018 {
1019 const Color* pCol = nullptr;
1020 m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentContent, &pCol, false);
1021 }
1022 return sCurrentContent;
1023 }
1024
1026 {
1027 std::pair<bool, double> aResult = ParseCurrentDateParam();
1028 if(!aResult.first)
1029 return;
1030
1031 // Current date became invalid
1032 if(GetDateInCurrentDateFormat(aResult.second) != GetContent())
1033 {
1035 (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= OUString();
1036 }
1037 }
1038}
1039
1040/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
struct _xmlTextWriter * xmlTextWriterPtr
virtual bool InsertString(const SwPaM &rRg, const OUString &, const SwInsertFlags nInsertMode=SwInsertFlags::EMPTYEXPAND)=0
Insert string into existing text node at position rRg.Point().
virtual sfx2::LinkManager & GetLinkManager()=0
virtual void SetModified()=0
Must be called manually at changes of format.
virtual SwUndoId EndUndo(SwUndoId const eUndoId, SwRewriter const *const pRewriter)=0
Closes undo block.
virtual void AppendUndo(std::unique_ptr< SwUndo > pUndo)=0
Add new Undo action.
virtual bool DoesUndo() const =0
Is Undo enabled?
virtual SwUndoId StartUndo(SwUndoId const eUndoId, SwRewriter const *const pRewriter)=0
Opens undo block.
SfxHintId GetId() const
bool isLOKMobilePhone() const
virtual void libreOfficeKitViewCallback(int nType, const char *pPayload) const override
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)
SwContentIndex & Assign(const SwContentNode *, sal_Int32)
Definition: index.cxx:206
Definition: doc.hxx:192
IDocumentState const & getIDocumentState() const
Definition: doc.cxx:395
bool IsClipBoard() const
Definition: doc.hxx:967
IDocumentLinksAdministration const & getIDocumentLinksAdministration() const
Definition: doc.cxx:261
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:316
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:145
::sfx2::IXmlIdRegistry & GetXmlIdRegistry()
Definition: docnew.cxx:820
::sw::DocumentContentOperationsManager const & GetDocumentContentOperationsManager() const
Definition: doc.cxx:326
SvNumberFormatter * GetNumberFormatter(bool bCreate=true)
Definition: doc.hxx:1414
bool IsInHeaderFooter(const SwNode &) const
Definition: doclay.cxx:1550
Window class for the Writer edit area, this is the one handling mouse and keyboard events and doing t...
Definition: edtwin.hxx:61
Base class of the Writer document model elements.
Definition: node.hxx:84
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:877
bool IsNoTextNode() const
Definition: node.hxx:677
bool IsEndNode() const
Definition: node.hxx:661
SwNodeOffset StartOfSectionIndex() const
Definition: node.hxx:702
bool IsSectionNode() const
Definition: node.hxx:673
bool IsTextNode() const
Definition: node.hxx:665
const SwStartNode * StartOfSectionNode() const
Definition: node.hxx:137
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:187
const SwPosition * GetMark() const
Definition: pam.hxx:263
const SwPosition * GetPoint() const
Definition: pam.hxx:261
void InvalidatePaM()
Definition: pam.cxx:1331
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:259
Of course Writer needs its own rectangles.
Definition: swrect.hxx:35
bool IsEmpty() const
Definition: swrect.hxx:304
Point TopLeft() const
Definition: swrect.hxx:254
Point BottomRight() const
Definition: swrect.hxx:266
tools::Rectangle SVRect() const
Definition: swrect.hxx:292
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:89
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:293
const OUString & GetText() const
Definition: ndtxt.hxx:227
static rtl::Reference< SwXBookmark > CreateXBookmark(SwDoc &rDoc, ::sw::mark::IMark *pBookmark)
Definition: unobkm.cxx:157
void disposeAndClear()
static VclPtr< reference_type > Create(Arg &&... arg)
void RemoveServer(SvLinkSource *rObj)
virtual void CallSwClientNotify(const SfxHint &rHint) const override
Definition: calbck.cxx:325
bool ReplaceRange(SwPaM &rPam, const OUString &rNewStr, const bool bRegExReplace) override
Replace selected range in a TextNode with string.
bool InsertString(const SwPaM &rRg, const OUString &, const SwInsertFlags nInsertMode=SwInsertFlags::EMPTYEXPAND) override
Insert string into existing text node at position rRg.Point().
void DeleteDummyChar(SwPosition const &rPos, sal_Unicode cDummy)
virtual void SetHideCondition(const OUString &rHideCondition) override
virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
virtual auto InvalidateFrames() -> void override
virtual bool IsInClipboard() const override
virtual void DeregisterFromDoc(SwDoc &io_rDoc) override
virtual bool IsInUndo() const override
virtual void Hide(bool rHide) override
virtual ::sfx2::IXmlIdRegistry & GetRegistry() override
Bookmark(const SwPaM &rPaM, const vcl::KeyCode &rCode, const OUString &rName)
virtual bool IsInContent() const override
virtual void InitDoc(SwDoc &io_Doc, sw::mark::InsertMode eMode, SwPosition const *pSepPos) override
bool IsChecked() const override
void SetChecked(bool checked) override
CheckboxFieldmark(const SwPaM &rPaM)
void SetPortionPaintAreaStart(const SwRect &rPortionPaintArea)
std::pair< bool, double > ParseCurrentDateParam() const
virtual std::pair< bool, double > GetCurrentDate() const override
virtual void InitDoc(SwDoc &io_rDoc, sw::mark::InsertMode eMode, SwPosition const *pSepPos) override
virtual ~DateFieldmark() override
virtual void SetCurrentDate(double fDate) override
DateFieldmark(const SwPaM &rPaM)
SvNumberFormatter * m_pNumberFormatter
virtual void ReleaseDoc(SwDoc &rDoc) override
virtual OUString GetDateInStandardDateFormat(double fDate) const override
virtual OUString GetContent() const override
virtual void ReplaceContent(const OUString &sNewContent) override
virtual void ShowButton(SwEditWin *pEditWin) override
void SetPortionPaintAreaEnd(const SwRect &rPortionPaintArea)
OUString GetDateInCurrentDateFormat(double fDate) const
sw::DocumentContentOperationsManager * m_pDocumentContentOperationsManager
virtual ~DdeBookmark() override
void SetRefObject(SwServerObject *pObj)
DdeBookmark(const SwPaM &rPaM)
tools::SvRef< SwServerObject > m_aRefObj
virtual void DeregisterFromDoc(SwDoc &rDoc)
void SendLOKShowMessage(const SfxViewShell *pViewShell)
virtual void ShowButton(SwEditWin *pEditWin) override
virtual void RemoveButton() override
virtual ~DropDownFieldmark() override
DropDownFieldmark(const SwPaM &rPaM)
void SetPortionPaintArea(const SwRect &rPortionPaintArea)
static void SendLOKHideMessage(const SfxViewShell *pViewShell)
Fieldmark with a drop down button (e.g. this button opens the date picker for a date field)
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override
virtual IFieldmark::parameter_map_t * GetParameters() override
virtual void Invalidate() override
IFieldmark::parameter_map_t m_vParams
void SetMarkStartPos(const SwPosition &rNewStartPos)
Fieldmark(const SwPaM &rPaM)
virtual OUString ToString() const override
std::map< OUString, css::uno::Any > parameter_map_t
Definition: IMark.hxx:94
virtual parameter_map_t * GetParameters()=0
virtual const SwPosition & GetOtherMarkPos() const =0
virtual const SwPosition & GetMarkEnd() const =0
virtual const SwPosition & GetMarkStart() const =0
virtual const SwPosition & GetMarkPos() const =0
virtual void SwClientNotify(const SwModify &, const SfxHint &) override
virtual void SetOtherMarkPos(const SwPosition &rNewPos)
virtual SwPosition & GetMarkPos() const override
std::optional< SwPosition > m_oPos1
virtual ~MarkBase() override
virtual OUString ToString() const override
void SetXBookmark(rtl::Reference< SwXBookmark > const &xBkmk)
virtual SwPosition & GetOtherMarkPos() const override
virtual bool IsExpanded() const override
virtual SwPosition & GetMarkEnd() const override
virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override
std::optional< SwPosition > m_oPos2
unotools::WeakReference< SwXBookmark > m_wXBookmark
virtual auto InvalidateFrames() -> void
virtual bool IsCoveringPosition(const SwPosition &rPos) const override
virtual void SetMarkPos(const SwPosition &rNewPos)
MarkBase(const SwPaM &rPaM, OUString aName)
static OUString GenerateNewName(std::u16string_view rPrefix)
virtual SwPosition & GetMarkStart() const override
NavigatorReminder(const SwPaM &rPaM)
NonTextFieldmark(const SwPaM &rPaM)
virtual void ReleaseDoc(SwDoc &rDoc) override
virtual void InitDoc(SwDoc &io_rDoc, sw::mark::InsertMode eMode, SwPosition const *pSepPos) override
virtual void InitDoc(SwDoc &io_rDoc, sw::mark::InsertMode eMode, SwPosition const *pSepPos) override
TextFieldmark(const SwPaM &rPaM, const OUString &rName)
virtual void ReleaseDoc(SwDoc &rDoc) override
UnoMark(const SwPaM &rPaM)
rtl::OString toString() const
T * get() const
bool is() const
int nCount
float u
#define CH_TXT_ATR_FIELDSEP
Definition: hintids.hxx:180
constexpr TypedWhichId< SwPtrMsgPoolItem > RES_REMOVE_UNO_OBJECT(181)
#define CH_TXT_ATR_FORMELEMENT
Definition: hintids.hxx:177
#define CH_TXT_ATR_FIELDEND
Definition: hintids.hxx:181
#define CH_TXT_ATR_FIELDSTART
Definition: hintids.hxx:179
OUString aName
Mode eMode
void * p
sal_Int64 n
#define SAL_INFO(area, stream)
double getLength(const B2DPolygon &rCandidate)
unsigned int uniform_uint_distribution(unsigned int a, unsigned int b)
OUString anyToString(uno::Any const &value)
int i
end
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
SwPosition FindFieldSep(IFieldmark const &rMark)
return position of the CH_TXT_ATR_FIELDSEP for rMark
InsertMode
Definition: IMark.hxx:32
void UpdateFramesForRemoveDeleteRedline(SwDoc &rDoc, SwPaM const &rPam)
constexpr OUStringLiteral ODF_FORMCHECKBOX_RESULT
constexpr OUStringLiteral ODF_FORMDROPDOWN_RESULT
constexpr OUStringLiteral ODF_FORMDATE_CURRENTDATE
constexpr OUStringLiteral ODF_FORMDATE_DATEFORMAT
constexpr OUStringLiteral ODF_FORMDATE_CURRENTDATE_FORMAT
#define ODF_FORMDATE_CURRENTDATE_LANGUAGE
constexpr OUStringLiteral ODF_FORMDROPDOWN_LISTENTRY
constexpr OUStringLiteral ODF_FORMDATE_DATEFORMAT_LANGUAGE
SwContentNode * GetNode(SwPaM &rPam, bool &rbFirst, SwMoveFnCollection const &fnMove, bool const bInReadOnly, SwRootFrame const *const i_pLayout)
This function returns the next node in direction of search.
Definition: pam.cxx:1012
OUString m_aName
QPRO_FUNC_TYPE nType
Marks a position in the document model.
Definition: pam.hxx:37
SwNode & GetNode() const
Definition: pam.hxx:80
void dumpAsXml(xmlTextWriterPtr pWriter) const
Definition: pam.cxx:222
SwNodeOffset GetNodeIndex() const
Definition: pam.hxx:77
const SwNodes & GetNodes() const
Definition: pam.hxx:78
sal_Int32 GetContentIndex() const
Definition: pam.hxx:84
void AdjustContent(sal_Int32 nDelta)
Adjust content index, only valid to call this if the position points to a SwContentNode subclass.
Definition: pam.cxx:261
SwContentIndex nContent
Definition: pam.hxx:39
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:164
#define SAL_MAX_INT32
sal_uInt16 sal_Unicode
SvNumFormatType
constexpr sal_uInt32 NUMBERFORMAT_ENTRY_NOT_FOUND