/************************************************************** * * 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 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" // INCLUDE --------------------------------------------------------------- #include #include #include #include #include #include #include "cell.hxx" #include "compiler.hxx" #include "formula/errorcodes.hxx" #include "document.hxx" #include "rangenam.hxx" #include "rechead.hxx" #include "refupdat.hxx" #include "scmatrix.hxx" #include "editutil.hxx" #include "chgtrack.hxx" #include "externalrefmgr.hxx" using namespace formula; // STATIC DATA ----------------------------------------------------------- #ifdef USE_MEMPOOL const sal_uInt16 nMemPoolEditCell = (0x1000 - 64) / sizeof(ScNoteCell); IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell, nMemPoolEditCell, nMemPoolEditCell ) #endif // ============================================================================ ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP, const SfxItemPool* pFromPool ) : ScBaseCell( CELLTYPE_EDIT ), pString( NULL ), pDoc( pDocP ) { SetTextObject( pObject, pFromPool ); } ScEditCell::ScEditCell( const ScEditCell& rCell, ScDocument& rDoc ) : ScBaseCell( rCell ), pString( NULL ), pDoc( &rDoc ) { SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() ); } ScEditCell::ScEditCell( const String& rString, ScDocument* pDocP ) : ScBaseCell( CELLTYPE_EDIT ), pString( NULL ), pDoc( pDocP ) { DBG_ASSERT( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND, "EditCell mit einfachem Text !?!?" ); EditEngine& rEngine = pDoc->GetEditEngine(); rEngine.SetText( rString ); pData = rEngine.CreateTextObject(); } ScEditCell::~ScEditCell() { delete pData; delete pString; #ifdef DBG_UTIL eCellType = CELLTYPE_DESTROYED; #endif } void ScEditCell::SetData( const EditTextObject* pObject, const SfxItemPool* pFromPool ) { if ( pString ) { delete pString; pString = NULL; } delete pData; SetTextObject( pObject, pFromPool ); } void ScEditCell::GetData( const EditTextObject*& rpObject ) const { rpObject = pData; } void ScEditCell::GetString( String& rString ) const { if ( pString ) rString = *pString; else if ( pData ) { // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine EditEngine& rEngine = pDoc->GetEditEngine(); rEngine.SetText( *pData ); rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs // cache short strings for formulas if ( rString.Len() < 256 ) ((ScEditCell*)this)->pString = new String( rString ); //! non-const } else rString.Erase(); } void ScEditCell::SetTextObject( const EditTextObject* pObject, const SfxItemPool* pFromPool ) { if ( pObject ) { if ( pFromPool && pDoc->GetEditPool() == pFromPool ) pData = pObject->Clone(); else { //! anderer Pool // Leider gibt es keinen anderen Weg, um den Pool umzuhaengen, // als das Object durch eine entsprechende Engine zu schleusen.. EditEngine& rEngine = pDoc->GetEditEngine(); if ( pObject->HasOnlineSpellErrors() ) { sal_uLong nControl = rEngine.GetControlWord(); const sal_uLong nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS; sal_Bool bNewControl = ( (nControl & nSpellControl) != nSpellControl ); if ( bNewControl ) rEngine.SetControlWord( nControl | nSpellControl ); rEngine.SetText( *pObject ); pData = rEngine.CreateTextObject(); if ( bNewControl ) rEngine.SetControlWord( nControl ); } else { rEngine.SetText( *pObject ); pData = rEngine.CreateTextObject(); } } } else pData = NULL; } // ============================================================================ namespace { using std::deque; typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&); static SCCOLROW lcl_GetCol(const ScSingleRefData& rData) { return rData.nCol; } static SCCOLROW lcl_GetRow(const ScSingleRefData& rData) { return rData.nRow; } static SCCOLROW lcl_GetTab(const ScSingleRefData& rData) { return rData.nTab; } /** Check if both references span the same range in selected dimension. */ static bool lcl_checkRangeDimension( const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2, const DimensionSelector aWhich) { return aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1) && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2); } static bool lcl_checkRangeDimensions( const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2, bool& bCol, bool& bRow, bool& bTab) { const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol)); const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow)); const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab)); // Test if exactly two dimensions are equal if (!(bSameCols ^ bSameRows ^ bSameTabs) && (bSameCols || bSameRows || bSameTabs)) { bCol = !bSameCols; bRow = !bSameRows; bTab = !bSameTabs; return true; } return false; } /** Check if references in given reference list can possibly form a range. To do that, two of their dimensions must be the same. */ static bool lcl_checkRangeDimensions( const deque::const_iterator aBegin, const deque::const_iterator aEnd, bool& bCol, bool& bRow, bool& bTab) { deque::const_iterator aCur(aBegin); ++aCur; const SingleDoubleRefProvider aRef(**aBegin); bool bOk(false); { const SingleDoubleRefProvider aRefCur(**aCur); bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab); } while (bOk && aCur != aEnd) { const SingleDoubleRefProvider aRefCur(**aCur); bool bColTmp(false); bool bRowTmp(false); bool bTabTmp(false); bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp); bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp); ++aCur; } if (bOk && aCur == aEnd) { bCol = bCol; bRow = bRow; bTab = bTab; return true; } return false; } bool lcl_lessReferenceBy( const ScToken* const pRef1, const ScToken* const pRef2, const DimensionSelector aWhich) { const SingleDoubleRefProvider rRef1(*pRef1); const SingleDoubleRefProvider rRef2(*pRef2); return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1); } /** Returns true if range denoted by token pRef2 starts immediately after range denoted by token pRef1. Dimension, in which the comparison takes place, is given by aWhich. */ bool lcl_isImmediatelyFollowing( const ScToken* const pRef1, const ScToken* const pRef2, const DimensionSelector aWhich) { const SingleDoubleRefProvider rRef1(*pRef1); const SingleDoubleRefProvider rRef2(*pRef2); return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1; } static bool lcl_checkIfAdjacent( const deque& rReferences, const DimensionSelector aWhich) { typedef deque::const_iterator Iter; Iter aBegin(rReferences.begin()); Iter aEnd(rReferences.end()); Iter aBegin1(aBegin); ++aBegin1, --aEnd; return std::equal( aBegin, aEnd, aBegin1, boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich)); } static void lcl_fillRangeFromRefList( const deque& rReferences, ScRange& rRange) { const ScSingleRefData aStart( SingleDoubleRefProvider(*rReferences.front()).Ref1); rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab); const ScSingleRefData aEnd( SingleDoubleRefProvider(*rReferences.back()).Ref2); rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab); } static bool lcl_refListFormsOneRange( const ScAddress& aPos, deque& rReferences, ScRange& rRange) { std::for_each( rReferences.begin(), rReferences.end(), bind(&ScToken::CalcAbsIfRel, _1, aPos)) ; if (rReferences.size() == 1) { lcl_fillRangeFromRefList(rReferences, rRange); return true; } bool bCell(false); bool bRow(false); bool bTab(false); if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(), bCell, bRow, bTab)) { DimensionSelector aWhich; if (bCell) { aWhich = lcl_GetCol; } else if (bRow) { aWhich = lcl_GetRow; } else if (bTab) { aWhich = lcl_GetTab; } else { OSL_ENSURE(false, "lcl_checkRangeDimensions shouldn't allow that!"); aWhich = lcl_GetRow; // initialize to avoid warning } // Sort the references by start of range std::sort(rReferences.begin(), rReferences.end(), boost::bind(lcl_lessReferenceBy, _1, _2, aWhich)); if (lcl_checkIfAdjacent(rReferences, aWhich)) { lcl_fillRangeFromRefList(rReferences, rRange); return true; } } return false; } bool lcl_isReference(const FormulaToken& rToken) { return rToken.GetType() == svSingleRef || rToken.GetType() == svDoubleRef; } } sal_Bool ScFormulaCell::IsEmpty() { if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); return aResult.GetCellResultType() == formula::svEmptyCell; } sal_Bool ScFormulaCell::IsEmptyDisplayedAsString() { if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); return aResult.IsEmptyDisplayedAsString(); } sal_Bool ScFormulaCell::IsValue() { if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); return aResult.IsValue(); } double ScFormulaCell::GetValue() { if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) && !aResult.GetResultError()) return aResult.GetDouble(); return 0.0; } double ScFormulaCell::GetValueAlways() { // for goal seek: return result value even if error code is set if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); return aResult.GetDouble(); } void ScFormulaCell::GetString( String& rString ) { if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) && !aResult.GetResultError()) rString = aResult.GetString(); else rString.Erase(); } const ScMatrix* ScFormulaCell::GetMatrix() { if ( pDocument->GetAutoCalc() ) { if( IsDirtyOrInTableOpDirty() // Was stored !bDirty but an accompanying matrix cell was bDirty? || (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is())) Interpret(); } return aResult.GetMatrix(); } sal_Bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const { switch ( cMatrixFlag ) { case MM_FORMULA : rPos = aPos; return sal_True; // break; case MM_REFERENCE : { pCode->Reset(); ScToken* t = static_cast(pCode->GetNextReferenceRPN()); if( t ) { ScSingleRefData& rRef = t->GetSingleRef(); rRef.CalcAbsIfRel( aPos ); if ( rRef.Valid() ) { rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab ); return sal_True; } } } break; } return sal_False; } /* Edge-Values: 8 4 16 2 innerhalb: 1 ausserhalb: 0 (reserviert: offen: 32) */ sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) { switch ( cMatrixFlag ) { case MM_FORMULA : case MM_REFERENCE : { static SCCOL nC; static SCROW nR; ScAddress aOrg; if ( !GetMatrixOrigin( aOrg ) ) return 0; // dumm gelaufen.. if ( aOrg != rOrgPos ) { // erstes Mal oder andere Matrix als letztes Mal rOrgPos = aOrg; ScFormulaCell* pFCell; if ( cMatrixFlag == MM_REFERENCE ) pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg ); else pFCell = this; // this MM_FORMULA // this gibt's nur einmal, kein Vergleich auf pFCell==this if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA && pFCell->cMatrixFlag == MM_FORMULA ) { pFCell->GetMatColsRows( nC, nR ); if ( nC == 0 || nR == 0 ) { // aus altem Dokument geladen, neu erzeugen nC = 1; nR = 1; ScAddress aTmpOrg; ScBaseCell* pCell; ScAddress aAdr( aOrg ); aAdr.IncCol(); sal_Bool bCont = sal_True; do { pCell = pDocument->GetCell( aAdr ); if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) { nC++; aAdr.IncCol(); } else bCont = sal_False; } while ( bCont ); aAdr = aOrg; aAdr.IncRow(); bCont = sal_True; do { pCell = pDocument->GetCell( aAdr ); if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) { nR++; aAdr.IncRow(); } else bCont = sal_False; } while ( bCont ); pFCell->SetMatColsRows( nC, nR ); } } else { #ifdef DBG_UTIL String aTmp; ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " ); aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); aMsg += ", MatOrg: "; aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); DBG_ERRORFILE( aMsg.GetBuffer() ); #endif return 0; // bad luck ... } } // here we are, healthy and clean, somewhere in between SCsCOL dC = aPos.Col() - aOrg.Col(); SCsROW dR = aPos.Row() - aOrg.Row(); sal_uInt16 nEdges = 0; if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR ) { if ( dC == 0 ) nEdges |= 4; // linke Kante if ( dC+1 == nC ) nEdges |= 16; // rechte Kante if ( dR == 0 ) nEdges |= 8; // obere Kante if ( dR+1 == nR ) nEdges |= 2; // untere Kante if ( !nEdges ) nEdges = 1; // mittendrin } #ifdef DBG_UTIL else { String aTmp; ByteString aMsg( "broken Matrix, Pos: " ); aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); aMsg += ", MatOrg: "; aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); aMsg += ", MatCols: "; aMsg += ByteString::CreateFromInt32( nC ); aMsg += ", MatRows: "; aMsg += ByteString::CreateFromInt32( nR ); aMsg += ", DiffCols: "; aMsg += ByteString::CreateFromInt32( dC ); aMsg += ", DiffRows: "; aMsg += ByteString::CreateFromInt32( dR ); DBG_ERRORFILE( aMsg.GetBuffer() ); } #endif return nEdges; // break; } default: return 0; } } sal_uInt16 ScFormulaCell::GetErrCode() { if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) Interpret(); /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors * and not also abused for signaling other error conditions we could bail * out even before attempting to interpret broken code. */ sal_uInt16 nErr = pCode->GetCodeError(); if (nErr) return nErr; return aResult.GetResultError(); } sal_uInt16 ScFormulaCell::GetRawError() { sal_uInt16 nErr = pCode->GetCodeError(); if (nErr) return nErr; return aResult.GetResultError(); } sal_Bool ScFormulaCell::HasOneReference( ScRange& r ) const { pCode->Reset(); ScToken* p = static_cast(pCode->GetNextReferenceRPN()); if( p && !pCode->GetNextReferenceRPN() ) // nur eine! { p->CalcAbsIfRel( aPos ); SingleDoubleRefProvider aProv( *p ); r.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab ); r.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab ); return sal_True; } else return sal_False; } bool ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const { /* If there appears just one reference in the formula, it's the same as HasOneReference(). If there are more of them, they can denote one range if they are (sole) arguments of one function. Union of these references must form one range and their intersection must be empty set. */ // Detect the simple case of exactly one reference in advance without all // overhead. // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference) // work again, where the function does not have only references. if (HasOneReference( rRange)) return true; pCode->Reset(); // Get first reference, if any ScToken* const pFirstReference( dynamic_cast(pCode->GetNextReferenceRPN())); if (pFirstReference) { // Collect all consecutive references, starting by the one // already found std::deque aReferences; aReferences.push_back(pFirstReference); FormulaToken* pToken(pCode->NextRPN()); FormulaToken* pFunction(0); while (pToken) { if (lcl_isReference(*pToken)) { aReferences.push_back(dynamic_cast(pToken)); pToken = pCode->NextRPN(); } else { if (pToken->IsFunction()) { pFunction = pToken; } break; } } if (pFunction && !pCode->GetNextReferenceRPN() && (pFunction->GetParamCount() == aReferences.size())) { return lcl_refListFormsOneRange(aPos, aReferences, rRange); } } return false; } sal_Bool ScFormulaCell::HasRelNameReference() const { pCode->Reset(); ScToken* t; while ( ( t = static_cast(pCode->GetNextReferenceRPN()) ) != NULL ) { if ( t->GetSingleRef().IsRelName() || (t->GetType() == formula::svDoubleRef && t->GetDoubleRef().Ref2.IsRelName()) ) return sal_True; } return sal_False; } sal_Bool ScFormulaCell::HasColRowName() const { pCode->Reset(); return (pCode->GetNextColRowName() != NULL); } void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, const ScRange& r, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ) { SCCOL nCol1 = r.aStart.Col(); SCROW nRow1 = r.aStart.Row(); SCTAB nTab1 = r.aStart.Tab(); SCCOL nCol2 = r.aEnd.Col(); SCROW nRow2 = r.aEnd.Row(); SCTAB nTab2 = r.aEnd.Tab(); SCCOL nCol = aPos.Col(); SCROW nRow = aPos.Row(); SCTAB nTab = aPos.Tab(); ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc if ( pUndoCellPos ) aUndoPos = *pUndoCellPos; ScAddress aOldPos( aPos ); // sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde sal_Bool bIsInsert = sal_False; if (eUpdateRefMode == URM_INSDEL) { bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0); if ( nDx && nRow >= nRow1 && nRow <= nRow2 && nTab >= nTab1 && nTab <= nTab2 ) { if (nCol >= nCol1) { nCol = sal::static_int_cast( nCol + nDx ); if ((SCsCOL) nCol < 0) nCol = 0; else if ( nCol > MAXCOL ) nCol = MAXCOL; aPos.SetCol( nCol ); // bPosChanged = sal_True; } } if ( nDy && nCol >= nCol1 && nCol <= nCol2 && nTab >= nTab1 && nTab <= nTab2 ) { if (nRow >= nRow1) { nRow = sal::static_int_cast( nRow + nDy ); if ((SCsROW) nRow < 0) nRow = 0; else if ( nRow > MAXROW ) nRow = MAXROW; aPos.SetRow( nRow ); // bPosChanged = sal_True; } } if ( nDz && nCol >= nCol1 && nCol <= nCol2 && nRow >= nRow1 && nRow <= nRow2 ) { if (nTab >= nTab1) { SCTAB nMaxTab = pDocument->GetTableCount() - 1; nTab = sal::static_int_cast( nTab + nDz ); if ((SCsTAB) nTab < 0) nTab = 0; else if ( nTab > nMaxTab ) nTab = nMaxTab; aPos.SetTab( nTab ); // bPosChanged = sal_True; } } } else if ( r.In( aPos ) ) { aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz ); // bPosChanged = sal_True; } sal_Bool bHasRefs = sal_False; sal_Bool bHasColRowNames = sal_False; sal_Bool bOnRefMove = sal_False; if ( !pDocument->IsClipOrUndo() ) { pCode->Reset(); bHasRefs = (pCode->GetNextReferenceRPN() != NULL); if ( !bHasRefs || eUpdateRefMode == URM_COPY ) { pCode->Reset(); bHasColRowNames = (pCode->GetNextColRowName() != NULL); bHasRefs = bHasRefs || bHasColRowNames; } bOnRefMove = pCode->IsRecalcModeOnRefMove(); } if( bHasRefs || bOnRefMove ) { ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; sal_Bool bValChanged; ScRangeData* pRangeData; sal_Bool bRangeModified; // any range, not only shared formula sal_Bool bRefSizeChanged; if ( bHasRefs ) { ScCompiler aComp(pDocument, aPos, *pCode); aComp.SetGrammar(pDocument->GetGrammar()); pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r, nDx, nDy, nDz, bValChanged, bRefSizeChanged); bRangeModified = aComp.HasModifiedRange(); } else { bValChanged = sal_False; pRangeData = NULL; bRangeModified = sal_False; bRefSizeChanged = sal_False; } if ( bOnRefMove ) bOnRefMove = (bValChanged || (aPos != aOldPos)); // Cell may reference itself, e.g. ocColumn, ocRow without parameter sal_Bool bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo; if ( bHasRefs ) { // Upon Insert ColRowNames have to be recompiled in case the // insertion occurs right in front of the range. bColRowNameCompile = (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0)); if ( bColRowNameCompile ) { bColRowNameCompile = sal_False; ScToken* t; ScRangePairList* pColList = pDocument->GetColNameRanges(); ScRangePairList* pRowList = pDocument->GetRowNameRanges(); pCode->Reset(); while ( !bColRowNameCompile && (t = static_cast(pCode->GetNextColRowName())) != NULL ) { ScSingleRefData& rRef = t->GetSingleRef(); if ( nDy > 0 && rRef.IsColRel() ) { // ColName rRef.CalcAbsIfRel( aPos ); ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); ScRangePair* pR = pColList->Find( aAdr ); if ( pR ) { // definiert if ( pR->GetRange(1).aStart.Row() == nRow1 ) bColRowNameCompile = sal_True; } else { // on the fly if ( rRef.nRow + 1 == nRow1 ) bColRowNameCompile = sal_True; } } if ( nDx > 0 && rRef.IsRowRel() ) { // RowName rRef.CalcAbsIfRel( aPos ); ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); ScRangePair* pR = pRowList->Find( aAdr ); if ( pR ) { // definiert if ( pR->GetRange(1).aStart.Col() == nCol1 ) bColRowNameCompile = sal_True; } else { // on the fly if ( rRef.nCol + 1 == nCol1 ) bColRowNameCompile = sal_True; } } } } else if ( eUpdateRefMode == URM_MOVE ) { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde // oder diese Zelle auf einen zeigt und verschoben wurde bColRowNameCompile = bCompile; // evtl. aus Copy-ctor if ( !bColRowNameCompile ) { sal_Bool bMoved = (aPos != aOldPos); pCode->Reset(); ScToken* t = static_cast(pCode->GetNextColRowName()); if ( t && bMoved ) bColRowNameCompile = sal_True; while ( t && !bColRowNameCompile ) { ScSingleRefData& rRef = t->GetSingleRef(); rRef.CalcAbsIfRel( aPos ); if ( rRef.Valid() ) { ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); if ( r.In( aAdr ) ) bColRowNameCompile = sal_True; } t = static_cast(pCode->GetNextColRowName()); } } } else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged ) { bColRowNameCompile = sal_True; } ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack(); if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) bInDeleteUndo = sal_True; else bInDeleteUndo = sal_False; // RelNameRefs are always moved bHasRelName = HasRelNameReference(); // Reference changed and new listening needed? // Except in Insert/Delete without specialties. bNewListening = (bRangeModified || pRangeData || bColRowNameCompile || (bValChanged && (eUpdateRefMode != URM_INSDEL || bInDeleteUndo || bRefSizeChanged)) || (bHasRelName && eUpdateRefMode != URM_COPY)) // #i36299# Don't duplicate action during cut&paste / drag&drop // on a cell in the range moved, start/end listeners is done // via ScDocument::DeleteArea() and ScDocument::CopyFromClip(). && !(eUpdateRefMode == URM_MOVE && pDocument->IsInsertingFromOtherDoc() && r.In(aPos)); if ( bNewListening ) EndListeningTo( pDocument, pOld, aOldPos ); } else { bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo = sal_False; } sal_Bool bNeedDirty; // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames if ( bRangeModified || pRangeData || bColRowNameCompile || (bValChanged && eUpdateRefMode != URM_COPY && (eUpdateRefMode != URM_MOVE || bHasRelName) && (!bIsInsert || bHasRelName || bInDeleteUndo || bRefSizeChanged)) || bOnRefMove) bNeedDirty = sal_True; else bNeedDirty = sal_False; if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove)) { // Copy the cell to aUndoPos, which is its current position in the document, // so this works when UpdateReference is called before moving the cells // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed). // If there is already a formula cell in the undo document, don't overwrite it, // the first (oldest) is the important cell. if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA ) { ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos, pOld, eTempGrammar, cMatrixFlag ); pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) pUndoDoc->PutCell( aUndoPos, pFCell ); } } // #i116833# If the formula is changed, always invalidate the stream (even if the result is the same). // If the formula is moved, the change is recognized separately. if (bValChanged && pDocument->IsStreamValid(aPos.Tab())) pDocument->SetStreamValid(aPos.Tab(), sal_False); bValChanged = sal_False; if ( pRangeData ) { // Replace shared formula with own formula pDocument->RemoveFromFormulaTree( this ); // update formula count delete pCode; pCode = pRangeData->GetCode()->Clone(); // #i18937# #i110008# call MoveRelWrap, but with the old position ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); ScCompiler aComp2(pDocument, aPos, *pCode); aComp2.SetGrammar(pDocument->GetGrammar()); aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r, nDx, nDy, nDz ); bValChanged = sal_True; bNeedDirty = sal_True; } if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 ) { CompileTokenArray( bNewListening ); // kein Listening bNeedDirty = sal_True; } if ( !bInDeleteUndo ) { // In ChangeTrack Delete-Reject listeners are established in // InsertCol/InsertRow if ( bNewListening ) { if ( eUpdateRefMode == URM_INSDEL ) { // Inserts/Deletes re-establish listeners after all // UpdateReference calls. // All replaced shared formula listeners have to be // established after an Insert or Delete. Do nothing here. SetNeedsListening( sal_True); } else StartListeningTo( pDocument ); } } if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) ) { // Referenzen abgeschnitten, ungueltig o.ae.? sal_Bool bOldAutoCalc = pDocument->GetAutoCalc(); // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen pDocument->SetAutoCalc( sal_False ); SetDirty(); pDocument->SetAutoCalc( bOldAutoCalc ); } delete pOld; } } void ScFormulaCell::UpdateInsertTab(SCTAB nTable) { sal_Bool bPosChanged = ( aPos.Tab() >= nTable ? sal_True : sal_False ); pCode->Reset(); if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) { EndListeningTo( pDocument ); // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab ! if ( bPosChanged ) aPos.IncTab(); ScRangeData* pRangeData; ScCompiler aComp(pDocument, aPos, *pCode); aComp.SetGrammar(pDocument->GetGrammar()); pRangeData = aComp.UpdateInsertTab( nTable, sal_False ); if (pRangeData) // Shared Formula gegen echte Formel { // austauschen sal_Bool bRefChanged; pDocument->RemoveFromFormulaTree( this ); // update formula count delete pCode; pCode = new ScTokenArray( *pRangeData->GetCode() ); ScCompiler aComp2(pDocument, aPos, *pCode); aComp2.SetGrammar(pDocument->GetGrammar()); aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); aComp2.UpdateInsertTab( nTable, sal_False ); // If the shared formula contained a named range/formula containing // an absolute reference to a sheet, those have to be readjusted. aComp2.UpdateDeleteTab( nTable, sal_False, sal_True, bRefChanged ); bCompile = sal_True; } // kein StartListeningTo weil pTab[nTab] noch nicht existiert! } else if ( bPosChanged ) aPos.IncTab(); } sal_Bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, sal_Bool bIsMove) { sal_Bool bRefChanged = sal_False; sal_Bool bPosChanged = ( aPos.Tab() > nTable ? sal_True : sal_False ); pCode->Reset(); if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) { EndListeningTo( pDocument ); // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab ! if ( bPosChanged ) aPos.IncTab(-1); ScRangeData* pRangeData; ScCompiler aComp(pDocument, aPos, *pCode); aComp.SetGrammar(pDocument->GetGrammar()); pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, sal_False, bRefChanged); if (pRangeData) // Shared Formula gegen echte Formel { // austauschen pDocument->RemoveFromFormulaTree( this ); // update formula count delete pCode; pCode = pRangeData->GetCode()->Clone(); ScCompiler aComp2(pDocument, aPos, *pCode); aComp2.SetGrammar(pDocument->GetGrammar()); aComp2.CompileTokenArray(); aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); aComp2.UpdateDeleteTab( nTable, sal_False, sal_False, bRefChanged ); // If the shared formula contained a named range/formula containing // an absolute reference to a sheet, those have to be readjusted. aComp2.UpdateInsertTab( nTable,sal_True ); // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein bRefChanged = sal_True; bCompile = sal_True; } // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! } else if ( bPosChanged ) aPos.IncTab(-1); return bRefChanged; } void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) { pCode->Reset(); if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) { EndListeningTo( pDocument ); // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab ! aPos.SetTab( nTabNo ); ScRangeData* pRangeData; ScCompiler aComp(pDocument, aPos, *pCode); aComp.SetGrammar(pDocument->GetGrammar()); pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, sal_False ); if (pRangeData) // Shared Formula gegen echte Formel { // austauschen pDocument->RemoveFromFormulaTree( this ); // update formula count delete pCode; pCode = pRangeData->GetCode()->Clone(); ScCompiler aComp2(pDocument, aPos, *pCode); aComp2.SetGrammar(pDocument->GetGrammar()); aComp2.CompileTokenArray(); aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); aComp2.UpdateMoveTab( nOldPos, nNewPos, sal_True ); bCompile = sal_True; } // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! } else aPos.SetTab( nTabNo ); } void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable) { if( !pDocument->IsClipOrUndo() ) { pCode->Reset(); ScToken* p = static_cast(pCode->GetNextReferenceRPN()); while( p ) { ScSingleRefData& rRef1 = p->GetSingleRef(); if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab ) rRef1.nTab++; if( p->GetType() == formula::svDoubleRef ) { ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab ) rRef2.nTab++; } p = static_cast(pCode->GetNextReferenceRPN()); } } } sal_Bool ScFormulaCell::TestTabRefAbs(SCTAB nTable) { sal_Bool bRet = sal_False; if( !pDocument->IsClipOrUndo() ) { pCode->Reset(); ScToken* p = static_cast(pCode->GetNextReferenceRPN()); while( p ) { ScSingleRefData& rRef1 = p->GetSingleRef(); if( !rRef1.IsTabRel() ) { if( (SCsTAB) nTable != rRef1.nTab ) bRet = sal_True; else if (nTable != aPos.Tab()) rRef1.nTab = aPos.Tab(); } if( p->GetType() == formula::svDoubleRef ) { ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; if( !rRef2.IsTabRel() ) { if( (SCsTAB) nTable != rRef2.nTab ) bRet = sal_True; else if (nTable != aPos.Tab()) rRef2.nTab = aPos.Tab(); } } p = static_cast(pCode->GetNextReferenceRPN()); } } return bRet; } void ScFormulaCell::UpdateCompile( sal_Bool bForceIfNameInUse ) { if ( bForceIfNameInUse && !bCompile ) bCompile = pCode->HasNameOrColRowName(); if ( bCompile ) pCode->SetCodeError( 0 ); // make sure it will really be compiled CompileTokenArray(); } // Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen void ScFormulaCell::TransposeReference() { sal_Bool bFound = sal_False; pCode->Reset(); ScToken* t; while ( ( t = static_cast(pCode->GetNextReference()) ) != NULL ) { ScSingleRefData& rRef1 = t->GetSingleRef(); if ( rRef1.IsColRel() && rRef1.IsRowRel() ) { sal_Bool bDouble = (t->GetType() == formula::svDoubleRef); ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1); if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) ) { sal_Int16 nTemp; nTemp = rRef1.nRelCol; rRef1.nRelCol = static_cast(rRef1.nRelRow); rRef1.nRelRow = static_cast(nTemp); if ( bDouble ) { nTemp = rRef2.nRelCol; rRef2.nRelCol = static_cast(rRef2.nRelRow); rRef2.nRelRow = static_cast(nTemp); } bFound = sal_True; } } } if (bFound) bCompile = sal_True; } void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, ScDocument* pUndoDoc ) { EndListeningTo( pDocument ); ScAddress aOldPos = aPos; sal_Bool bPosChanged = sal_False; // ob diese Zelle bewegt wurde ScRange aDestRange( rDest, ScAddress( static_cast(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()), static_cast(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()), rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) ); if ( aDestRange.In( aOldPos ) ) { // Position zurueckrechnen SCsCOL nRelPosX = aOldPos.Col(); SCsROW nRelPosY = aOldPos.Row(); SCsTAB nRelPosZ = aOldPos.Tab(); ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart ); aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ ); bPosChanged = sal_True; } ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; sal_Bool bRefChanged = sal_False; ScToken* t; ScRangeData* pShared = NULL; pCode->Reset(); while( (t = static_cast(pCode->GetNextReferenceOrName())) != NULL ) { if( t->GetOpCode() == ocName ) { ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); if (pName) { if (pName->IsModified()) bRefChanged = sal_True; if (pName->HasType(RT_SHAREDMOD)) pShared = pName; } } else if( t->GetType() != svIndex ) { t->CalcAbsIfRel( aOldPos ); sal_Bool bMod; { // own scope for SingleDoubleRefModifier dtor if SingleRef SingleDoubleRefModifier aMod( *t ); ScComplexRefData& rRef = aMod.Ref(); bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, rDest, rRef ) != UR_NOTHING || bPosChanged); } if ( bMod ) { t->CalcRelFromAbs( aPos ); bRefChanged = sal_True; } } } if (pShared) // Shared Formula gegen echte Formel austauschen { pDocument->RemoveFromFormulaTree( this ); // update formula count delete pCode; pCode = new ScTokenArray( *pShared->GetCode() ); bRefChanged = sal_True; pCode->Reset(); while( (t = static_cast(pCode->GetNextReference())) != NULL ) { if( t->GetType() != svIndex ) { t->CalcAbsIfRel( aOldPos ); sal_Bool bMod; { // own scope for SingleDoubleRefModifier dtor if SingleRef SingleDoubleRefModifier aMod( *t ); ScComplexRefData& rRef = aMod.Ref(); bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, rDest, rRef ) != UR_NOTHING || bPosChanged); } if ( bMod ) t->CalcRelFromAbs( aPos ); } } } if (bRefChanged) { if (pUndoDoc) { ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld, eTempGrammar, cMatrixFlag); pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell ); } bCompile = sal_True; CompileTokenArray(); // ruft auch StartListeningTo SetDirty(); } else StartListeningTo( pDocument ); // Listener wie vorher delete pOld; } void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) { EndListeningTo( pDocument ); sal_Bool bRefChanged = sal_False; ScToken* t; ScRangeData* pShared = NULL; pCode->Reset(); while( (t = static_cast(pCode->GetNextReferenceOrName())) != NULL ) { if( t->GetOpCode() == ocName ) { ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); if (pName) { if (pName->IsModified()) bRefChanged = sal_True; if (pName->HasType(RT_SHAREDMOD)) pShared = pName; } } else if( t->GetType() != svIndex ) { t->CalcAbsIfRel( aPos ); sal_Bool bMod; { // own scope for SingleDoubleRefModifier dtor if SingleRef SingleDoubleRefModifier aMod( *t ); ScComplexRefData& rRef = aMod.Ref(); bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, rRef ) != UR_NOTHING); } if ( bMod ) { t->CalcRelFromAbs( aPos ); bRefChanged = sal_True; } } } if (pShared) // Shared Formula gegen echte Formel austauschen { pDocument->RemoveFromFormulaTree( this ); // update formula count delete pCode; pCode = new ScTokenArray( *pShared->GetCode() ); bRefChanged = sal_True; pCode->Reset(); while( (t = static_cast(pCode->GetNextReference())) != NULL ) { if( t->GetType() != svIndex ) { t->CalcAbsIfRel( aPos ); sal_Bool bMod; { // own scope for SingleDoubleRefModifier dtor if SingleRef SingleDoubleRefModifier aMod( *t ); ScComplexRefData& rRef = aMod.Ref(); bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, rRef ) != UR_NOTHING); } if ( bMod ) t->CalcRelFromAbs( aPos ); } } } if (bRefChanged) { bCompile = sal_True; CompileTokenArray(); // ruft auch StartListeningTo SetDirty(); } else StartListeningTo( pDocument ); // Listener wie vorher } sal_Bool lcl_IsRangeNameInUse(sal_uInt16 nIndex, ScTokenArray* pCode, ScRangeName* pNames) { for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) { if (p->GetOpCode() == ocName) { if (p->GetIndex() == nIndex) return sal_True; else { // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#) ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); if (pSubName && lcl_IsRangeNameInUse(nIndex, pSubName->GetCode(), pNames)) return sal_True; } } } return sal_False; } sal_Bool ScFormulaCell::IsRangeNameInUse(sal_uInt16 nIndex) const { return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() ); } void lcl_FindRangeNamesInUse(std::set& rIndexes, ScTokenArray* pCode, ScRangeName* pNames) { for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) { if (p->GetOpCode() == ocName) { sal_uInt16 nTokenIndex = p->GetIndex(); rIndexes.insert( nTokenIndex ); ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); if (pSubName) lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames); } } } void ScFormulaCell::FindRangeNamesInUse(std::set& rIndexes) const { lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() ); } void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap ) { for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) { if( p->GetOpCode() == ocName ) { sal_uInt16 nIndex = p->GetIndex(); ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex); sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second; if ( nIndex != nNewIndex ) { p->SetIndex( nNewIndex ); bCompile = sal_True; } } } if( bCompile ) CompileTokenArray(); } void ScFormulaCell::CompileDBFormula() { for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) { if ( p->GetOpCode() == ocDBArea || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) ) { bCompile = sal_True; CompileTokenArray(); SetDirty(); break; } } } void ScFormulaCell::CompileDBFormula( sal_Bool bCreateFormulaString ) { // zwei Phasen, muessen (!) nacheinander aufgerufen werden: // 1. FormelString mit alten Namen erzeugen // 2. FormelString mit neuen Namen kompilieren if ( bCreateFormulaString ) { sal_Bool bRecompile = sal_False; pCode->Reset(); for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) { switch ( p->GetOpCode() ) { case ocBad: // DB-Bereich evtl. zugefuegt case ocColRowName: // #36762# falls Namensgleichheit case ocDBArea: // DB-Bereich bRecompile = sal_True; break; case ocName: if ( p->GetIndex() >= SC_START_INDEX_DB_COLL ) bRecompile = sal_True; // DB-Bereich break; default: ; // nothing } } if ( bRecompile ) { String aFormula; GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) { if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) aFormula.Erase( aFormula.Len()-1 , 1 ); if ( aFormula.GetChar(0) == '{' ) aFormula.Erase( 0, 1 ); } EndListeningTo( pDocument ); pDocument->RemoveFromFormulaTree( this ); pCode->Clear(); SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); } } else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) { Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar ); aResult.SetToken( NULL); SetDirty(); } } void ScFormulaCell::CompileNameFormula( sal_Bool bCreateFormulaString ) { // zwei Phasen, muessen (!) nacheinander aufgerufen werden: // 1. FormelString mit alten RangeNames erzeugen // 2. FormelString mit neuen RangeNames kompilieren if ( bCreateFormulaString ) { sal_Bool bRecompile = sal_False; pCode->Reset(); for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) { switch ( p->GetOpCode() ) { case ocBad: // RangeName evtl. zugefuegt case ocColRowName: // #36762# falls Namensgleichheit bRecompile = sal_True; break; default: if ( p->GetType() == svIndex ) bRecompile = sal_True; // RangeName } } if ( bRecompile ) { String aFormula; GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) { if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) aFormula.Erase( aFormula.Len()-1 , 1 ); if ( aFormula.GetChar(0) == '{' ) aFormula.Erase( 0, 1 ); } EndListeningTo( pDocument ); pDocument->RemoveFromFormulaTree( this ); pCode->Clear(); SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); } } else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) { Compile( aResult.GetHybridFormula(), sal_False, eTempGrammar ); aResult.SetToken( NULL); SetDirty(); } } void ScFormulaCell::CompileColRowNameFormula() { pCode->Reset(); for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) { if ( p->GetOpCode() == ocColRowName ) { bCompile = sal_True; CompileTokenArray(); SetDirty(); break; } } } // ============================================================================