/* -*- 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 #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 #include #include #include #include #include #include #include using namespace ::com::sun::star::uno; namespace sw { bool IsFieldDeletedInModel(IDocumentRedlineAccess const& rIDRA, SwTextField const& rTextField) { SwRedlineTable::size_type tmp; SwPosition const pos(rTextField.GetTextNode(), rTextField.GetStart()); SwRangeRedline const*const pRedline(rIDRA.GetRedline(pos, &tmp)); return (pRedline && pRedline->GetType() == RedlineType::Delete && *pRedline->GetPoint() != *pRedline->GetMark()); } } namespace { #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS OUString lcl_GetDBVarName( SwDoc& rDoc, SwDBNameInfField& rDBField ) { SwDBData aDBData( rDBField.GetDBData( &rDoc )); OUString sDBNumNm; SwDBData aDocData = rDoc.GetDBData(); if( aDBData != aDocData ) { sDBNumNm = aDBData.sDataSource + OUStringChar(DB_DELIM) + aDBData.sCommand + OUStringChar(DB_DELIM); } sDBNumNm += SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber); return sDBNumNm; } #endif bool IsFieldDeleted(IDocumentRedlineAccess const& rIDRA, SwRootFrame const& rLayout, SwTextField const& rTextField) { SwTextNode const& rNode(rTextField.GetTextNode()); bool const isInBody( rNode.GetNodes().GetEndOfExtras().GetIndex() < rNode.GetIndex()); if (!isInBody && nullptr == rNode.getLayoutFrame(&rLayout)) { // see SwDocUpdateField::GetBodyNode() - fields in hidden sections // don't have layout frames but must be updated, so use the same // check as there, but do it again because GetBodyNode() checks // for *any* layout... return true; } return sw::IsFieldDeletedInModel(rIDRA, rTextField); } void lcl_CalcField( SwDoc& rDoc, SwCalc& rCalc, const SetGetExpField& rSGEField, SwDBManager* pMgr, SwRootFrame const*const pLayout) { const SwTextField* pTextField = rSGEField.GetTextField(); if( !pTextField ) return ; if (pLayout && pLayout->IsHideRedlines() && IsFieldDeleted(rDoc.getIDocumentRedlineAccess(), *pLayout, *pTextField)) { return; } const SwField* pField = pTextField->GetFormatField().GetField(); const SwFieldIds nFieldWhich = pField->GetTyp()->Which(); if( SwFieldIds::SetExp == nFieldWhich ) { SwSbxValue aValue; if( nsSwGetSetExpType::GSE_EXPR & pField->GetSubType() ) aValue.PutDouble( static_cast(pField)->GetValue(pLayout) ); else // Extension to calculate with Strings aValue.PutString( static_cast(pField)->GetExpStr(pLayout) ); // set the new value in Calculator rCalc.VarChange( pField->GetTyp()->GetName(), aValue ); } else if( pMgr ) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) rDoc; #else switch( nFieldWhich ) { case SwFieldIds::DbNumSet: { SwDBNumSetField* pDBField = const_cast(static_cast(pField)); SwDBData aDBData(pDBField->GetDBData(&rDoc)); if( pDBField->IsCondValid() && pMgr->OpenDataSource( aDBData.sDataSource, aDBData.sCommand )) rCalc.VarChange( lcl_GetDBVarName( rDoc, *pDBField), pDBField->GetFormat() ); } break; case SwFieldIds::DbNextSet: { SwDBNextSetField* pDBField = const_cast(static_cast(pField)); SwDBData aDBData(pDBField->GetDBData(&rDoc)); if( !pDBField->IsCondValid() || !pMgr->OpenDataSource( aDBData.sDataSource, aDBData.sCommand )) break; OUString sDBNumNm(lcl_GetDBVarName( rDoc, *pDBField)); SwCalcExp* pExp = rCalc.VarLook( sDBNumNm ); if( pExp ) rCalc.VarChange( sDBNumNm, pExp->nValue.GetLong() + 1 ); } break; default: break; } #endif } } } namespace sw { DocumentFieldsManager::DocumentFieldsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ), mbNewFieldLst(true), mpUpdateFields(new SwDocUpdateField(m_rDoc)), mpFieldTypes( new SwFieldTypes ), mnLockExpField( 0 ) { } const SwFieldTypes* DocumentFieldsManager::GetFieldTypes() const { return mpFieldTypes.get(); } /** Insert field types * * @param rFieldTyp ??? * @return Always returns a pointer to the type, if it's new or already added. */ SwFieldType* DocumentFieldsManager::InsertFieldType(const SwFieldType &rFieldTyp) { const SwFieldTypes::size_type nSize = mpFieldTypes->size(); const SwFieldIds nFieldWhich = rFieldTyp.Which(); SwFieldTypes::size_type i = INIT_FLDTYPES; switch( nFieldWhich ) { case SwFieldIds::SetExp: //JP 29.01.96: SequenceFields start at INIT_FLDTYPES - 3!! // Or we get doubble number circles!! //MIB 14.03.95: From now on also the SW3-Reader relies on this, when //constructing string pools and when reading SetExp fields if( nsSwGetSetExpType::GSE_SEQ & static_cast(rFieldTyp).GetType() ) i -= INIT_SEQ_FLDTYPES; [[fallthrough]]; case SwFieldIds::Database: case SwFieldIds::User: case SwFieldIds::Dde: { const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); OUString sFieldNm( rFieldTyp.GetName() ); for( ; i < nSize; ++i ) if( nFieldWhich == (*mpFieldTypes)[i]->Which() && rSCmp.isEqual( sFieldNm, (*mpFieldTypes)[i]->GetName() )) return (*mpFieldTypes)[i].get(); } break; case SwFieldIds::TableOfAuthorities: for( ; i < nSize; ++i ) if( nFieldWhich == (*mpFieldTypes)[i]->Which() ) return (*mpFieldTypes)[i].get(); break; default: for( i = 0; i < nSize; ++i ) if( nFieldWhich == (*mpFieldTypes)[i]->Which() ) return (*mpFieldTypes)[i].get(); } std::unique_ptr pNew = rFieldTyp.Copy(); switch( nFieldWhich ) { case SwFieldIds::Dde: static_cast(pNew.get())->SetDoc( &m_rDoc ); break; case SwFieldIds::Database: case SwFieldIds::Table: case SwFieldIds::DateTime: case SwFieldIds::GetExp: static_cast(pNew.get())->SetDoc( &m_rDoc ); break; case SwFieldIds::User: case SwFieldIds::SetExp: static_cast(pNew.get())->SetDoc( &m_rDoc ); // JP 29.07.96: Optionally prepare FieldList for Calculator: mpUpdateFields->InsertFieldType( *pNew ); break; case SwFieldIds::TableOfAuthorities : static_cast(pNew.get())->SetDoc( &m_rDoc ); break; default: break; } mpFieldTypes->insert( mpFieldTypes->begin() + nSize, std::move(pNew) ); m_rDoc.getIDocumentState().SetModified(); return (*mpFieldTypes)[ nSize ].get(); } /// @returns the field type of the Doc SwFieldType *DocumentFieldsManager::GetSysFieldType( const SwFieldIds eWhich ) const { for( SwFieldTypes::size_type i = 0; i < INIT_FLDTYPES; ++i ) if( eWhich == (*mpFieldTypes)[i]->Which() ) return (*mpFieldTypes)[i].get(); return nullptr; } /// Find first type with ResId and name SwFieldType* DocumentFieldsManager::GetFieldType( SwFieldIds nResId, const OUString& rName, bool bDbFieldMatching // used in some UNO calls for SwFieldIds::Database to use different string matching code #i51815# ) const { const SwFieldTypes::size_type nSize = mpFieldTypes->size(); SwFieldTypes::size_type i {0}; const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); switch( nResId ) { case SwFieldIds::SetExp: //JP 29.01.96: SequenceFields start at INIT_FLDTYPES - 3!! // Or we get doubble number circles!! //MIB 14.03.95: From now on also the SW3-Reader relies on this, when //constructing string pools and when reading SetExp fields i = INIT_FLDTYPES - INIT_SEQ_FLDTYPES; break; case SwFieldIds::Database: case SwFieldIds::User: case SwFieldIds::Dde: case SwFieldIds::TableOfAuthorities: i = INIT_FLDTYPES; break; default: break; } SwFieldType* pRet = nullptr; for( ; i < nSize; ++i ) { SwFieldType* pFieldType = (*mpFieldTypes)[i].get(); if (nResId == pFieldType->Which()) { OUString aFieldName( pFieldType->GetName() ); if (bDbFieldMatching && nResId == SwFieldIds::Database) // #i51815# aFieldName = aFieldName.replace(DB_DELIM, '.'); if (rSCmp.isEqual( rName, aFieldName )) { pRet = pFieldType; break; } } } return pRet; } /// Remove field type void DocumentFieldsManager::RemoveFieldType(size_t nField) { OSL_ENSURE( INIT_FLDTYPES <= nField, "don't remove InitFields" ); /* * Dependent fields present -> ErrRaise */ if(nField >= mpFieldTypes->size()) return; SwFieldType* pTmp = (*mpFieldTypes)[nField].get(); // JP 29.07.96: Optionally prepare FieldList for Calculator SwFieldIds nWhich = pTmp->Which(); switch( nWhich ) { case SwFieldIds::SetExp: case SwFieldIds::User: mpUpdateFields->RemoveFieldType( *pTmp ); [[fallthrough]]; case SwFieldIds::Dde: if( pTmp->HasWriterListeners() && !m_rDoc.IsUsed( *pTmp ) ) { if( SwFieldIds::SetExp == nWhich ) static_cast(pTmp)->SetDeleted( true ); else if( SwFieldIds::User == nWhich ) static_cast(pTmp)->SetDeleted( true ); else static_cast(pTmp)->SetDeleted( true ); nWhich = SwFieldIds::Database; } break; default: break; } if( nWhich != SwFieldIds::Database ) { OSL_ENSURE( !pTmp->HasWriterListeners(), "Dependent fields present!" ); } else (*mpFieldTypes)[nField].release(); // DB fields are ref-counted and delete themselves mpFieldTypes->erase( mpFieldTypes->begin() + nField ); m_rDoc.getIDocumentState().SetModified(); } // All have to be re-evaluated. void DocumentFieldsManager::UpdateFields(bool bCloseDB) { // Tell all types to update their fields for(auto const& pFieldType: *mpFieldTypes) pFieldType->UpdateFields(); if(!IsExpFieldsLocked()) UpdateExpFields(nullptr, false); // update expression fields // Tables UpdateTableFields(nullptr); // References UpdateRefFields(); if(bCloseDB) { #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS m_rDoc.GetDBManager()->CloseAll(); #endif } // Only evaluate on full update m_rDoc.getIDocumentState().SetModified(); } void DocumentFieldsManager::InsDeletedFieldType( SwFieldType& rFieldTyp ) { // The FieldType was marked as deleted and removed from the array. // One has to look this up again, now. // - If it's not present, it can be re-inserted. // - If the same type is found, the deleted one has to be renamed. const SwFieldTypes::size_type nSize = mpFieldTypes->size(); const SwFieldIds nFieldWhich = rFieldTyp.Which(); OSL_ENSURE( SwFieldIds::SetExp == nFieldWhich || SwFieldIds::User == nFieldWhich || SwFieldIds::Dde == nFieldWhich, "Wrong FieldType" ); const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); const OUString& rFieldNm = rFieldTyp.GetName(); for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i ) { SwFieldType* pFnd = (*mpFieldTypes)[i].get(); if( nFieldWhich == pFnd->Which() && rSCmp.isEqual( rFieldNm, pFnd->GetName() ) ) { // find new name SwFieldTypes::size_type nNum = 1; do { OUString sSrch = rFieldNm + OUString::number( nNum ); for( i = INIT_FLDTYPES; i < nSize; ++i ) { pFnd = (*mpFieldTypes)[i].get(); if( nFieldWhich == pFnd->Which() && rSCmp.isEqual( sSrch, pFnd->GetName() ) ) break; } if( i >= nSize ) // not found { const_cast(rFieldNm) = sSrch; break; // exit while loop } ++nNum; } while( true ); break; } } // not found, so insert, and updated deleted flag mpFieldTypes->insert( mpFieldTypes->begin() + nSize, std::unique_ptr(&rFieldTyp) ); switch( nFieldWhich ) { case SwFieldIds::SetExp: static_cast(rFieldTyp).SetDeleted( false ); break; case SwFieldIds::User: static_cast(rFieldTyp).SetDeleted( false ); break; case SwFieldIds::Dde: static_cast(rFieldTyp).SetDeleted( false ); break; default: break; } } void DocumentFieldsManager::PutValueToField(const SwPosition & rPos, const Any& rVal, sal_uInt16 nWhich) { Any aOldVal; SwField * pField = GetFieldAtPos(rPos); if (m_rDoc.GetIDocumentUndoRedo().DoesUndo() && pField->QueryValue(aOldVal, nWhich)) { m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique(rPos, aOldVal, rVal, nWhich)); } pField->PutValue(rVal, nWhich); } bool DocumentFieldsManager::UpdateField(SwTextField * pDstTextField, SwField & rSrcField, SwMsgPoolItem * pMsgHint, bool bUpdateFields) { OSL_ENSURE(pDstTextField, "no field to update!"); bool bTableSelBreak = false; SwFormatField * pDstFormatField = const_cast(&pDstTextField->GetFormatField()); SwField * pDstField = pDstFormatField->GetField(); SwFieldIds nFieldWhich = rSrcField.GetTyp()->Which(); SwNodeIndex aTableNdIdx(pDstTextField->GetTextNode()); if (pDstField->GetTyp()->Which() == rSrcField.GetTyp()->Which()) { if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) { SwPosition aPosition( pDstTextField->GetTextNode() ); aPosition.nContent = pDstTextField->GetStart(); m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique( aPosition, *pDstField, rSrcField, pMsgHint, bUpdateFields) ); } pDstFormatField->SetField(rSrcField.CopyField()); SwField* pNewField = pDstFormatField->GetField(); switch( nFieldWhich ) { case SwFieldIds::SetExp: case SwFieldIds::GetExp: case SwFieldIds::HiddenText: case SwFieldIds::HiddenPara: UpdateExpFields( pDstTextField, true ); break; case SwFieldIds::Table: { const SwTableNode* pTableNd = m_rDoc.IsIdxInTable(aTableNdIdx); if( pTableNd ) { SwTableFormulaUpdate aTableUpdate( &pTableNd-> GetTable() ); if (bUpdateFields) UpdateTableFields( &aTableUpdate ); else pNewField->GetTyp()->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aTableUpdate)); if (! bUpdateFields) bTableSelBreak = true; } } break; case SwFieldIds::Macro: if( bUpdateFields && pDstTextField->GetpTextNode() ) pDstTextField->GetpTextNode()->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, pDstFormatField)); break; case SwFieldIds::DatabaseName: case SwFieldIds::DbNextSet: case SwFieldIds::DbNumSet: case SwFieldIds::DbSetNumber: m_rDoc.ChgDBData(static_cast( pNewField)->GetRealDBData()); pNewField->GetTyp()->UpdateFields(); break; case SwFieldIds::Database: #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS { // JP 10.02.96: call ChgValue, so that the style change sets the // ContentString correctly SwDBField* pDBField = static_cast(pNewField); if (pDBField->IsInitialized()) pDBField->ChgValue( pDBField->GetValue(), true ); pDBField->ClearInitialized(); pDBField->InitContent(); } #endif [[fallthrough]]; default: pDstFormatField->UpdateTextNode(nullptr, pMsgHint); } // The fields we can calculate here are being triggered for an update // here explicitly. if( nFieldWhich == SwFieldIds::User ) UpdateUsrFields(); } return bTableSelBreak; } /// Update reference and table fields void DocumentFieldsManager::UpdateRefFields() { for(auto const& pFieldType: *mpFieldTypes) if(SwFieldIds::GetRef == pFieldType->Which()) static_cast(pFieldType.get())->UpdateGetReferences(); } void DocumentFieldsManager::UpdateTableFields( SfxPoolItem* pHt ) { OSL_ENSURE( !pHt || RES_TABLEFML_UPDATE == pHt->Which(), "What MessageItem is this?" ); auto pFieldType = GetFieldType( SwFieldIds::Table, OUString(), false ); if(pFieldType) { std::vector vFields; pFieldType->GatherFields(vFields); SwTableFormulaUpdate* pUpdateField = nullptr; if( pHt && RES_TABLEFML_UPDATE == pHt->Which() ) pUpdateField = static_cast(pHt); for(auto pFormatField : vFields) { SwTableField* pField = static_cast(pFormatField->GetField()); if( pUpdateField ) { // table where this field is located const SwTableNode* pTableNd; const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode(); pTableNd = rTextNd.FindTableNode(); if (pTableNd == nullptr) continue; switch( pUpdateField->m_eFlags ) { case TBL_CALC: // re-set the value flag // JP 17.06.96: internal representation of all formulas // (reference to other table!!!) if( nsSwExtendedSubType::SUB_CMD & pField->GetSubType() ) pField->PtrToBoxNm( pUpdateField->m_pTable ); else pField->ChgValid( false ); break; case TBL_BOXNAME: // is this the wanted table? if( &pTableNd->GetTable() == pUpdateField->m_pTable ) // to the external representation pField->PtrToBoxNm( pUpdateField->m_pTable ); break; case TBL_BOXPTR: // to the internal representation // JP 17.06.96: internal representation on all formulas // (reference to other table!!!) pField->BoxNmToPtr( &pTableNd->GetTable() ); break; case TBL_RELBOXNAME: // is this the wanted table? if( &pTableNd->GetTable() == pUpdateField->m_pTable ) // to the relative representation pField->ToRelBoxNm( pUpdateField->m_pTable ); break; default: break; } } else // reset the value flag for all pField->ChgValid( false ); } } // process all table box formulas for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA)) { auto pBoxFormula = dynamic_cast(pItem); if( pBoxFormula && pBoxFormula->GetDefinedIn() ) { const_cast(pBoxFormula)->ChangeState( pHt ); } } SwRootFrame const* pLayout(nullptr); for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts()) { assert(!pLayout || pLay->IsHideRedlines() == pLayout->IsHideRedlines()); // TODO pLayout = pLay; } // all fields/boxes are now invalid, so we can start to calculate if( pHt && ( RES_TABLEFML_UPDATE != pHt->Which() || TBL_CALC != static_cast(pHt)->m_eFlags )) return ; std::unique_ptr> pCalc; if( pFieldType ) { std::vector vFields; pFieldType->GatherFields(vFields); for(SwFormatField* pFormatField: vFields) { // start calculation at the end // new fields are inserted at the beginning of the modify chain // that gives faster calculation on import // mba: do we really need this "optimization"? Is it still valid? SwTableField *const pField(static_cast(pFormatField->GetField())); if (nsSwExtendedSubType::SUB_CMD & pField->GetSubType()) continue; // needs to be recalculated if( !pField->IsValid() ) { // table where this field is located const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode(); const SwTableNode* pTableNd = rTextNd.FindTableNode(); if( !pTableNd ) continue; // if this field is not in the to-be-updated table, skip it if( pHt && &pTableNd->GetTable() != static_cast(pHt)->m_pTable ) continue; if( !pCalc ) pCalc.reset(new SwCalc( m_rDoc )); // get the values of all SetExpression fields that are valid // until the table SwFrame* pFrame = nullptr; if( pTableNd->GetIndex() < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() ) { // is in the special section, that's expensive! Point aPt; // return the first frame of the layout - Tab.Headline!! std::pair const tmp(aPt, true); pFrame = rTextNd.getLayoutFrame(pLayout, nullptr, &tmp); if( pFrame ) { SwPosition aPos( *pTableNd ); if( GetBodyTextNode( m_rDoc, aPos, *pFrame ) ) { FieldsToCalc( *pCalc, SetGetExpField( aPos.nNode, pFormatField->GetTextField(), &aPos.nContent, pFrame->GetPhyPageNum()), pLayout); } else pFrame = nullptr; } } if( !pFrame ) { // create index to determine the TextNode SwNodeIndex aIdx( rTextNd ); SwFrame const*const pFrame2 = ::sw::FindNeighbourFrameForNode(rTextNd); FieldsToCalc( *pCalc, SetGetExpField(aIdx, pFormatField->GetTextField(), nullptr, pFrame2 ? pFrame2->GetPhyPageNum() : 0), pLayout); } SwTableCalcPara aPara(*pCalc, pTableNd->GetTable(), pLayout); pField->CalcField( aPara ); if( aPara.IsStackOverflow() ) { bool const bResult = aPara.CalcWithStackOverflow(); if (bResult) { pField->CalcField( aPara ); } OSL_ENSURE(bResult, "the chained formula could no be calculated"); } pCalc->SetCalcError( SwCalcError::NONE ); } pFormatField->UpdateTextNode(nullptr, pHt); } } // calculate the formula at the boxes for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA)) { auto pFormula = const_cast(dynamic_cast(pItem)); if( pFormula && pFormula->GetDefinedIn() && !pFormula->IsValid() ) { SwTableBox* pBox = pFormula->GetTableBox(); if( pBox && pBox->GetSttNd() && pBox->GetSttNd()->GetNodes().IsDocNodes() ) { const SwTableNode* pTableNd = pBox->GetSttNd()->FindTableNode(); if( !pHt || &pTableNd->GetTable() == static_cast(pHt)->m_pTable ) { double nValue; if( !pCalc ) pCalc.reset(new SwCalc( m_rDoc )); // get the values of all SetExpression fields that are valid // until the table SwFrame* pFrame = nullptr; if( pTableNd->GetIndex() < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() ) { // is in the special section, that's expensive! SwNodeIndex aCNdIdx( *pTableNd, +2 ); SwContentNode* pCNd = aCNdIdx.GetNode().GetContentNode(); if( !pCNd ) pCNd = m_rDoc.GetNodes().GoNext( &aCNdIdx ); if (pCNd) { Point aPt; // return the first frame of the layout - Tab.Headline!! std::pair const tmp(aPt, true); pFrame = pCNd->getLayoutFrame(pLayout, nullptr, &tmp); if( pFrame ) { SwPosition aPos( *pCNd ); if( GetBodyTextNode( m_rDoc, aPos, *pFrame ) ) { FieldsToCalc(*pCalc, SetGetExpField(aPos.nNode, nullptr, nullptr, pFrame->GetPhyPageNum()), pLayout); } else pFrame = nullptr; } } } if( !pFrame ) { // create index to determine the TextNode SwNodeIndex aIdx( *pTableNd ); SwFrame const*const pFrame2 = ::sw::FindNeighbourFrameForNode(*pTableNd); FieldsToCalc(*pCalc, SetGetExpField(aIdx, nullptr, nullptr, pFrame2 ? pFrame2->GetPhyPageNum() : 0), pLayout); } SwTableCalcPara aPara(*pCalc, pTableNd->GetTable(), pLayout); pFormula->Calc( aPara, nValue ); if( aPara.IsStackOverflow() ) { bool const bResult = aPara.CalcWithStackOverflow(); if (bResult) { pFormula->Calc( aPara, nValue ); } OSL_ENSURE(bResult, "the chained formula could no be calculated"); } SwFrameFormat* pFormat = pBox->ClaimFrameFormat(); SfxItemSetFixed aTmp( m_rDoc.GetAttrPool() ); if( pCalc->IsCalcError() ) nValue = DBL_MAX; aTmp.Put( SwTableBoxValue( nValue )); if( SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_FORMAT )) aTmp.Put( SwTableBoxNumFormat( 0 )); pFormat->SetFormatAttr( aTmp ); pCalc->SetCalcError( SwCalcError::NONE ); } } } } } void DocumentFieldsManager::UpdateExpFields( SwTextField* pUpdateField, bool bUpdRefFields ) { if( IsExpFieldsLocked() || m_rDoc.IsInReading() ) return; bool bOldInUpdateFields = mpUpdateFields->IsInUpdateFields(); mpUpdateFields->SetInUpdateFields( true ); mpUpdateFields->MakeFieldList( m_rDoc, true, GETFLD_ALL ); mbNewFieldLst = false; if (mpUpdateFields->GetSortList()->empty()) { if( bUpdRefFields ) UpdateRefFields(); mpUpdateFields->SetInUpdateFields( bOldInUpdateFields ); mpUpdateFields->SetFieldsDirty( false ); return ; } SwRootFrame const* pLayout(nullptr); SwRootFrame const* pLayoutRLHidden(nullptr); for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts()) { if (pLay->IsHideRedlines()) { pLayoutRLHidden = pLay; } else { pLayout = pLay; } } if (pLayout || !pLayoutRLHidden) // always calc *something*... { UpdateExpFieldsImpl(pUpdateField, pLayout); } if (pLayoutRLHidden) { UpdateExpFieldsImpl(pUpdateField, pLayoutRLHidden); } // update reference fields if( bUpdRefFields ) UpdateRefFields(); mpUpdateFields->SetInUpdateFields( bOldInUpdateFields ); mpUpdateFields->SetFieldsDirty( false ); } void DocumentFieldsManager::UpdateExpFieldsImpl( SwTextField * pUpdateField, SwRootFrame const*const pLayout) { SwFieldIds nWhich; // Hash table for all string replacements is filled on-the-fly. // Try to fabricate an uneven number. const SwFieldTypes::size_type nHashSize {(( mpFieldTypes->size() / 7 ) + 1 ) * 7}; const sal_uInt16 nStrFormatCnt = o3tl::narrowing(nHashSize); OSL_ENSURE( nStrFormatCnt == nHashSize, "Downcasting to sal_uInt16 lost information!" ); SwHashTable aHashStrTable(nStrFormatCnt); { const SwFieldType* pFieldType; // process separately: for( auto n = mpFieldTypes->size(); n; ) { pFieldType = (*mpFieldTypes)[ --n ].get(); switch( pFieldType->Which() ) { case SwFieldIds::User: { // Entry present? sal_uInt16 nPos; const OUString& rNm = pFieldType->GetName(); OUString sExpand(const_cast(static_cast(pFieldType))->Expand(nsSwGetSetExpType::GSE_STRING, 0, LANGUAGE_SYSTEM)); SwHash* pFnd = aHashStrTable.Find( rNm, &nPos ); if( pFnd ) // modify entry in the hash table static_cast(pFnd)->aSetStr = sExpand; else // insert the new entry aHashStrTable[nPos].reset( new HashStr( rNm, sExpand, aHashStrTable[nPos].release() ) ); } break; default: break; } } } // The array is filled with all fields; start calculation. SwCalc aCalc( m_rDoc ); #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS OUString sDBNumNm( SwFieldType::GetTypeStr( SwFieldTypesEnum::DatabaseSetNumber ) ); // already set the current record number SwDBManager* pMgr = m_rDoc.GetDBManager(); pMgr->CloseAll( false ); SvtSysLocale aSysLocale; const LocaleDataWrapper* pLclData = &aSysLocale.GetLocaleData(); const LanguageType nLang = pLclData->getLanguageTag().getLanguageType(); bool bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc ); #endif // Make sure we don't hide all content, which would lead to a crash. First, count how many visible sections we have. int nShownSections = 0; SwNodeOffset nContentStart = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionIndex() + 1; SwNodeOffset nContentEnd = m_rDoc.GetNodes().GetEndOfContent().GetIndex(); SwSectionFormats& rSectFormats = m_rDoc.GetSections(); for( SwSectionFormats::size_type n = 0; nEndOfSectionNode(), 1 ); if ( n == 0 && pSectionNode->GetIndex() != nContentStart ) nShownSections++; //document does not start with a section if ( n == rSectFormats.size() - 1 ) { if ( aNextIdx.GetIndex() != nContentEnd ) nShownSections++; //document does not end in a section } else if ( !aNextIdx.GetNode().IsSectionNode() ) nShownSections++; //section is not immediately followed by another section } // count only visible sections if ( pSect && !pSect->CalcHiddenFlag()) nShownSections++; } IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess()); std::unordered_map SetExpOutlineNodeMap; for (std::unique_ptr const& it : *mpUpdateFields->GetSortList()) { SwSection* pSect = const_cast(it->GetSection()); if( pSect ) { SwSbxValue aValue = aCalc.Calculate( pSect->GetCondition() ); if(!aValue.IsVoidValue()) { // Do we want to hide this one? bool bHide = aValue.GetBool(); if (bHide && !pSect->IsCondHidden()) { // This section will be hidden, but it wasn't before if (nShownSections == 1) { // This would be the last section, so set its condition to false, and avoid hiding it. pSect->SetCondition("0"); bHide = false; } nShownSections--; } pSect->SetCondHidden( bHide ); } continue; } ::sw::mark::IBookmark *const pBookmark( const_cast<::sw::mark::IBookmark *>(it->GetBookmark())); if (pBookmark) { SwSbxValue const aValue(aCalc.Calculate(pBookmark->GetHideCondition())); if (!aValue.IsVoidValue()) { pBookmark->Hide(aValue.GetBool()); } continue; } SwTextField* pTextField = const_cast(it->GetTextField()); if( !pTextField ) { OSL_ENSURE( false, "what's wrong now'" ); continue; } if (pLayout && pLayout->IsHideRedlines() && IsFieldDeleted(rIDRA, *pLayout, *pTextField)) { continue; } SwFormatField* pFormatField = const_cast(&pTextField->GetFormatField()); const SwField* pField = pFormatField->GetField(); nWhich = pField->GetTyp()->Which(); switch( nWhich ) { case SwFieldIds::HiddenText: { SwHiddenTextField* pHField = const_cast(static_cast(pField)); SwSbxValue aValue = aCalc.Calculate( pHField->GetPar1() ); bool bValue = !aValue.GetBool(); if(!aValue.IsVoidValue()) { pHField->SetValue( bValue ); // evaluate field pHField->Evaluate(m_rDoc); } } break; case SwFieldIds::HiddenPara: { SwHiddenParaField* pHPField = const_cast(static_cast(pField)); SwSbxValue aValue = aCalc.Calculate( pHPField->GetPar1() ); bool bValue = aValue.GetBool(); if(!aValue.IsVoidValue()) pHPField->SetHidden( bValue ); } break; case SwFieldIds::DbSetNumber: #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS { const_cast(static_cast(pField))->Evaluate(m_rDoc); aCalc.VarChange( sDBNumNm, static_cast(pField)->GetSetNumber()); pField->ExpandField(m_rDoc.IsClipBoard(), nullptr); } #endif break; case SwFieldIds::DbNextSet: case SwFieldIds::DbNumSet: #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS { UpdateDBNumFields( *const_cast(static_cast(pField)), aCalc ); if( bCanFill ) bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc ); } #endif break; case SwFieldIds::Database: { #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS // evaluate field const_cast(static_cast(pField))->Evaluate(); SwDBData aTmpDBData(static_cast(pField)->GetDBData()); if( pMgr->IsDataSourceOpen(aTmpDBData.sDataSource, aTmpDBData.sCommand, false)) aCalc.VarChange( sDBNumNm, pMgr->GetSelectedRecordId(aTmpDBData.sDataSource, aTmpDBData.sCommand, aTmpDBData.nCommandType)); const OUString& rName = pField->GetTyp()->GetName(); // Add entry to hash table // Entry present? sal_uInt16 nPos; HashStr* pFnd = aHashStrTable.Find( rName, &nPos ); OUString const value(pField->ExpandField(m_rDoc.IsClipBoard(), nullptr)); if( pFnd ) { // Modify entry in the hash table pFnd->aSetStr = value; } else { // insert new entry aHashStrTable[nPos].reset( new HashStr( rName, value, aHashStrTable[nPos].release()) ); } #endif } break; case SwFieldIds::GetExp: case SwFieldIds::SetExp: { if( nsSwGetSetExpType::GSE_STRING & pField->GetSubType() ) // replace String { if( SwFieldIds::GetExp == nWhich ) { SwGetExpField* pGField = const_cast(static_cast(pField)); if( (!pUpdateField || pUpdateField == pTextField ) && pGField->IsInBodyText() ) { OUString aNew = LookString( aHashStrTable, pGField->GetFormula() ); pGField->ChgExpStr( aNew, pLayout ); } } else { SwSetExpField* pSField = const_cast(static_cast(pField)); // is the "formula" a field? OUString aNew = LookString( aHashStrTable, pSField->GetFormula() ); if( aNew.isEmpty() ) // nothing found then the formula is the new value aNew = pSField->GetFormula(); // only update one field if( !pUpdateField || pUpdateField == pTextField ) pSField->ChgExpStr( aNew, pLayout ); // lookup the field's name aNew = static_cast(pSField->GetTyp())->GetSetRefName(); // Entry present? sal_uInt16 nPos; HashStr* pFnd = aHashStrTable.Find( aNew, &nPos ); if( pFnd ) // Modify entry in the hash table pFnd->aSetStr = pSField->GetExpStr(pLayout); else { // insert new entry aHashStrTable[nPos].reset( new HashStr( aNew, pSField->GetExpStr(pLayout), aHashStrTable[nPos].release() ) ); pFnd = aHashStrTable[nPos].get(); } // Extension for calculation with Strings SwSbxValue aValue; aValue.PutString( pFnd->aSetStr ); aCalc.VarChange( aNew, aValue ); } } else // recalculate formula { if( SwFieldIds::GetExp == nWhich ) { SwGetExpField* pGField = const_cast(static_cast(pField)); if( (!pUpdateField || pUpdateField == pTextField ) && pGField->IsInBodyText() ) { SwSbxValue aValue = aCalc.Calculate( pGField->GetFormula()); if(!aValue.IsVoidValue()) pGField->SetValue(aValue.GetDouble(), pLayout); } } else { SwSetExpField* pSField = const_cast(static_cast(pField)); SwSetExpFieldType* pSFieldTyp = static_cast(pField->GetTyp()); OUString aNew = pSFieldTyp->GetName(); SwNode* pSeqNd = nullptr; if( pSField->IsSequenceField() ) { const sal_uInt8 nLvl = pSFieldTyp->GetOutlineLvl(); if( MAXLEVEL > nLvl ) { // test if the Number needs to be updated pSeqNd = m_rDoc.GetNodes()[ it->GetNode() ]; const SwTextNode* pOutlNd = pSeqNd-> FindOutlineNodeOfLevel(nLvl, pLayout); auto const iter(SetExpOutlineNodeMap.find(pSFieldTyp)); if (iter == SetExpOutlineNodeMap.end() || iter->second != pOutlNd) { SetExpOutlineNodeMap[pSFieldTyp] = pOutlNd; aCalc.VarChange( aNew, 0 ); } } } aNew += "=" + pSField->GetFormula(); SwSbxValue aValue = aCalc.Calculate( aNew ); if (!aCalc.IsCalcError()) { double nErg = aValue.GetDouble(); // only update one field if( !aValue.IsVoidValue() && (!pUpdateField || pUpdateField == pTextField) ) { pSField->SetValue(nErg, pLayout); if( pSeqNd ) pSFieldTyp->SetChapter(*pSField, *pSeqNd, pLayout); } } } } } break; default: break; } // switch { // avoid calling ReplaceText() for input fields, it is pointless // here and moves the cursor if it's inside the field ... SwTextInputField *const pInputField( pUpdateField == pTextField // ... except once, when the dialog ? nullptr // is used to change content via UpdateOneField() : dynamic_cast(pTextField)); if (pInputField) { bool const tmp = pInputField->LockNotifyContentChange(); (void) tmp; assert(tmp && "should not be locked here?"); } ::comphelper::ScopeGuard g([pInputField]() { if (pInputField) { pInputField->UnlockNotifyContentChange(); } }); pFormatField->UpdateTextNode(nullptr, nullptr); // trigger formatting } if (pUpdateField == pTextField) // if only this one is updated { if( SwFieldIds::GetExp == nWhich || // only GetField or SwFieldIds::HiddenText == nWhich || // HiddenText? SwFieldIds::HiddenPara == nWhich) // HiddenParaField? break; // quit pUpdateField = nullptr; // update all from here on } } #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS pMgr->CloseAll(false); #endif } /// Insert field type that was marked as deleted void DocumentFieldsManager::UpdateUsrFields() { SwCalc* pCalc = nullptr; for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < mpFieldTypes->size(); ++i ) { const SwFieldType* pFieldType = (*mpFieldTypes)[i].get(); if( SwFieldIds::User == pFieldType->Which() ) { if( !pCalc ) pCalc = new SwCalc( m_rDoc ); const_cast(static_cast(pFieldType))->GetValue( *pCalc ); } } if( pCalc ) { delete pCalc; m_rDoc.getIDocumentState().SetModified(); } } sal_Int32 DocumentFieldsManager::GetRecordsPerDocument() const { sal_Int32 nRecords = 1; mpUpdateFields->MakeFieldList( m_rDoc, true, GETFLD_ALL ); if (mpUpdateFields->GetSortList()->empty()) return nRecords; for (std::unique_ptr const& it : *mpUpdateFields->GetSortList()) { const SwTextField *pTextField = it->GetTextField(); if( !pTextField ) continue; const SwFormatField &pFormatField = pTextField->GetFormatField(); const SwField* pField = pFormatField.GetField(); switch( pField->GetTyp()->Which() ) { case SwFieldIds::DbNextSet: case SwFieldIds::DbNumSet: nRecords++; break; default: break; } } return nRecords; } void DocumentFieldsManager::UpdatePageFields( SfxPoolItem* pMsgHint ) { for( SwFieldTypes::size_type i = 0; i < INIT_FLDTYPES; ++i ) { SwFieldType* pFieldType = (*mpFieldTypes)[ i ].get(); switch( pFieldType->Which() ) { case SwFieldIds::PageNumber: case SwFieldIds::Chapter: case SwFieldIds::GetExp: case SwFieldIds::RefPageGet: pFieldType->CallSwClientNotify(sw::LegacyModifyHint(nullptr, pMsgHint)); break; case SwFieldIds::DocStat: pFieldType->CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr)); break; default: break; } } SetNewFieldLst(true); } void DocumentFieldsManager::LockExpFields() { ++mnLockExpField; } void DocumentFieldsManager::UnlockExpFields() { assert(mnLockExpField != 0); if( mnLockExpField ) --mnLockExpField; } bool DocumentFieldsManager::IsExpFieldsLocked() const { return 0 != mnLockExpField; } SwDocUpdateField& DocumentFieldsManager::GetUpdateFields() const { return *mpUpdateFields; } bool DocumentFieldsManager::SetFieldsDirty( bool b, const SwNode* pChk, SwNodeOffset nLen ) { // See if the supplied nodes actually contain fields. // If they don't, the flag doesn't need to be changed. bool bFieldsFnd = false; if( b && pChk && !GetUpdateFields().IsFieldsDirty() && !m_rDoc.IsInDtor() // ?? what's up with Undo, this is also wanted there! /*&& &pChk->GetNodes() == &GetNodes()*/ ) { b = false; if( !nLen ) ++nLen; SwNodeOffset nStt = pChk->GetIndex(); const SwNodes& rNds = pChk->GetNodes(); while( nLen-- ) { const SwTextNode* pTNd = rNds[ nStt++ ]->GetTextNode(); if( pTNd ) { if( pTNd->GetAttrOutlineLevel() != 0 ) // update chapter fields b = true; else if( pTNd->GetpSwpHints() && pTNd->GetSwpHints().Count() ) { const size_t nEnd = pTNd->GetSwpHints().Count(); for( size_t n = 0 ; n < nEnd; ++n ) { const SwTextAttr* pAttr = pTNd->GetSwpHints().Get(n); if ( pAttr->Which() == RES_TXTATR_FIELD || pAttr->Which() == RES_TXTATR_INPUTFIELD) { b = true; break; } } } if( b ) break; } } bFieldsFnd = b; } GetUpdateFields().SetFieldsDirty( b ); return bFieldsFnd; } void DocumentFieldsManager::SetFixFields( const DateTime* pNewDateTime ) { bool bIsModified = m_rDoc.getIDocumentState().IsModified(); sal_Int32 nDate; sal_Int64 nTime; if( pNewDateTime ) { nDate = pNewDateTime->GetDate(); nTime = pNewDateTime->GetTime(); } else { DateTime aDateTime( DateTime::SYSTEM ); nDate = aDateTime.GetDate(); nTime = aDateTime.GetTime(); } SwFieldIds const aTypes[] { /*0*/ SwFieldIds::DocInfo, /*1*/ SwFieldIds::Author, /*2*/ SwFieldIds::ExtUser, /*3*/ SwFieldIds::Filename, /*4*/ SwFieldIds::DateTime }; // MUST be at the end! for(SwFieldIds aType : aTypes) { std::vector vFields; GetSysFieldType(aType)->GatherFields(vFields); for(auto pFormatField: vFields) { if (pFormatField->GetTextField()) { bool bChgd = false; switch( aType ) { case SwFieldIds::DocInfo: if( static_cast(pFormatField->GetField())->IsFixed() ) { bChgd = true; SwDocInfoField* pDocInfField = static_cast(pFormatField->GetField()); pDocInfField->SetExpansion( static_cast( pDocInfField->GetTyp())->Expand( pDocInfField->GetSubType(), pDocInfField->GetFormat(), pDocInfField->GetLanguage(), pDocInfField->GetName() ) ); } break; case SwFieldIds::Author: if( static_cast(pFormatField->GetField())->IsFixed() ) { bChgd = true; SwAuthorField* pAuthorField = static_cast(pFormatField->GetField()); pAuthorField->SetExpansion( SwAuthorFieldType::Expand( pAuthorField->GetFormat() ) ); } break; case SwFieldIds::ExtUser: if( static_cast(pFormatField->GetField())->IsFixed() ) { bChgd = true; SwExtUserField* pExtUserField = static_cast(pFormatField->GetField()); pExtUserField->SetExpansion( SwExtUserFieldType::Expand(pExtUserField->GetSubType()) ); } break; case SwFieldIds::DateTime: if( static_cast(pFormatField->GetField())->IsFixed() ) { bChgd = true; static_cast(pFormatField->GetField())->SetDateTime( DateTime(Date(nDate), tools::Time(nTime)) ); } break; case SwFieldIds::Filename: if( static_cast(pFormatField->GetField())->IsFixed() ) { bChgd = true; SwFileNameField* pFileNameField = static_cast(pFormatField->GetField()); pFileNameField->SetExpansion( static_cast( pFileNameField->GetTyp())->Expand( pFileNameField->GetFormat() ) ); } break; default: break; } // Trigger formatting if( bChgd ) pFormatField->UpdateTextNode(nullptr, nullptr); } } } if( !bIsModified ) m_rDoc.getIDocumentState().ResetModified(); } void DocumentFieldsManager::FieldsToCalc(SwCalc& rCalc, const SetGetExpField& rToThisField, SwRootFrame const*const pLayout) { // create the sorted list of all SetFields mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC ); mbNewFieldLst = false; #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS SwDBManager* pMgr = NULL; #else SwDBManager* pMgr = m_rDoc.GetDBManager(); pMgr->CloseAll(false); #endif if (!mpUpdateFields->GetSortList()->empty()) { SetGetExpFields::const_iterator const itLast = mpUpdateFields->GetSortList()->upper_bound( &rToThisField); for (auto it = mpUpdateFields->GetSortList()->begin(); it != itLast; ++it) { lcl_CalcField(m_rDoc, rCalc, **it, pMgr, pLayout); } } #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS pMgr->CloseAll(false); #endif } void DocumentFieldsManager::FieldsToCalc(SwCalc& rCalc, SwNodeOffset const nLastNd, sal_Int32 const nLastCnt) { // create the sorted list of all SetFields mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC ); mbNewFieldLst = false; #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS SwDBManager* pMgr = NULL; #else SwDBManager* pMgr = m_rDoc.GetDBManager(); pMgr->CloseAll(false); #endif SwRootFrame const* pLayout(nullptr); SwRootFrame const* pLayoutRLHidden(nullptr); for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts()) { if (pLay->IsHideRedlines()) { pLayoutRLHidden = pLay; } else { pLayout = pLay; } } // note this is not duplicate of the other FieldsToCalc because there is // (currently) no SetGetExpField that compares only a position for(auto it = mpUpdateFields->GetSortList()->begin(); it != mpUpdateFields->GetSortList()->end() && ( (*it)->GetNode() < nLastNd || ( (*it)->GetNode() == nLastNd && (*it)->GetContent() <= nLastCnt ) ); ++it ) { if (pLayout || !pLayoutRLHidden) // always calc *something*... { lcl_CalcField( m_rDoc, rCalc, **it, pMgr, pLayout ); } if (pLayoutRLHidden) { lcl_CalcField( m_rDoc, rCalc, **it, pMgr, pLayoutRLHidden ); } } #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS pMgr->CloseAll(false); #endif } void DocumentFieldsManager::FieldsToExpand( SwHashTable & rHashTable, const SetGetExpField& rToThisField, SwRootFrame const& rLayout) { // create the sorted list of all SetFields mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_EXPAND ); mbNewFieldLst = false; IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess()); // Hash table for all string replacements is filled on-the-fly. // Try to fabricate an uneven number. sal_uInt16 nTableSize = ((mpUpdateFields->GetSortList()->size() / 7) + 1) * 7; rHashTable.resize(nTableSize); SetGetExpFields::const_iterator const itLast = mpUpdateFields->GetSortList()->upper_bound(&rToThisField); for (auto it = mpUpdateFields->GetSortList()->begin(); it != itLast; ++it) { const SwTextField* pTextField = (*it)->GetTextField(); if( !pTextField ) continue; if (rLayout.IsHideRedlines() && IsFieldDeleted(rIDRA, rLayout, *pTextField)) { continue; } const SwField* pField = pTextField->GetFormatField().GetField(); switch( pField->GetTyp()->Which() ) { case SwFieldIds::SetExp: if( nsSwGetSetExpType::GSE_STRING & pField->GetSubType() ) { // set the new value in the hash table // is the formula a field? SwSetExpField* pSField = const_cast(static_cast(pField)); OUString aNew = LookString( rHashTable, pSField->GetFormula() ); if( aNew.isEmpty() ) // nothing found, then the formula is aNew = pSField->GetFormula(); // the new value // #i3141# - update expression of field as in method // for string/text fields pSField->ChgExpStr(aNew, &rLayout); // look up the field's name aNew = static_cast(pSField->GetTyp())->GetSetRefName(); // Entry present? sal_uInt16 nPos; SwHash* pFnd = rHashTable.Find( aNew, &nPos ); if( pFnd ) // modify entry in the hash table static_cast(pFnd)->aSetStr = pSField->GetExpStr(&rLayout); else // insert the new entry rHashTable[nPos].reset( new HashStr( aNew, pSField->GetExpStr(&rLayout), rHashTable[nPos].release())); } break; case SwFieldIds::Database: { const OUString& rName = pField->GetTyp()->GetName(); // Insert entry in the hash table // Entry present? sal_uInt16 nPos; HashStr* pFnd = rHashTable.Find( rName, &nPos ); OUString const value(pField->ExpandField(m_rDoc.IsClipBoard(), nullptr)); if( pFnd ) { // modify entry in the hash table pFnd->aSetStr = value; } else { // insert the new entry rHashTable[nPos].reset( new HashStr( rName, value, rHashTable[nPos].release()) ); } } break; default: break; } } } bool DocumentFieldsManager::IsNewFieldLst() const { return mbNewFieldLst; } void DocumentFieldsManager::SetNewFieldLst(bool bFlag) { mbNewFieldLst = bFlag; } void DocumentFieldsManager::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField ) { if (!mbNewFieldLst && !m_rDoc.IsInDtor()) mpUpdateFields->InsDelFieldInFieldLst( bIns, rField ); } SwField * DocumentFieldsManager::GetFieldAtPos(const SwPosition & rPos) { SwTextField * const pAttr = GetTextFieldAtPos(rPos); return pAttr ? const_cast( pAttr->GetFormatField().GetField() ) : nullptr; } SwTextField * DocumentFieldsManager::GetTextFieldAtPos(const SwPosition & rPos) { SwTextNode * const pNode = rPos.nNode.GetNode().GetTextNode(); return (pNode != nullptr) ? pNode->GetFieldTextAttrAt( rPos.nContent.GetIndex(), true ) : nullptr; } /// @note For simplicity assume that all field types have updatable contents so /// optimization currently only available when no fields exist. bool DocumentFieldsManager::containsUpdatableFields() { std::vector vFields; for (auto const& pFieldType: *mpFieldTypes) { pFieldType->GatherFields(vFields); if(vFields.size()>0) return true; } return false; } /// Remove all unreferenced field types of a document void DocumentFieldsManager::GCFieldTypes() { for( auto n = mpFieldTypes->size(); n > INIT_FLDTYPES; ) if( !(*mpFieldTypes)[ --n ]->HasWriterListeners() ) RemoveFieldType( n ); } void DocumentFieldsManager::InitFieldTypes() // is being called by the CTOR { // Field types mpFieldTypes->emplace_back( new SwDateTimeFieldType(&m_rDoc) ); mpFieldTypes->emplace_back( new SwChapterFieldType ); mpFieldTypes->emplace_back( new SwPageNumberFieldType ); mpFieldTypes->emplace_back( new SwAuthorFieldType ); mpFieldTypes->emplace_back( new SwFileNameFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwDBNameFieldType(&m_rDoc) ); mpFieldTypes->emplace_back( new SwGetExpFieldType(&m_rDoc) ); mpFieldTypes->emplace_back( new SwGetRefFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwHiddenTextFieldType ); mpFieldTypes->emplace_back( new SwPostItFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwDocStatFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwDocInfoFieldType(&m_rDoc) ); mpFieldTypes->emplace_back( new SwInputFieldType( &m_rDoc ) ); mpFieldTypes->emplace_back( new SwTableFieldType( &m_rDoc ) ); mpFieldTypes->emplace_back( new SwMacroFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwHiddenParaFieldType ); mpFieldTypes->emplace_back( new SwDBNextSetFieldType ); mpFieldTypes->emplace_back( new SwDBNumSetFieldType ); mpFieldTypes->emplace_back( new SwDBSetNumberFieldType ); mpFieldTypes->emplace_back( new SwTemplNameFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwTemplNameFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwExtUserFieldType ); mpFieldTypes->emplace_back( new SwRefPageSetFieldType ); mpFieldTypes->emplace_back( new SwRefPageGetFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwJumpEditFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwScriptFieldType(m_rDoc) ); mpFieldTypes->emplace_back( new SwCombinedCharFieldType ); mpFieldTypes->emplace_back( new SwDropDownFieldType ); // Types have to be at the end! // We expect this in the InsertFieldType! // MIB 14.04.95: In Sw3StringPool::Setup (sw3imp.cxx) and // lcl_sw3io_InSetExpField (sw3field.cxx) now also mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc, SwResId(STR_POOLCOLL_LABEL_ABB), nsSwGetSetExpType::GSE_SEQ) ); mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc, SwResId(STR_POOLCOLL_LABEL_TABLE), nsSwGetSetExpType::GSE_SEQ) ); mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc, SwResId(STR_POOLCOLL_LABEL_FRAME), nsSwGetSetExpType::GSE_SEQ) ); mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc, SwResId(STR_POOLCOLL_LABEL_DRAWING), nsSwGetSetExpType::GSE_SEQ) ); mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc, SwResId(STR_POOLCOLL_LABEL_FIGURE), nsSwGetSetExpType::GSE_SEQ) ); assert( mpFieldTypes->size() == INIT_FLDTYPES ); } void DocumentFieldsManager::ClearFieldTypes() { mpFieldTypes->erase( mpFieldTypes->begin() + INIT_FLDTYPES, mpFieldTypes->end() ); } void DocumentFieldsManager::UpdateDBNumFields( SwDBNameInfField& rDBField, SwCalc& rCalc ) { #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS (void) rDBField; (void) rCalc; #else SwDBManager* pMgr = m_rDoc.GetDBManager(); SwFieldIds nFieldType = rDBField.Which(); bool bPar1 = rCalc.Calculate( rDBField.GetPar1() ).GetBool(); if( SwFieldIds::DbNextSet == nFieldType ) static_cast(rDBField).SetCondValid( bPar1 ); else static_cast(rDBField).SetCondValid( bPar1 ); if( !rDBField.GetRealDBData().sDataSource.isEmpty() ) { // Edit a certain database if( SwFieldIds::DbNextSet == nFieldType ) static_cast(rDBField).Evaluate(m_rDoc); else static_cast(rDBField).Evaluate(m_rDoc); SwDBData aTmpDBData( rDBField.GetDBData(&m_rDoc) ); if( pMgr->OpenDataSource( aTmpDBData.sDataSource, aTmpDBData.sCommand )) rCalc.VarChange( lcl_GetDBVarName( m_rDoc, rDBField), pMgr->GetSelectedRecordId(aTmpDBData.sDataSource, aTmpDBData.sCommand, aTmpDBData.nCommandType) ); } else { OSL_FAIL("TODO: what should happen with unnamed DBFields?"); } #endif } DocumentFieldsManager::~DocumentFieldsManager() { mpUpdateFields.reset(); mpFieldTypes.reset(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */