ITEM: Refactor ItemType
[LibreOffice.git] / sw / source / core / doc / docfld.cxx
blobeb4737575a716be8cc23ad32a84ef3f4547e3dc4
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 <config_features.h>
21 #include <config_fuzzers.h>
23 #include <hintids.hxx>
25 #include <comphelper/string.hxx>
26 #include <osl/diagnose.h>
27 #include <unotools/charclass.hxx>
28 #ifndef UNX
29 #include <unotools/transliterationwrapper.hxx>
30 #endif
31 #include <o3tl/string_view.hxx>
32 #include <doc.hxx>
33 #include <IDocumentFieldsAccess.hxx>
34 #include <IDocumentMarkAccess.hxx>
35 #include <IDocumentState.hxx>
36 #include <IDocumentLayoutAccess.hxx>
37 #include <node2lay.hxx>
38 #include <cntfrm.hxx>
39 #include <pagefrm.hxx>
40 #include <txtfrm.hxx>
41 #include <notxtfrm.hxx>
42 #include <pam.hxx>
43 #include <ndtxt.hxx>
44 #include <swtable.hxx>
45 #include <calc.hxx>
46 #include <txtfld.hxx>
47 #include <fmtfld.hxx>
48 #include <txttxmrk.hxx>
49 #include <docfld.hxx>
50 #include <docufld.hxx>
51 #include <usrfld.hxx>
52 #include <expfld.hxx>
53 #include <dbfld.hxx>
54 #include <reffld.hxx>
55 #include <dbmgr.hxx>
56 #include <section.hxx>
57 #include <docary.hxx>
58 #include <authfld.hxx>
59 #include <txtinet.hxx>
60 #include <fmtcntnt.hxx>
61 #include <utility>
63 // the StartIndex can be supplied optionally (e.g. if it was queried before - is a virtual
64 // method otherwise!)
65 SetGetExpField::SetGetExpField(
66 const SwNode& rNdIdx,
67 const SwTextField* pField,
68 std::optional<sal_Int32> oContentIdx,
69 sal_uInt16 const nPageNumber)
70 : m_nPageNumber(nPageNumber)
72 m_eSetGetExpFieldType = TEXTFIELD;
73 m_CNTNT.pTextField = pField;
74 m_nNode = rNdIdx.GetIndex();
75 if( oContentIdx )
76 m_nContent = *oContentIdx;
77 else if( pField )
78 m_nContent = pField->GetStart();
79 else
80 m_nContent = 0;
83 SetGetExpField::SetGetExpField( const SwNode& rNdIdx,
84 const SwTextINetFormat& rINet )
86 m_eSetGetExpFieldType = TEXTINET;
87 m_CNTNT.pTextINet = &rINet;
88 m_nNode = rNdIdx.GetIndex();
89 m_nContent = rINet.GetStart();
92 // Extension for Sections:
93 // these always have content position 0xffffffff!
94 // There is never a field on this, only up to COMPLETE_STRING possible
95 SetGetExpField::SetGetExpField( const SwSectionNode& rSectNd,
96 const SwPosition* pPos,
97 sal_uInt16 const nPageNumber)
98 : m_nPageNumber(nPageNumber)
100 m_eSetGetExpFieldType = SECTIONNODE;
101 m_CNTNT.pSection = &rSectNd.GetSection();
103 if( pPos )
105 m_nNode = pPos->GetNodeIndex();
106 m_nContent = pPos->GetContentIndex();
108 else
110 m_nNode = rSectNd.GetIndex();
111 m_nContent = 0;
115 SetGetExpField::SetGetExpField(::sw::mark::Bookmark const& rBookmark,
116 SwPosition const*const pPos,
117 sal_uInt16 const nPageNumber)
118 : m_nPageNumber(nPageNumber)
120 m_eSetGetExpFieldType = BOOKMARK;
121 m_CNTNT.pBookmark = &rBookmark;
123 if (pPos)
125 m_nNode = pPos->GetNodeIndex();
126 m_nContent = pPos->GetContentIndex();
128 else
130 m_nNode = rBookmark.GetMarkStart().GetNodeIndex();
131 m_nContent = rBookmark.GetMarkStart().GetContentIndex();;
135 SetGetExpField::SetGetExpField( const SwTableBox& rTBox )
137 m_eSetGetExpFieldType = TABLEBOX;
138 m_CNTNT.pTBox = &rTBox;
140 m_nNode = SwNodeOffset(0);
141 m_nContent = 0;
142 if( rTBox.GetSttNd() )
144 SwNodeIndex aIdx( *rTBox.GetSttNd() );
145 const SwContentNode* pNd = SwNodes::GoNext(&aIdx);
146 if( pNd )
147 m_nNode = pNd->GetIndex();
151 SetGetExpField::SetGetExpField( const SwNode& rNdIdx,
152 const SwTextTOXMark& rTOX )
154 m_eSetGetExpFieldType = TEXTTOXMARK;
155 m_CNTNT.pTextTOX = &rTOX;
156 m_nNode = rNdIdx.GetIndex();
157 m_nContent = rTOX.GetStart();
160 SetGetExpField::SetGetExpField( const SwPosition& rPos )
162 m_eSetGetExpFieldType = CRSRPOS;
163 m_CNTNT.pPos = &rPos;
164 m_nNode = rPos.GetNodeIndex();
165 m_nContent = rPos.GetContentIndex();
168 SetGetExpField::SetGetExpField( const SwFlyFrameFormat& rFlyFormat,
169 const SwPosition* pPos )
171 m_eSetGetExpFieldType = FLYFRAME;
172 m_CNTNT.pFlyFormat = &rFlyFormat;
173 if( pPos )
175 m_nNode = pPos->GetNodeIndex();
176 m_nContent = pPos->GetContentIndex();
178 else
180 const SwFormatContent& rContent = rFlyFormat.GetContent();
181 m_nNode = rContent.GetContentIdx()->GetIndex() + 1;
182 m_nContent = 0;
186 void SetGetExpField::GetPosOfContent( SwPosition& rPos ) const
188 const SwNode* pNd = GetNodeFromContent();
189 if( pNd )
190 pNd = pNd->GetContentNode();
192 if( pNd )
194 rPos.Assign( *static_cast<const SwContentNode*>(pNd), GetCntPosFromContent() );
196 else
198 rPos.Assign( m_nNode, m_nContent );
202 void SetGetExpField::SetBodyPos( const SwContentFrame& rFrame )
204 if( !rFrame.IsInDocBody() )
206 SwNodeIndex aIdx( rFrame.IsTextFrame()
207 ? *static_cast<SwTextFrame const&>(rFrame).GetTextNodeFirst()
208 : *static_cast<SwNoTextFrame const&>(rFrame).GetNode() );
209 SwDoc& rDoc = aIdx.GetNodes().GetDoc();
210 SwPosition aPos( aIdx );
211 bool const bResult = ::GetBodyTextNode( rDoc, aPos, rFrame );
212 OSL_ENSURE(bResult, "Where is the field?");
213 m_nNode = aPos.GetNodeIndex();
214 // tdf#106663 - use the starting position of the frame
215 m_nContent = 0;
219 bool SetGetExpField::operator==( const SetGetExpField& rField ) const
221 return m_nNode == rField.m_nNode
222 && m_nContent == rField.m_nContent
223 && ( !m_CNTNT.pTextField
224 || !rField.m_CNTNT.pTextField
225 || m_CNTNT.pTextField == rField.m_CNTNT.pTextField );
228 bool SetGetExpField::operator<( const SetGetExpField& rField ) const
230 if (m_nPageNumber != rField.m_nPageNumber)
232 // sort "invalid" page nums of 0 after valid page nums of non 0
233 if (m_nPageNumber == 0 || rField.m_nPageNumber == 0)
234 return m_nPageNumber != 0;
235 return m_nPageNumber < rField.m_nPageNumber;
237 if( m_nNode < rField.m_nNode || ( m_nNode == rField.m_nNode && m_nContent < rField.m_nContent ))
238 return true;
239 else if( m_nNode != rField.m_nNode || m_nContent != rField.m_nContent )
240 return false;
242 const SwNode *pFirst = GetNodeFromContent(),
243 *pNext = rField.GetNodeFromContent();
245 // Position is the same: continue only if both field pointers are set!
246 if( !pFirst || !pNext )
247 return false;
249 // same Section?
250 if( pFirst->StartOfSectionNode() != pNext->StartOfSectionNode() )
252 // is one in the table?
253 const SwNode *pFirstStt, *pNextStt;
254 const SwTableNode* pTableNd = pFirst->FindTableNode();
255 if( pTableNd )
256 pFirstStt = pTableNd->StartOfSectionNode();
257 else
258 pFirstStt = pFirst->StartOfSectionNode();
260 pTableNd = pNext->FindTableNode();
261 if( pTableNd )
262 pNextStt = pTableNd->StartOfSectionNode();
263 else
264 pNextStt = pNext->StartOfSectionNode();
266 if( pFirstStt != pNextStt )
268 if( pFirst->IsTextNode() && pNext->IsTextNode() &&
269 ( pFirst->FindFlyStartNode() || pNext->FindFlyStartNode() ))
271 // FIXME: in NewFieldPortion(), SwGetExpField are expanded via
272 // DocumentFieldsManager::FieldsToExpand() calling
273 // std::upper_bound binary search function - the sort order
274 // depends on the fly positions in the layout, but the fly
275 // positions depend on the expansion of the SwGetExpField!
276 // This circular dep will cause trouble, it would be better to
277 // use only model positions (anchor), but then how to compare
278 // at-page anchored flys which don't have a model anchor?
279 return ::IsFrameBehind( *pNext->GetTextNode(), m_nContent, *pFirst->GetTextNode(), m_nContent );
281 return pFirstStt->GetIndex() < pNextStt->GetIndex();
285 // same Section: is the field in the same Node?
286 if( pFirst != pNext )
287 return pFirst->GetIndex() < pNext->GetIndex();
289 // same Node in the Section, check Position in the Node
290 return GetCntPosFromContent() < rField.GetCntPosFromContent();
293 const SwNode* SetGetExpField::GetNodeFromContent() const
295 const SwNode* pRet = nullptr;
296 if( m_CNTNT.pTextField )
297 switch( m_eSetGetExpFieldType )
299 case TEXTFIELD:
300 pRet = &m_CNTNT.pTextField->GetTextNode();
301 break;
303 case TEXTINET:
304 pRet = &m_CNTNT.pTextINet->GetTextNode();
305 break;
307 case SECTIONNODE:
308 pRet = m_CNTNT.pSection->GetFormat()->GetSectionNode();
309 break;
311 case BOOKMARK:
312 pRet = &m_CNTNT.pBookmark->GetMarkStart().GetNode();
313 break;
315 case CRSRPOS:
316 pRet = &m_CNTNT.pPos->GetNode();
317 break;
319 case TEXTTOXMARK:
320 pRet = &m_CNTNT.pTextTOX->GetTextNode();
321 break;
323 case TABLEBOX:
324 if( m_CNTNT.pTBox->GetSttNd() )
326 SwNodeIndex aIdx( *m_CNTNT.pTBox->GetSttNd() );
327 pRet = SwNodes::GoNext(&aIdx);
329 break;
331 case FLYFRAME:
333 SwNodeIndex aIdx( *m_CNTNT.pFlyFormat->GetContent().GetContentIdx() );
334 pRet = SwNodes::GoNext(&aIdx);
336 break;
338 return pRet;
341 sal_Int32 SetGetExpField::GetCntPosFromContent() const
343 sal_Int32 nRet = 0;
344 if( m_CNTNT.pTextField )
345 switch( m_eSetGetExpFieldType )
347 case TEXTFIELD:
348 nRet = m_CNTNT.pTextField->GetStart();
349 break;
350 case TEXTINET:
351 nRet = m_CNTNT.pTextINet->GetStart();
352 break;
353 case TEXTTOXMARK:
354 nRet = m_CNTNT.pTextTOX->GetStart();
355 break;
356 case BOOKMARK:
357 nRet = m_CNTNT.pBookmark->GetMarkStart().GetContentIndex();
358 break;
359 case CRSRPOS:
360 nRet = m_CNTNT.pPos->GetContentIndex();
361 break;
362 default:
363 break;
365 return nRet;
368 /// Look up the Name, if it is present, return its String, otherwise return an empty String
369 OUString LookString( std::unordered_map<OUString, OUString> const & rTable, const OUString& rName )
371 auto it = rTable.find( comphelper::string::strip(rName, ' ') );
372 if( it != rTable.end() )
373 return it->second;
375 return OUString();
378 SwDBData const & SwDoc::GetDBData()
380 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
381 if(maDBData.sDataSource.isEmpty())
383 // Similar to: SwEditShell::IsAnyDatabaseFieldInDoc
384 for (const auto& pFieldType : *getIDocumentFieldsAccess().GetFieldTypes())
386 if (IsUsed(*pFieldType))
388 SwFieldIds nWhich = pFieldType->Which();
389 switch(nWhich)
391 case SwFieldIds::Database:
392 case SwFieldIds::DbNextSet:
393 case SwFieldIds::DbNumSet:
394 case SwFieldIds::DbSetNumber:
396 std::vector<SwFormatField*> vFields;
397 pFieldType->GatherFields(vFields);
398 if(vFields.size())
400 if(SwFieldIds::Database == nWhich)
401 maDBData = static_cast<SwDBFieldType*>(vFields.front()->GetField()->GetTyp())->GetDBData();
402 else
403 maDBData = static_cast<SwDBNameInfField*> (vFields.front()->GetField())->GetRealDBData();
406 break;
407 default: break;
412 if(maDBData.sDataSource.isEmpty())
413 maDBData = SwDBManager::GetAddressDBName();
414 #endif
415 return maDBData;
418 void SwDoc::SetInitDBFields( bool b )
420 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
421 (void) b;
422 #else
423 GetDBManager()->SetInitDBFields( b );
424 #endif
427 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
429 /// Get all databases that are used by fields
430 static OUString lcl_DBDataToString(const SwDBData& rData)
432 return rData.sDataSource + OUStringChar(DB_DELIM)
433 + rData.sCommand + OUStringChar(DB_DELIM)
434 + OUString::number(rData.nCommandType);
437 #endif
439 void SwDoc::GetAllUsedDB( std::vector<OUString>& rDBNameList,
440 const std::vector<OUString>* pAllDBNames )
442 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
443 (void) rDBNameList;
444 (void) pAllDBNames;
445 #else
446 std::vector<OUString> aUsedDBNames;
447 std::vector<OUString> aAllDBNames;
449 if( !pAllDBNames )
451 GetAllDBNames( aAllDBNames );
452 pAllDBNames = &aAllDBNames;
455 SwSectionFormats& rArr = GetSections();
456 for (auto n = rArr.size(); n; )
458 SwSection* pSect = rArr[ --n ]->GetSection();
460 if( pSect )
462 AddUsedDBToList( rDBNameList, FindUsedDBs( *pAllDBNames,
463 pSect->GetCondition(), aUsedDBNames ) );
464 aUsedDBNames.clear();
468 for (const TypedWhichId<SwFormatField> & nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
470 ForEachFormatField(nWhichHint,
471 [this, &rDBNameList, &aUsedDBNames, &pAllDBNames] (const SwFormatField& rFormatField) -> bool
473 const SwField* pField = rFormatField.GetField();
474 switch (pField->GetTyp()->Which())
476 case SwFieldIds::Database:
477 AddUsedDBToList( rDBNameList,
478 lcl_DBDataToString(static_cast<const SwDBField*>(pField)->GetDBData() ));
479 break;
481 case SwFieldIds::DbSetNumber:
482 case SwFieldIds::DatabaseName:
483 AddUsedDBToList( rDBNameList,
484 lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
485 break;
487 case SwFieldIds::DbNumSet:
488 case SwFieldIds::DbNextSet:
489 AddUsedDBToList( rDBNameList,
490 lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
491 [[fallthrough]]; // JP: is that right like that?
493 case SwFieldIds::HiddenText:
494 case SwFieldIds::HiddenPara:
495 AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
496 pField->GetPar1(), aUsedDBNames ));
497 aUsedDBNames.clear();
498 break;
500 case SwFieldIds::SetExp:
501 case SwFieldIds::GetExp:
502 case SwFieldIds::Table:
503 AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
504 pField->GetFormula(), aUsedDBNames ));
505 aUsedDBNames.clear();
506 break;
507 default: break;
509 return true;
512 #endif
515 void SwDoc::GetAllDBNames( std::vector<OUString>& rAllDBNames )
517 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
518 (void) rAllDBNames;
519 #else
520 SwDBManager* pMgr = GetDBManager();
522 const SwDSParams_t& rArr = pMgr->GetDSParamArray();
523 for (const auto& pParam : rArr)
525 rAllDBNames.emplace_back(pParam->sDataSource + OUStringChar(DB_DELIM) + pParam->sCommand);
527 #endif
530 std::vector<OUString>& SwDoc::FindUsedDBs( const std::vector<OUString>& rAllDBNames,
531 const OUString& rFormula,
532 std::vector<OUString>& rUsedDBNames )
534 const CharClass& rCC = GetAppCharClass();
535 #ifndef UNX
536 const OUString sFormula(rCC.uppercase( rFormula ));
537 #else
538 const OUString sFormula(rFormula);
539 #endif
541 for (const auto &sItem : rAllDBNames)
543 sal_Int32 nPos = sFormula.indexOf( sItem );
544 if( nPos>=0 &&
545 sFormula[ nPos + sItem.getLength() ] == '.' &&
546 (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 )))
548 // Look up table name
549 nPos += sItem.getLength() + 1;
550 const sal_Int32 nEndPos = sFormula.indexOf('.', nPos);
551 if( nEndPos>=0 )
553 rUsedDBNames.emplace_back(sItem + OUStringChar(DB_DELIM) + sFormula.subView( nPos, nEndPos - nPos ));
557 return rUsedDBNames;
560 void SwDoc::AddUsedDBToList( std::vector<OUString>& rDBNameList,
561 const std::vector<OUString>& rUsedDBNames )
563 for ( const auto &sName : rUsedDBNames )
564 AddUsedDBToList( rDBNameList, sName );
567 void SwDoc::AddUsedDBToList( std::vector<OUString>& rDBNameList, const OUString& rDBName)
569 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
570 (void) rDBNameList;
571 (void) rDBName;
572 #else
573 if( rDBName.isEmpty() )
574 return;
576 #ifdef UNX
577 for( const auto &sName : rDBNameList )
578 if( rDBName == o3tl::getToken(sName, 0, ';') )
579 return;
580 #else
581 const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
582 for( const auto &sName : rDBNameList )
583 if( rSCmp.isEqual( rDBName, sName.getToken(0, ';') ) )
584 return;
585 #endif
587 SwDBData aData;
588 sal_Int32 nIdx{ 0 };
589 aData.sDataSource = rDBName.getToken(0, DB_DELIM, nIdx);
590 aData.sCommand = rDBName.getToken(0, DB_DELIM, nIdx);
591 aData.nCommandType = -1;
592 GetDBManager()->CreateDSData(aData);
593 rDBNameList.push_back(rDBName);
594 #endif
597 void SwDoc::ChangeDBFields( const std::vector<OUString>& rOldNames,
598 const OUString& rNewName )
600 #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
601 (void) rOldNames;
602 (void) rNewName;
603 #else
604 SwDBData aNewDBData;
605 sal_Int32 nIdx{ 0 };
606 aNewDBData.sDataSource = rNewName.getToken(0, DB_DELIM, nIdx);
607 aNewDBData.sCommand = rNewName.getToken(0, DB_DELIM, nIdx);
608 aNewDBData.nCommandType = o3tl::toInt32(o3tl::getToken(rNewName, 0, DB_DELIM, nIdx));
610 SwSectionFormats& rArr = GetSections();
611 for (auto n = rArr.size(); n; )
613 SwSection* pSect = rArr[ --n ]->GetSection();
615 if( pSect )
617 pSect->SetCondition(ReplaceUsedDBs(rOldNames, rNewName, pSect->GetCondition()));
621 for (const TypedWhichId<SwFormatField> & nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
623 ForEachFormatField(nWhichHint,
624 [this, &rOldNames, &aNewDBData, &rNewName] (const SwFormatField& rFormatField) -> bool
626 const SwTextField* pTextField = rFormatField.GetTextField();
628 SwField* pField(const_cast<SwFormatField&>(rFormatField).GetField());
629 bool bExpand = false;
631 switch( pField->GetTyp()->Which() )
633 case SwFieldIds::Database:
634 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
635 if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast<SwDBField*>(pField)->GetDBData())))
637 SwDBFieldType* pOldTyp = static_cast<SwDBFieldType*>(pField->GetTyp());
639 SwDBFieldType* pTyp = static_cast<SwDBFieldType*>(getIDocumentFieldsAccess().InsertFieldType(
640 SwDBFieldType(this, pOldTyp->GetColumnName(), aNewDBData)));
642 // SwFormatField is non-shareable, so const_cast is somewhat OK
643 const_cast<SwFormatField&>(rFormatField).RegisterToFieldType( *pTyp );
644 pField->ChgTyp(pTyp);
646 static_cast<SwDBField*>(pField)->ClearInitialized();
647 static_cast<SwDBField*>(pField)->InitContent();
649 bExpand = true;
651 #endif
652 break;
654 case SwFieldIds::DbSetNumber:
655 case SwFieldIds::DatabaseName:
656 if (IsNameInArray(rOldNames,
657 lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
659 static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
660 bExpand = true;
662 break;
664 case SwFieldIds::DbNumSet:
665 case SwFieldIds::DbNextSet:
666 if (IsNameInArray(rOldNames,
667 lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
669 static_cast<SwDBNameInfField*>(pField)->SetDBData(aNewDBData);
671 [[fallthrough]];
672 case SwFieldIds::HiddenText:
673 case SwFieldIds::HiddenPara:
674 pField->SetPar1( ReplaceUsedDBs(rOldNames, rNewName, pField->GetPar1()) );
675 bExpand = true;
676 break;
678 case SwFieldIds::SetExp:
679 case SwFieldIds::GetExp:
680 case SwFieldIds::Table:
681 pField->SetPar2( ReplaceUsedDBs(rOldNames, rNewName, pField->GetFormula()) );
682 bExpand = true;
683 break;
684 default: break;
687 if (bExpand)
688 pTextField->ExpandTextField( true );
689 return true;
692 getIDocumentState().SetModified();
693 #endif
696 namespace
699 OUString lcl_CutOffDBCommandType(const OUString& rName)
701 return rName.replaceFirst(OUStringChar(DB_DELIM), ".").getToken(0, DB_DELIM);
706 OUString SwDoc::ReplaceUsedDBs( const std::vector<OUString>& rUsedDBNames,
707 const OUString& rNewName, const OUString& rFormula )
709 const CharClass& rCC = GetAppCharClass();
710 const OUString sNewName( lcl_CutOffDBCommandType(rNewName) );
711 OUString sFormula(rFormula);
713 for(const auto & rUsedDBName : rUsedDBNames)
715 const OUString sDBName( lcl_CutOffDBCommandType(rUsedDBName) );
717 if (sDBName!=sNewName)
719 sal_Int32 nPos = 0;
720 for (;;)
722 nPos = sFormula.indexOf(sDBName, nPos);
723 if (nPos<0)
725 break;
728 if( sFormula[nPos + sDBName.getLength()] == '.' &&
729 (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 )))
731 sFormula = sFormula.replaceAt(nPos, sDBName.getLength(), sNewName);
732 //prevent re-searching - this is useless and provokes
733 //endless loops when names containing each other and numbers are exchanged
734 //e.g.: old ?12345.12345 new: i12345.12345
735 nPos += sNewName.getLength();
740 return sFormula;
743 bool SwDoc::IsNameInArray( const std::vector<OUString>& rArr, const OUString& rName )
745 #ifdef UNX
746 for( const auto &sName : rArr )
747 if( rName == sName )
748 return true;
749 #else
750 const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
751 for( const auto &sName : rArr )
752 if( rSCmp.isEqual( rName, sName ))
753 return true;
754 #endif
755 return false;
758 void SwDoc::ChangeAuthorityData( const SwAuthEntry* pNewData )
760 const SwFieldTypes::size_type nSize = getIDocumentFieldsAccess().GetFieldTypes()->size();
762 for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i )
764 SwFieldType* pFieldType = (*getIDocumentFieldsAccess().GetFieldTypes())[i].get();
765 if( SwFieldIds::TableOfAuthorities == pFieldType->Which() )
767 SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(pFieldType);
768 pAuthType->ChangeEntryContent(pNewData);
769 break;
775 void SwDocUpdateField::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField )
777 const SwFieldIds nWhich = rField.GetFormatField().GetField()->GetTyp()->Which();
778 switch( nWhich )
780 case SwFieldIds::Database:
781 case SwFieldIds::SetExp:
782 case SwFieldIds::HiddenPara:
783 case SwFieldIds::HiddenText:
784 case SwFieldIds::DbNumSet:
785 case SwFieldIds::DbNextSet:
786 case SwFieldIds::DbSetNumber:
787 case SwFieldIds::GetExp:
788 break; // these have to be added/removed!
790 default:
791 return;
794 SetFieldsDirty( true );
795 if (!m_pFieldSortList)
797 if( !bIns ) // if list is present and deleted
798 return; // don't do a thing
799 m_pFieldSortList.reset(new SetGetExpFields);
802 if( bIns ) // insert anew:
803 GetBodyNode( rField, nWhich );
804 else
806 // look up via the pTextField pointer. It is a sorted list, but it's sorted by node
807 // position. Until this is found, the search for the pointer is already done.
808 SetGetExpFields::size_type n = 0;
809 while (n < m_pFieldSortList->size())
811 if (&rField == (*m_pFieldSortList)[n]->GetPointer())
813 m_pFieldSortList->erase_at(n);
814 // one field can occur multiple times
816 else
818 ++n;
824 void SwDocUpdateField::MakeFieldList( SwDoc& rDoc, bool bAll, int eGetMode )
826 if (!m_pFieldSortList || bAll
827 || ((eGetMode & m_nFieldListGetMode) != eGetMode)
828 || rDoc.GetNodes().Count() != m_nNodes)
830 MakeFieldList_( rDoc, eGetMode );
834 void SwDocUpdateField::MakeFieldList_( SwDoc& rDoc, int eGetMode )
836 // new version: walk all fields of the attribute pool
837 m_pFieldSortList.reset(new SetGetExpFields);
839 // remember sections that were unhidden and need to be hidden again
840 std::vector<std::reference_wrapper<SwSection>> aUnhiddenSections;
842 // consider and unhide sections
843 // with hide condition, only in mode GETFLD_ALL (<eGetMode == GETFLD_ALL>)
844 // notes by OD:
845 // eGetMode == GETFLD_CALC in call from methods SwDoc::FieldsToCalc
846 // eGetMode == GETFLD_EXPAND in call from method SwDoc::FieldsToExpand
847 // eGetMode == GETFLD_ALL in call from method SwDoc::UpdateExpFields
848 // I figured out that hidden section only have to be shown,
849 // if fields have updated (call by SwDoc::UpdateExpFields) and thus
850 // the hide conditions of section have to be updated.
851 // For correct updating the hide condition of a section, its position
852 // have to be known in order to insert the hide condition as a new
853 // expression field into the sorted field list (<m_pFieldSortList>).
854 if ( eGetMode == GETFLD_ALL )
855 // Collect the sections first. Supply sections that are hidden by condition
856 // with frames so that the contained fields are sorted properly.
858 // In order for the frames to be created the right way, they have to be expanded
859 // from top to bottom
860 std::vector<SwNodeOffset> aTmpArr;
861 std::vector<SwNodeOffset>::size_type nArrStt = 0;
862 SwSectionFormats& rArr = rDoc.GetSections();
863 SwSectionNode* pSectNd = nullptr;
864 SwNodeOffset nSttContent = rDoc.GetNodes().GetEndOfExtras().GetIndex();
866 for (SwSectionFormats::size_type n = rArr.size(); n; )
868 SwSection* pSect = rArr[ --n ]->GetSection();
869 if( !pSect || !pSect->IsHidden() || pSect->GetCondition().isEmpty() )
870 continue;
871 pSectNd = pSect->GetFormat()->GetSectionNode();
872 if( pSectNd )
874 SwNodeOffset nIdx = pSectNd->GetIndex();
875 aTmpArr.push_back( nIdx );
876 if( nIdx < nSttContent )
877 ++nArrStt;
880 std::sort(aTmpArr.begin(), aTmpArr.end());
882 // Display all first so that we have frames. The BodyAnchor is defined by that.
883 // First the ContentArea, then the special areas!
884 for (std::vector<sal_uLong>::size_type n = nArrStt; n < aTmpArr.size(); ++n)
886 pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode();
887 OSL_ENSURE( pSectNd, "Where is my SectionNode" );
889 auto& rSection = pSectNd->GetSection();
890 // unhide and remember the conditionally hidden sections
891 if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden())
893 aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again
894 rSection.SetCondHidden(false);
897 for (std::vector<sal_uLong>::size_type n = 0; n < nArrStt; ++n)
899 pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode();
900 OSL_ENSURE( pSectNd, "Where is my SectionNode" );
902 auto& rSection = pSectNd->GetSection();
903 // unhide and remember the conditionally hidden sections
904 if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden())
906 aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again
907 rSection.SetCondHidden(false);
911 // add all to the list so that they are sorted
912 for (const auto &nId : aTmpArr)
914 SwSectionNode const& rSectionNode(*rDoc.GetNodes()[ nId ]->GetSectionNode());
915 GetBodyNodeGeneric(rSectionNode, rSectionNode);
918 // bookmarks with hide conditions, handle similar to sections
919 auto const& rIDMA(*rDoc.getIDocumentMarkAccess());
920 for (auto it = rIDMA.getBookmarksBegin(); it != rIDMA.getBookmarksEnd(); ++it)
922 ::sw::mark::Bookmark const* const pBookmark(*it);
923 assert(pBookmark);
924 if (!pBookmark->GetHideCondition().isEmpty())
926 GetBodyNodeGeneric((*it)->GetMarkStart().GetNode(), *pBookmark);
931 static constexpr OUString sTrue(u"TRUE"_ustr);
932 static constexpr OUString sFalse(u"FALSE"_ustr);
934 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
935 bool bIsDBManager = nullptr != rDoc.GetDBManager();
936 #endif
938 for (const TypedWhichId<SwFormatField> & nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
940 rDoc.ForEachFormatField(nWhichHint,
941 [this, eGetMode, &rDoc
942 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
943 , bIsDBManager
944 #endif
945 ] (const SwFormatField& rFormatField) -> bool
947 const SwTextField* pTextField = rFormatField.GetTextField();
949 OUString sFormula;
950 const SwField* pField = rFormatField.GetField();
951 const SwFieldIds nWhich = pField->GetTyp()->Which();
952 switch (nWhich)
954 case SwFieldIds::DbSetNumber:
955 case SwFieldIds::GetExp:
956 if (GETFLD_ALL == eGetMode)
957 sFormula = sTrue;
958 break;
960 case SwFieldIds::Database:
961 if (GETFLD_EXPAND & eGetMode)
962 sFormula = sTrue;
963 break;
965 case SwFieldIds::SetExp:
966 if ((eGetMode != GETFLD_EXPAND) ||
967 (nsSwGetSetExpType::GSE_STRING & pField->GetSubType()))
969 sFormula = sTrue;
971 break;
973 case SwFieldIds::HiddenPara:
974 if (GETFLD_ALL == eGetMode)
976 sFormula = pField->GetPar1();
977 if (sFormula.isEmpty() || sFormula==sFalse)
978 const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( false );
979 else if (sFormula==sTrue)
980 const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField))->SetHidden( true );
981 else
982 break;
984 sFormula.clear();
985 // trigger formatting
986 const_cast<SwFormatField&>(rFormatField).ForceUpdateTextNode();
988 break;
990 case SwFieldIds::HiddenText:
991 if (GETFLD_ALL == eGetMode)
993 sFormula = pField->GetPar1();
994 if (sFormula.isEmpty() || sFormula==sFalse)
995 const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( true );
996 else if (sFormula==sTrue)
997 const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->SetValue( false );
998 else
999 break;
1001 sFormula.clear();
1003 // evaluate field
1004 const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField))->Evaluate(rDoc);
1005 // trigger formatting
1006 const_cast<SwFormatField&>(rFormatField).ForceUpdateTextNode();
1008 break;
1010 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
1011 case SwFieldIds::DbNumSet:
1013 SwDBData aDBData(const_cast<SwDBNumSetField*>(static_cast<const SwDBNumSetField*>(pField))->GetDBData(&rDoc));
1015 if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand))
1016 && (GETFLD_ALL == eGetMode
1017 || (GETFLD_CALC & eGetMode
1018 && static_cast<const SwDBNumSetField*>(pField)->IsCondValid()))
1021 sFormula = pField->GetPar1();
1024 break;
1025 case SwFieldIds::DbNextSet:
1027 SwDBData aDBData(const_cast<SwDBNextSetField*>(static_cast<const SwDBNextSetField*>(pField))->GetDBData(&rDoc));
1029 if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand))
1030 && (GETFLD_ALL == eGetMode
1031 || (GETFLD_CALC & eGetMode
1032 && static_cast<const SwDBNextSetField*>(pField)->IsCondValid()))
1035 sFormula = pField->GetPar1();
1038 break;
1039 #endif
1040 default: break;
1043 if (!sFormula.isEmpty())
1045 GetBodyNode( *pTextField, nWhich );
1047 return true;
1050 m_nFieldListGetMode = eGetMode;
1051 m_nNodes = rDoc.GetNodes().Count();
1053 // return the conditional hidden value back to the previous value
1054 for (auto& rSectionWrapper : aUnhiddenSections)
1056 auto& rSection = rSectionWrapper.get();
1057 rSection.SetCondHidden(true);
1061 void SwDocUpdateField::GetBodyNode( const SwTextField& rTField, SwFieldIds nFieldWhich )
1063 const SwTextNode& rTextNd = rTField.GetTextNode();
1064 const SwDoc& rDoc = rTextNd.GetDoc();
1066 // always the first! (in tab headline, header-/footer)
1067 Point aPt;
1068 std::pair<Point, bool> const tmp(aPt, false);
1069 // need pos to get the frame on the correct page
1070 SwPosition const pos(rTextNd, rTField.GetStart());
1071 const SwFrame* pFrame = rTextNd.getLayoutFrame(
1072 rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), &pos, &tmp);
1074 std::unique_ptr<SetGetExpField> pNew;
1075 bool bIsInBody = false;
1077 if( !pFrame || pFrame->IsInDocBody() )
1079 bIsInBody = rDoc.GetNodes().GetEndOfExtras().GetIndex() < rTextNd.GetIndex();
1081 // We don't want to update fields in redlines, or those
1082 // in frames whose anchor is in redline. However, we do want to update
1083 // fields in hidden sections. So: In order to be updated, a field 1)
1084 // must have a frame, or 2) it must be in the document body.
1085 if (pFrame == nullptr && bIsInBody)
1086 { // try harder to get a frame for the page number
1087 pFrame = ::sw::FindNeighbourFrameForNode(rTextNd);
1088 // possibly there is no layout at all, happens in mail merge
1090 if( (pFrame != nullptr) || bIsInBody )
1092 pNew.reset(new SetGetExpField(rTextNd, &rTField, std::nullopt,
1093 pFrame ? pFrame->GetPhyPageNum() : 0));
1096 else
1098 // create index to determine the TextNode
1099 SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
1100 bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
1101 OSL_ENSURE(bResult, "where is the Field");
1102 pNew.reset(new SetGetExpField(aPos.GetNode(), &rTField, aPos.GetContentIndex(),
1103 pFrame->GetPhyPageNum()));
1106 // always set the BodyTextFlag in GetExp or DB fields
1107 if( SwFieldIds::GetExp == nFieldWhich )
1109 SwGetExpField* pGetField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(rTField.GetFormatField().GetField()));
1110 pGetField->ChgBodyTextFlag( bIsInBody );
1112 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
1113 else if( SwFieldIds::Database == nFieldWhich )
1115 SwDBField* pDBField = const_cast<SwDBField*>(static_cast<const SwDBField*>(rTField.GetFormatField().GetField()));
1116 pDBField->ChgBodyTextFlag( bIsInBody );
1118 #endif
1119 if( pNew != nullptr )
1120 m_pFieldSortList->insert( std::move(pNew) );
1123 template<typename T>
1124 void SwDocUpdateField::GetBodyNodeGeneric(SwNode const& rNode, T const& rCond)
1126 const SwDoc& rDoc = rNode.GetDoc();
1127 std::unique_ptr<SetGetExpField> pNew;
1129 if (rNode.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex())
1131 do { // middle check loop
1133 // we need to get the anchor first
1134 // create index to determine the TextNode
1135 SwPosition aPos(rNode);
1136 SwContentNode const*const pCNd = rNode.IsSectionNode()
1137 ? SwNodes::GoNext(&aPos.nNode) // to the next ContentNode
1138 : rNode.GetContentNode();
1140 if( !pCNd || !pCNd->IsTextNode() )
1141 break;
1143 // always the first! (in tab headline, header-/footer)
1144 Point aPt;
1145 std::pair<Point, bool> const tmp(aPt, false);
1146 const SwContentFrame* pFrame = pCNd->getLayoutFrame(
1147 rDoc.getIDocumentLayoutAccess().GetCurrentLayout(),
1148 nullptr, &tmp);
1149 if( !pFrame )
1150 break;
1152 bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
1153 OSL_ENSURE(bResult, "where is the Field");
1154 pNew.reset(new SetGetExpField(rCond, &aPos, pFrame->GetPhyPageNum()));
1156 } while( false );
1159 if( !pNew )
1161 // try harder to get a frame for the page number
1162 SwFrame const*const pFrame = ::sw::FindNeighbourFrameForNode(rNode);
1163 pNew.reset(new SetGetExpField(rCond, nullptr, pFrame ? pFrame->GetPhyPageNum() : 0));
1166 m_pFieldSortList->insert( std::move(pNew) );
1169 void SwDocUpdateField::InsertFieldType( const SwFieldType& rType )
1171 OUString sFieldName;
1172 switch( rType.Which() )
1174 case SwFieldIds::User :
1175 sFieldName = static_cast<const SwUserFieldType&>(rType).GetName();
1176 break;
1177 case SwFieldIds::SetExp:
1178 sFieldName = static_cast<const SwSetExpFieldType&>(rType).GetName();
1179 break;
1180 default:
1181 OSL_ENSURE( false, "No valid field type" );
1184 if( sFieldName.isEmpty() )
1185 return;
1187 SetFieldsDirty( true );
1188 // look up and remove from the hash table
1189 sFieldName = GetAppCharClass().lowercase( sFieldName );
1191 auto it = m_FieldTypeTable.find( sFieldName );
1192 if( it == m_FieldTypeTable.end() )
1193 m_FieldTypeTable.insert( { sFieldName, &rType } );
1196 void SwDocUpdateField::RemoveFieldType( const SwFieldType& rType )
1198 OUString sFieldName;
1199 switch( rType.Which() )
1201 case SwFieldIds::User :
1202 sFieldName = static_cast<const SwUserFieldType&>(rType).GetName();
1203 break;
1204 case SwFieldIds::SetExp:
1205 sFieldName = static_cast<const SwSetExpFieldType&>(rType).GetName();
1206 break;
1207 default: break;
1210 if( sFieldName.isEmpty() )
1211 return;
1213 SetFieldsDirty( true );
1214 // look up and remove from the hash table
1215 sFieldName = GetAppCharClass().lowercase( sFieldName );
1217 GetFieldTypeTable().erase( sFieldName );
1220 SwDocUpdateField::SwDocUpdateField(SwDoc& rDoc)
1221 : m_nNodes(0)
1222 , m_nFieldListGetMode(0)
1223 , m_rDoc(rDoc)
1224 , m_bInUpdateFields(false)
1225 , m_bFieldsDirty(false)
1229 SwDocUpdateField::~SwDocUpdateField()
1233 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */