/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #ifndef UNX #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star::uno; // the StartIndex can be supplied optionally (e.g. if it was queried before - is a virtual // method otherwise!) SetGetExpField::SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextField* pField, const SwIndex* pIdx, sal_uInt16 const nPageNumber) : m_nPageNumber(nPageNumber) { m_eSetGetExpFieldType = TEXTFIELD; m_CNTNT.pTextField = pField; m_nNode = rNdIdx.GetIndex(); if( pIdx ) m_nContent = pIdx->GetIndex(); else if( pField ) m_nContent = pField->GetStart(); else m_nContent = 0; } SetGetExpField::SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextINetFormat& rINet ) { m_eSetGetExpFieldType = TEXTINET; m_CNTNT.pTextINet = &rINet; m_nNode = rNdIdx.GetIndex(); m_nContent = rINet.GetStart(); } // Extension for Sections: // these always have content position 0xffffffff! // There is never a field on this, only up to COMPLETE_STRING possible SetGetExpField::SetGetExpField( const SwSectionNode& rSectNd, const SwPosition* pPos, sal_uInt16 const nPageNumber) : m_nPageNumber(nPageNumber) { m_eSetGetExpFieldType = SECTIONNODE; m_CNTNT.pSection = &rSectNd.GetSection(); if( pPos ) { m_nNode = pPos->nNode.GetIndex(); m_nContent = pPos->nContent.GetIndex(); } else { m_nNode = rSectNd.GetIndex(); m_nContent = 0; } } SetGetExpField::SetGetExpField(::sw::mark::IBookmark const& rBookmark, SwPosition const*const pPos, sal_uInt16 const nPageNumber) : m_nPageNumber(nPageNumber) { m_eSetGetExpFieldType = BOOKMARK; m_CNTNT.pBookmark = &rBookmark; if (pPos) { m_nNode = pPos->nNode.GetIndex(); m_nContent = pPos->nContent.GetIndex(); } else { m_nNode = rBookmark.GetMarkStart().nNode.GetIndex(); m_nContent = rBookmark.GetMarkStart().nContent.GetIndex();; } } SetGetExpField::SetGetExpField( const SwTableBox& rTBox ) { m_eSetGetExpFieldType = TABLEBOX; m_CNTNT.pTBox = &rTBox; m_nNode = SwNodeOffset(0); m_nContent = 0; if( rTBox.GetSttNd() ) { SwNodeIndex aIdx( *rTBox.GetSttNd() ); const SwContentNode* pNd = aIdx.GetNode().GetNodes().GoNext( &aIdx ); if( pNd ) m_nNode = pNd->GetIndex(); } } SetGetExpField::SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextTOXMark& rTOX ) { m_eSetGetExpFieldType = TEXTTOXMARK; m_CNTNT.pTextTOX = &rTOX; m_nNode = rNdIdx.GetIndex(); m_nContent = rTOX.GetStart(); } SetGetExpField::SetGetExpField( const SwPosition& rPos ) { m_eSetGetExpFieldType = CRSRPOS; m_CNTNT.pPos = &rPos; m_nNode = rPos.nNode.GetIndex(); m_nContent = rPos.nContent.GetIndex(); } SetGetExpField::SetGetExpField( const SwFlyFrameFormat& rFlyFormat, const SwPosition* pPos ) { m_eSetGetExpFieldType = FLYFRAME; m_CNTNT.pFlyFormat = &rFlyFormat; if( pPos ) { m_nNode = pPos->nNode.GetIndex(); m_nContent = pPos->nContent.GetIndex(); } else { const SwFormatContent& rContent = rFlyFormat.GetContent(); m_nNode = rContent.GetContentIdx()->GetIndex() + 1; m_nContent = 0; } } void SetGetExpField::GetPosOfContent( SwPosition& rPos ) const { const SwNode* pNd = GetNodeFromContent(); if( pNd ) pNd = pNd->GetContentNode(); if( pNd ) { rPos.nNode = *pNd; rPos.nContent.Assign( const_cast(static_cast(pNd)), GetCntPosFromContent() ); } else { rPos.nNode = m_nNode; rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContent ); } } void SetGetExpField::SetBodyPos( const SwContentFrame& rFrame ) { if( !rFrame.IsInDocBody() ) { SwNodeIndex aIdx( rFrame.IsTextFrame() ? *static_cast(rFrame).GetTextNodeFirst() : *static_cast(rFrame).GetNode() ); SwDoc& rDoc = aIdx.GetNodes().GetDoc(); SwPosition aPos( aIdx ); bool const bResult = ::GetBodyTextNode( rDoc, aPos, rFrame ); OSL_ENSURE(bResult, "Where is the field?"); m_nNode = aPos.nNode.GetIndex(); m_nContent = aPos.nContent.GetIndex(); } } bool SetGetExpField::operator==( const SetGetExpField& rField ) const { return m_nNode == rField.m_nNode && m_nContent == rField.m_nContent && ( !m_CNTNT.pTextField || !rField.m_CNTNT.pTextField || m_CNTNT.pTextField == rField.m_CNTNT.pTextField ); } bool SetGetExpField::operator<( const SetGetExpField& rField ) const { if (m_nPageNumber != 0 && rField.m_nPageNumber != 0 && m_nPageNumber != rField.m_nPageNumber) { return m_nPageNumber < rField.m_nPageNumber; } if( m_nNode < rField.m_nNode || ( m_nNode == rField.m_nNode && m_nContent < rField.m_nContent )) return true; else if( m_nNode != rField.m_nNode || m_nContent != rField.m_nContent ) return false; const SwNode *pFirst = GetNodeFromContent(), *pNext = rField.GetNodeFromContent(); // Position is the same: continue only if both field pointers are set! if( !pFirst || !pNext ) return false; // same Section? if( pFirst->StartOfSectionNode() != pNext->StartOfSectionNode() ) { // is one in the table? const SwNode *pFirstStt, *pNextStt; const SwTableNode* pTableNd = pFirst->FindTableNode(); if( pTableNd ) pFirstStt = pTableNd->StartOfSectionNode(); else pFirstStt = pFirst->StartOfSectionNode(); pTableNd = pNext->FindTableNode(); if( pTableNd ) pNextStt = pTableNd->StartOfSectionNode(); else pNextStt = pNext->StartOfSectionNode(); if( pFirstStt != pNextStt ) { if( pFirst->IsTextNode() && pNext->IsTextNode() && ( pFirst->FindFlyStartNode() || pNext->FindFlyStartNode() )) { // FIXME: in NewFieldPortion(), SwGetExpField are expanded via // DocumentFieldsManager::FieldsToExpand() calling // std::upper_bound binary search function - the sort order // depends on the fly positions in the layout, but the fly // positions depend on the expansion of the SwGetExpField! // This circular dep will cause trouble, it would be better to // use only model positions (anchor), but then how to compare // at-page anchored flys which don't have a model anchor? return ::IsFrameBehind( *pNext->GetTextNode(), m_nContent, *pFirst->GetTextNode(), m_nContent ); } return pFirstStt->GetIndex() < pNextStt->GetIndex(); } } // same Section: is the field in the same Node? if( pFirst != pNext ) return pFirst->GetIndex() < pNext->GetIndex(); // same Node in the Section, check Position in the Node return GetCntPosFromContent() < rField.GetCntPosFromContent(); } const SwNode* SetGetExpField::GetNodeFromContent() const { const SwNode* pRet = nullptr; if( m_CNTNT.pTextField ) switch( m_eSetGetExpFieldType ) { case TEXTFIELD: pRet = &m_CNTNT.pTextField->GetTextNode(); break; case TEXTINET: pRet = &m_CNTNT.pTextINet->GetTextNode(); break; case SECTIONNODE: pRet = m_CNTNT.pSection->GetFormat()->GetSectionNode(); break; case BOOKMARK: pRet = &m_CNTNT.pBookmark->GetMarkStart().nNode.GetNode(); break; case CRSRPOS: pRet = &m_CNTNT.pPos->nNode.GetNode(); break; case TEXTTOXMARK: pRet = &m_CNTNT.pTextTOX->GetTextNode(); break; case TABLEBOX: if( m_CNTNT.pTBox->GetSttNd() ) { SwNodeIndex aIdx( *m_CNTNT.pTBox->GetSttNd() ); pRet = aIdx.GetNode().GetNodes().GoNext( &aIdx ); } break; case FLYFRAME: { SwNodeIndex aIdx( *m_CNTNT.pFlyFormat->GetContent().GetContentIdx() ); pRet = aIdx.GetNode().GetNodes().GoNext( &aIdx ); } break; } return pRet; } sal_Int32 SetGetExpField::GetCntPosFromContent() const { sal_Int32 nRet = 0; if( m_CNTNT.pTextField ) switch( m_eSetGetExpFieldType ) { case TEXTFIELD: nRet = m_CNTNT.pTextField->GetStart(); break; case TEXTINET: nRet = m_CNTNT.pTextINet->GetStart(); break; case TEXTTOXMARK: nRet = m_CNTNT.pTextTOX->GetStart(); break; case BOOKMARK: nRet = m_CNTNT.pBookmark->GetMarkStart().nContent.GetIndex(); break; case CRSRPOS: nRet = m_CNTNT.pPos->nContent.GetIndex(); break; default: break; } return nRet; } HashStr::HashStr( const OUString& rName, const OUString& rText, HashStr* pNxt ) : SwHash( rName ), aSetStr( rText ) { pNext.reset( pNxt ); } /// Look up the Name, if it is present, return its String, otherwise return an empty String OUString LookString( SwHashTable const & rTable, const OUString& rName ) { HashStr* pFnd = rTable.Find( comphelper::string::strip(rName, ' ') ); if( pFnd ) return pFnd->aSetStr; return OUString(); } SwDBData const & SwDoc::GetDBData() { #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS if(maDBData.sDataSource.isEmpty()) { // Similar to: SwEditShell::IsAnyDatabaseFieldInDoc for (const auto& pFieldType : *getIDocumentFieldsAccess().GetFieldTypes()) { if (IsUsed(*pFieldType)) { SwFieldIds nWhich = pFieldType->Which(); switch(nWhich) { case SwFieldIds::Database: case SwFieldIds::DbNextSet: case SwFieldIds::DbNumSet: case SwFieldIds::DbSetNumber: { std::vector vFields; pFieldType->GatherFields(vFields); if(vFields.size()) { if(SwFieldIds::Database == nWhich) maDBData = static_cast(vFields.front()->GetField()->GetTyp())->GetDBData(); else maDBData = static_cast (vFields.front()->GetField())->GetRealDBData(); } } break; default: break; } } } } if(maDBData.sDataSource.isEmpty()) maDBData = SwDBManager::GetAddressDBName(); #endif return maDBData; } void SwDoc::SetInitDBFields( bool b ) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) b; #else GetDBManager()->SetInitDBFields( b ); #endif } #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS /// Get all databases that are used by fields static OUString lcl_DBDataToString(const SwDBData& rData) { return rData.sDataSource + OUStringChar(DB_DELIM) + rData.sCommand + OUStringChar(DB_DELIM) + OUString::number(rData.nCommandType); } #endif void SwDoc::GetAllUsedDB( std::vector& rDBNameList, const std::vector* pAllDBNames ) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) rDBNameList; (void) pAllDBNames; #else std::vector aUsedDBNames; std::vector aAllDBNames; if( !pAllDBNames ) { GetAllDBNames( aAllDBNames ); pAllDBNames = &aAllDBNames; } SwSectionFormats& rArr = GetSections(); for (auto n = rArr.size(); n; ) { SwSection* pSect = rArr[ --n ]->GetSection(); if( pSect ) { AddUsedDBToList( rDBNameList, FindUsedDBs( *pAllDBNames, pSect->GetCondition(), aUsedDBNames ) ); aUsedDBNames.clear(); } } for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD }) { for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(nWhichHint)) { const SwFormatField* pFormatField = static_cast(pItem); const SwTextField* pTextField = pFormatField->GetTextField(); if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes()) continue; const SwField* pField = pFormatField->GetField(); switch (pField->GetTyp()->Which()) { case SwFieldIds::Database: AddUsedDBToList( rDBNameList, lcl_DBDataToString(static_cast(pField)->GetDBData() )); break; case SwFieldIds::DbSetNumber: case SwFieldIds::DatabaseName: AddUsedDBToList( rDBNameList, lcl_DBDataToString(static_cast(pField)->GetRealDBData() )); break; case SwFieldIds::DbNumSet: case SwFieldIds::DbNextSet: AddUsedDBToList( rDBNameList, lcl_DBDataToString(static_cast(pField)->GetRealDBData() )); [[fallthrough]]; // JP: is that right like that? case SwFieldIds::HiddenText: case SwFieldIds::HiddenPara: AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames, pField->GetPar1(), aUsedDBNames )); aUsedDBNames.clear(); break; case SwFieldIds::SetExp: case SwFieldIds::GetExp: case SwFieldIds::Table: AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames, pField->GetFormula(), aUsedDBNames )); aUsedDBNames.clear(); break; default: break; } } } #endif } void SwDoc::GetAllDBNames( std::vector& rAllDBNames ) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) rAllDBNames; #else SwDBManager* pMgr = GetDBManager(); const SwDSParams_t& rArr = pMgr->GetDSParamArray(); for (const auto& pParam : rArr) { rAllDBNames.emplace_back(pParam->sDataSource + OUStringChar(DB_DELIM) + pParam->sCommand); } #endif } std::vector& SwDoc::FindUsedDBs( const std::vector& rAllDBNames, const OUString& rFormula, std::vector& rUsedDBNames ) { const CharClass& rCC = GetAppCharClass(); #ifndef UNX const OUString sFormula(rCC.uppercase( rFormula )); #else const OUString sFormula(rFormula); #endif for (const auto &sItem : rAllDBNames) { sal_Int32 nPos = sFormula.indexOf( sItem ); if( nPos>=0 && sFormula[ nPos + sItem.getLength() ] == '.' && (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 ))) { // Look up table name nPos += sItem.getLength() + 1; const sal_Int32 nEndPos = sFormula.indexOf('.', nPos); if( nEndPos>=0 ) { rUsedDBNames.emplace_back(sItem + OUStringChar(DB_DELIM) + sFormula.subView( nPos, nEndPos - nPos )); } } } return rUsedDBNames; } void SwDoc::AddUsedDBToList( std::vector& rDBNameList, const std::vector& rUsedDBNames ) { for ( const auto &sName : rUsedDBNames ) AddUsedDBToList( rDBNameList, sName ); } void SwDoc::AddUsedDBToList( std::vector& rDBNameList, const OUString& rDBName) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) rDBNameList; (void) rDBName; #else if( rDBName.isEmpty() ) return; #ifdef UNX for( const auto &sName : rDBNameList ) if( rDBName == o3tl::getToken(sName, 0, ';') ) return; #else const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); for( const auto &sName : rDBNameList ) if( rSCmp.isEqual( rDBName, sName.getToken(0, ';') ) ) return; #endif SwDBData aData; sal_Int32 nIdx{ 0 }; aData.sDataSource = rDBName.getToken(0, DB_DELIM, nIdx); aData.sCommand = rDBName.getToken(0, DB_DELIM, nIdx); aData.nCommandType = -1; GetDBManager()->CreateDSData(aData); rDBNameList.push_back(rDBName); #endif } void SwDoc::ChangeDBFields( const std::vector& rOldNames, const OUString& rNewName ) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) rOldNames; (void) rNewName; #else SwDBData aNewDBData; sal_Int32 nIdx{ 0 }; aNewDBData.sDataSource = rNewName.getToken(0, DB_DELIM, nIdx); aNewDBData.sCommand = rNewName.getToken(0, DB_DELIM, nIdx); aNewDBData.nCommandType = o3tl::toInt32(o3tl::getToken(rNewName, 0, DB_DELIM, nIdx)); SwSectionFormats& rArr = GetSections(); for (auto n = rArr.size(); n; ) { SwSection* pSect = rArr[ --n ]->GetSection(); if( pSect ) { pSect->SetCondition(ReplaceUsedDBs(rOldNames, rNewName, pSect->GetCondition())); } } for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD }) { for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(nWhichHint)) { SwFormatField* pFormatField = const_cast(static_cast(pItem)); SwTextField* pTextField = pFormatField->GetTextField(); if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes()) continue; SwField* pField = pFormatField->GetField(); bool bExpand = false; switch( pField->GetTyp()->Which() ) { case SwFieldIds::Database: #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast(pField)->GetDBData()))) { SwDBFieldType* pOldTyp = static_cast(pField->GetTyp()); SwDBFieldType* pTyp = static_cast(getIDocumentFieldsAccess().InsertFieldType( SwDBFieldType(this, pOldTyp->GetColumnName(), aNewDBData))); pFormatField->RegisterToFieldType( *pTyp ); pField->ChgTyp(pTyp); static_cast(pField)->ClearInitialized(); static_cast(pField)->InitContent(); bExpand = true; } #endif break; case SwFieldIds::DbSetNumber: case SwFieldIds::DatabaseName: if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast(pField)->GetRealDBData()))) { static_cast(pField)->SetDBData(aNewDBData); bExpand = true; } break; case SwFieldIds::DbNumSet: case SwFieldIds::DbNextSet: if (IsNameInArray(rOldNames, lcl_DBDataToString(static_cast(pField)->GetRealDBData()))) { static_cast(pField)->SetDBData(aNewDBData); } [[fallthrough]]; case SwFieldIds::HiddenText: case SwFieldIds::HiddenPara: pField->SetPar1( ReplaceUsedDBs(rOldNames, rNewName, pField->GetPar1()) ); bExpand = true; break; case SwFieldIds::SetExp: case SwFieldIds::GetExp: case SwFieldIds::Table: pField->SetPar2( ReplaceUsedDBs(rOldNames, rNewName, pField->GetFormula()) ); bExpand = true; break; default: break; } if (bExpand) pTextField->ExpandTextField( true ); } } getIDocumentState().SetModified(); #endif } namespace { OUString lcl_CutOffDBCommandType(const OUString& rName) { return rName.replaceFirst(OUStringChar(DB_DELIM), ".").getToken(0, DB_DELIM); } } OUString SwDoc::ReplaceUsedDBs( const std::vector& rUsedDBNames, const OUString& rNewName, const OUString& rFormula ) { const CharClass& rCC = GetAppCharClass(); const OUString sNewName( lcl_CutOffDBCommandType(rNewName) ); OUString sFormula(rFormula); for(const auto & rUsedDBName : rUsedDBNames) { const OUString sDBName( lcl_CutOffDBCommandType(rUsedDBName) ); if (sDBName!=sNewName) { sal_Int32 nPos = 0; for (;;) { nPos = sFormula.indexOf(sDBName, nPos); if (nPos<0) { break; } if( sFormula[nPos + sDBName.getLength()] == '.' && (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 ))) { sFormula = sFormula.replaceAt(nPos, sDBName.getLength(), sNewName); //prevent re-searching - this is useless and provokes //endless loops when names containing each other and numbers are exchanged //e.g.: old ?12345.12345 new: i12345.12345 nPos += sNewName.getLength(); } } } } return sFormula; } bool SwDoc::IsNameInArray( const std::vector& rArr, const OUString& rName ) { #ifdef UNX for( const auto &sName : rArr ) if( rName == sName ) return true; #else const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); for( const auto &sName : rArr ) if( rSCmp.isEqual( rName, sName )) return true; #endif return false; } void SwDoc::ChangeAuthorityData( const SwAuthEntry* pNewData ) { const SwFieldTypes::size_type nSize = getIDocumentFieldsAccess().GetFieldTypes()->size(); for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i ) { SwFieldType* pFieldType = (*getIDocumentFieldsAccess().GetFieldTypes())[i].get(); if( SwFieldIds::TableOfAuthorities == pFieldType->Which() ) { SwAuthorityFieldType* pAuthType = static_cast(pFieldType); pAuthType->ChangeEntryContent(pNewData); break; } } } void SwDocUpdateField::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField ) { const SwFieldIds nWhich = rField.GetFormatField().GetField()->GetTyp()->Which(); switch( nWhich ) { case SwFieldIds::Database: case SwFieldIds::SetExp: case SwFieldIds::HiddenPara: case SwFieldIds::HiddenText: case SwFieldIds::DbNumSet: case SwFieldIds::DbNextSet: case SwFieldIds::DbSetNumber: case SwFieldIds::GetExp: break; // these have to be added/removed! default: return; } SetFieldsDirty( true ); if (!m_pFieldSortList) { if( !bIns ) // if list is present and deleted return; // don't do a thing m_pFieldSortList.reset(new SetGetExpFields); } if( bIns ) // insert anew: GetBodyNode( rField, nWhich ); else { // look up via the pTextField pointer. It is a sorted list, but it's sorted by node // position. Until this is found, the search for the pointer is already done. for (SetGetExpFields::size_type n = 0; n < m_pFieldSortList->size(); ++n) { if (&rField == (*m_pFieldSortList)[n]->GetPointer()) { m_pFieldSortList->erase_at(n); n--; // one field can occur multiple times } } } } void SwDocUpdateField::MakeFieldList( SwDoc& rDoc, bool bAll, int eGetMode ) { if (!m_pFieldSortList || bAll || ((eGetMode & m_nFieldListGetMode) != eGetMode) || rDoc.GetNodes().Count() != m_nNodes) { MakeFieldList_( rDoc, eGetMode ); } } void SwDocUpdateField::MakeFieldList_( SwDoc& rDoc, int eGetMode ) { // new version: walk all fields of the attribute pool m_pFieldSortList.reset(new SetGetExpFields); // remembeer sections that were unhidden and need to be hidden again std::vector> aUnhiddenSections; // consider and unhide sections // with hide condition, only in mode GETFLD_ALL () // notes by OD: // eGetMode == GETFLD_CALC in call from methods SwDoc::FieldsToCalc // eGetMode == GETFLD_EXPAND in call from method SwDoc::FieldsToExpand // eGetMode == GETFLD_ALL in call from method SwDoc::UpdateExpFields // I figured out that hidden section only have to be shown, // if fields have updated (call by SwDoc::UpdateExpFields) and thus // the hide conditions of section have to be updated. // For correct updating the hide condition of a section, its position // have to be known in order to insert the hide condition as a new // expression field into the sorted field list (). if ( eGetMode == GETFLD_ALL ) // Collect the sections first. Supply sections that are hidden by condition // with frames so that the contained fields are sorted properly. { // In order for the frames to be created the right way, they have to be expanded // from top to bottom std::vector aTmpArr; std::vector::size_type nArrStt = 0; SwSectionFormats& rArr = rDoc.GetSections(); SwSectionNode* pSectNd = nullptr; SwNodeOffset nSttContent = rDoc.GetNodes().GetEndOfExtras().GetIndex(); for (SwSectionFormats::size_type n = rArr.size(); n; ) { SwSection* pSect = rArr[ --n ]->GetSection(); if( !pSect || !pSect->IsHidden() || pSect->GetCondition().isEmpty() ) continue; pSectNd = pSect->GetFormat()->GetSectionNode(); if( pSectNd ) { SwNodeOffset nIdx = pSectNd->GetIndex(); aTmpArr.push_back( nIdx ); if( nIdx < nSttContent ) ++nArrStt; } } std::sort(aTmpArr.begin(), aTmpArr.end()); // Display all first so that we have frames. The BodyAnchor is defined by that. // First the ContentArea, then the special areas! for (std::vector::size_type n = nArrStt; n < aTmpArr.size(); ++n) { pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode(); OSL_ENSURE( pSectNd, "Where is my SectionNode" ); auto& rSection = pSectNd->GetSection(); // unhide and remember the conditionally hidden sections if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden()) { aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again rSection.SetCondHidden(false); } } for (std::vector::size_type n = 0; n < nArrStt; ++n) { pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode(); OSL_ENSURE( pSectNd, "Where is my SectionNode" ); auto& rSection = pSectNd->GetSection(); // unhide and remember the conditionally hidden sections if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden()) { aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again rSection.SetCondHidden(false); } } // add all to the list so that they are sorted for (const auto &nId : aTmpArr) { SwSectionNode const& rSectionNode(*rDoc.GetNodes()[ nId ]->GetSectionNode()); GetBodyNodeGeneric(rSectionNode, rSectionNode); } // bookmarks with hide conditions, handle similar to sections auto const& rIDMA(*rDoc.getIDocumentMarkAccess()); for (auto it = rIDMA.getBookmarksBegin(); it != rIDMA.getBookmarksEnd(); ++it) { auto const pBookmark(dynamic_cast<::sw::mark::IBookmark const*>(*it)); assert(pBookmark); if (!pBookmark->GetHideCondition().isEmpty()) { GetBodyNodeGeneric((*it)->GetMarkStart().nNode.GetNode(), *pBookmark); } } } static const OUStringLiteral sTrue(u"TRUE"); static const OUStringLiteral sFalse(u"FALSE"); #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS bool bIsDBManager = nullptr != rDoc.GetDBManager(); #endif for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD }) { for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(nWhichHint)) { const SwFormatField* pFormatField = static_cast(pItem); const SwTextField* pTextField = pFormatField->GetTextField(); if (!pTextField || !pTextField->GetTextNode().GetNodes().IsDocNodes()) continue; OUString sFormula; const SwField* pField = pFormatField->GetField(); const SwFieldIds nWhich = pField->GetTyp()->Which(); switch (nWhich) { case SwFieldIds::DbSetNumber: case SwFieldIds::GetExp: if (GETFLD_ALL == eGetMode) sFormula = sTrue; break; case SwFieldIds::Database: if (GETFLD_EXPAND & eGetMode) sFormula = sTrue; break; case SwFieldIds::SetExp: if ((eGetMode != GETFLD_EXPAND) || (nsSwGetSetExpType::GSE_STRING & pField->GetSubType())) { sFormula = sTrue; } break; case SwFieldIds::HiddenPara: if (GETFLD_ALL == eGetMode) { sFormula = pField->GetPar1(); if (sFormula.isEmpty() || sFormula==sFalse) const_cast(static_cast(pField))->SetHidden( false ); else if (sFormula==sTrue) const_cast(static_cast(pField))->SetHidden( true ); else break; sFormula.clear(); // trigger formatting const_cast(pFormatField)->UpdateTextNode( nullptr, nullptr ); } break; case SwFieldIds::HiddenText: if (GETFLD_ALL == eGetMode) { sFormula = pField->GetPar1(); if (sFormula.isEmpty() || sFormula==sFalse) const_cast(static_cast(pField))->SetValue( true ); else if (sFormula==sTrue) const_cast(static_cast(pField))->SetValue( false ); else break; sFormula.clear(); // evaluate field const_cast(static_cast(pField))->Evaluate(rDoc); // trigger formatting const_cast(pFormatField)->UpdateTextNode(nullptr, nullptr); } break; #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS case SwFieldIds::DbNumSet: { SwDBData aDBData(const_cast(static_cast(pField))->GetDBData(&rDoc)); if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand)) && (GETFLD_ALL == eGetMode || (GETFLD_CALC & eGetMode && static_cast(pField)->IsCondValid())) ) { sFormula = pField->GetPar1(); } } break; case SwFieldIds::DbNextSet: { SwDBData aDBData(const_cast(static_cast(pField))->GetDBData(&rDoc)); if ( (bIsDBManager && rDoc.GetDBManager()->OpenDataSource(aDBData.sDataSource, aDBData.sCommand)) && (GETFLD_ALL == eGetMode || (GETFLD_CALC & eGetMode && static_cast(pField)->IsCondValid())) ) { sFormula = pField->GetPar1(); } } break; #endif default: break; } if (!sFormula.isEmpty()) { GetBodyNode( *pTextField, nWhich ); } } } m_nFieldListGetMode = eGetMode; m_nNodes = rDoc.GetNodes().Count(); // return the conditional hidden value back to the previous value for (auto& rSectionWrapper : aUnhiddenSections) { auto& rSection = rSectionWrapper.get(); rSection.SetCondHidden(true); } } void SwDocUpdateField::GetBodyNode( const SwTextField& rTField, SwFieldIds nFieldWhich ) { const SwTextNode& rTextNd = rTField.GetTextNode(); const SwDoc& rDoc = rTextNd.GetDoc(); // always the first! (in tab headline, header-/footer) Point aPt; std::pair const tmp(aPt, false); // need pos to get the frame on the correct page SwPosition const pos(const_cast(rTextNd), rTField.GetStart()); const SwFrame* pFrame = rTextNd.getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), &pos, &tmp); std::unique_ptr pNew; bool bIsInBody = false; if( !pFrame || pFrame->IsInDocBody() ) { // create index to determine the TextNode SwNodeIndex aIdx( rTextNd ); bIsInBody = rDoc.GetNodes().GetEndOfExtras().GetIndex() < aIdx.GetIndex(); // We don't want to update fields in redlines, or those // in frames whose anchor is in redline. However, we do want to update // fields in hidden sections. So: In order to be updated, a field 1) // must have a frame, or 2) it must be in the document body. if (pFrame == nullptr && bIsInBody) { // try harder to get a frame for the page number pFrame = ::sw::FindNeighbourFrameForNode(rTextNd); // possibly there is no layout at all, happens in mail merge } if( (pFrame != nullptr) || bIsInBody ) { pNew.reset(new SetGetExpField(aIdx, &rTField, nullptr, pFrame ? pFrame->GetPhyPageNum() : 0)); } } else { // create index to determine the TextNode SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() ); bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame ); OSL_ENSURE(bResult, "where is the Field"); pNew.reset(new SetGetExpField(aPos.nNode, &rTField, &aPos.nContent, pFrame->GetPhyPageNum())); } // always set the BodyTextFlag in GetExp or DB fields if( SwFieldIds::GetExp == nFieldWhich ) { SwGetExpField* pGetField = const_cast(static_cast(rTField.GetFormatField().GetField())); pGetField->ChgBodyTextFlag( bIsInBody ); } #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS else if( SwFieldIds::Database == nFieldWhich ) { SwDBField* pDBField = const_cast(static_cast(rTField.GetFormatField().GetField())); pDBField->ChgBodyTextFlag( bIsInBody ); } #endif if( pNew != nullptr ) m_pFieldSortList->insert( std::move(pNew) ); } template void SwDocUpdateField::GetBodyNodeGeneric(SwNode const& rNode, T const& rCond) { const SwDoc& rDoc = rNode.GetDoc(); std::unique_ptr pNew; if (rNode.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex()) { do { // middle check loop // we need to get the anchor first // create index to determine the TextNode SwPosition aPos(rNode); SwContentNode const*const pCNd = rNode.IsSectionNode() ? rDoc.GetNodes().GoNext(&aPos.nNode) // to the next ContentNode : rNode.GetContentNode(); if( !pCNd || !pCNd->IsTextNode() ) break; // always the first! (in tab headline, header-/footer) Point aPt; std::pair const tmp(aPt, false); const SwContentFrame* pFrame = pCNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp); if( !pFrame ) break; bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame ); OSL_ENSURE(bResult, "where is the Field"); pNew.reset(new SetGetExpField(rCond, &aPos, pFrame->GetPhyPageNum())); } while( false ); } if( !pNew ) { // try harder to get a frame for the page number SwFrame const*const pFrame = ::sw::FindNeighbourFrameForNode(rNode); pNew.reset(new SetGetExpField(rCond, nullptr, pFrame ? pFrame->GetPhyPageNum() : 0)); } m_pFieldSortList->insert( std::move(pNew) ); } void SwDocUpdateField::InsertFieldType( const SwFieldType& rType ) { OUString sFieldName; switch( rType.Which() ) { case SwFieldIds::User : sFieldName = static_cast(rType).GetName(); break; case SwFieldIds::SetExp: sFieldName = static_cast(rType).GetName(); break; default: OSL_ENSURE( false, "No valid field type" ); } if( sFieldName.isEmpty() ) return; SetFieldsDirty( true ); // look up and remove from the hash table sFieldName = GetAppCharClass().lowercase( sFieldName ); sal_uInt16 n; SwCalcFieldType* pFnd = GetFieldTypeTable().Find( sFieldName, &n ); if( !pFnd ) { SwCalcFieldType* pNew = new SwCalcFieldType( sFieldName, &rType ); pNew->pNext.reset( m_FieldTypeTable[n].release() ); m_FieldTypeTable[n].reset(pNew); } } void SwDocUpdateField::RemoveFieldType( const SwFieldType& rType ) { OUString sFieldName; switch( rType.Which() ) { case SwFieldIds::User : sFieldName = static_cast(rType).GetName(); break; case SwFieldIds::SetExp: sFieldName = static_cast(rType).GetName(); break; default: break; } if( sFieldName.isEmpty() ) return; SetFieldsDirty( true ); // look up and remove from the hash table sFieldName = GetAppCharClass().lowercase( sFieldName ); sal_uInt16 n; SwCalcFieldType* pFnd = GetFieldTypeTable().Find( sFieldName, &n ); if( !pFnd ) return; if (m_FieldTypeTable[n].get() == pFnd) { m_FieldTypeTable[n].reset(static_cast(pFnd->pNext.release())); } else { SwHash* pPrev = m_FieldTypeTable[n].get(); while( pPrev->pNext.get() != pFnd ) pPrev = pPrev->pNext.get(); pPrev->pNext = std::move(pFnd->pNext); // no need to explicitly delete here, the embedded linked list uses unique_ptr } } SwDocUpdateField::SwDocUpdateField(SwDoc& rDoc) : m_FieldTypeTable(TBLSZ) , m_nNodes(0) , m_nFieldListGetMode(0) , m_rDoc(rDoc) , m_bInUpdateFields(false) , m_bFieldsDirty(false) { } SwDocUpdateField::~SwDocUpdateField() { } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */