summaryrefslogtreecommitdiff
path: root/sc/source/core
diff options
context:
space:
mode:
authorKohei Yoshida <kohei.yoshida@gmail.com>2013-03-28 15:20:14 -0400
committerKohei Yoshida <kohei.yoshida@gmail.com>2013-03-28 17:03:56 -0400
commit428b0aeceecbccb7558a246eda090ececd28d7c5 (patch)
treeb0121f061abab23e83d39860f45ff4d02f250628 /sc/source/core
parent3d6a7391466cf259127a9c3a9cd40a86fb85b719 (diff)
Move ScFormulaCell into its own header source files: formulacell.?xx.
Change-Id: I65f2cb12c06784b5bcf6c6a2fad773825b6c409c
Diffstat (limited to 'sc/source/core')
-rw-r--r--sc/source/core/data/attarray.cxx2
-rw-r--r--sc/source/core/data/autonamecache.cxx2
-rw-r--r--sc/source/core/data/cell.cxx1459
-rw-r--r--sc/source/core/data/cell2.cxx1749
-rw-r--r--sc/source/core/data/cellvalue.cxx1
-rw-r--r--sc/source/core/data/colorscale.cxx2
-rw-r--r--sc/source/core/data/column.cxx1
-rw-r--r--sc/source/core/data/column2.cxx1
-rw-r--r--sc/source/core/data/column3.cxx10
-rw-r--r--sc/source/core/data/conditio.cxx2
-rw-r--r--sc/source/core/data/dociter.cxx1
-rw-r--r--sc/source/core/data/documen2.cxx1
-rw-r--r--sc/source/core/data/documen3.cxx1
-rw-r--r--sc/source/core/data/documen4.cxx1
-rw-r--r--sc/source/core/data/documen7.cxx1
-rw-r--r--sc/source/core/data/document.cxx1
-rw-r--r--sc/source/core/data/dpfilteredcache.cxx2
-rw-r--r--sc/source/core/data/dpitemdata.cxx2
-rw-r--r--sc/source/core/data/dpshttab.cxx2
-rw-r--r--sc/source/core/data/dptabsrc.cxx2
-rw-r--r--sc/source/core/data/fillinfo.cxx2
-rw-r--r--sc/source/core/data/formulacell.cxx3208
-rw-r--r--sc/source/core/data/formulaiter.cxx2
-rw-r--r--sc/source/core/data/postit.cxx2
-rw-r--r--sc/source/core/data/table1.cxx2
-rw-r--r--sc/source/core/data/table2.cxx2
-rw-r--r--sc/source/core/data/table3.cxx2
-rw-r--r--sc/source/core/data/table4.cxx2
-rw-r--r--sc/source/core/data/table5.cxx2
-rw-r--r--sc/source/core/data/table6.cxx2
-rw-r--r--sc/source/core/data/validat.cxx2
-rw-r--r--sc/source/core/tool/cellform.cxx2
-rw-r--r--sc/source/core/tool/chartarr.cxx2
-rw-r--r--sc/source/core/tool/chgtrack.cxx2
-rw-r--r--sc/source/core/tool/compiler.cxx2
-rw-r--r--sc/source/core/tool/consoli.cxx2
-rw-r--r--sc/source/core/tool/detfunc.cxx2
-rw-r--r--sc/source/core/tool/doubleref.cxx2
-rw-r--r--sc/source/core/tool/interpr1.cxx2
-rw-r--r--sc/source/core/tool/interpr2.cxx2
-rw-r--r--sc/source/core/tool/interpr3.cxx2
-rw-r--r--sc/source/core/tool/interpr4.cxx2
-rw-r--r--sc/source/core/tool/interpr5.cxx2
-rw-r--r--sc/source/core/tool/rangeseq.cxx2
44 files changed, 3254 insertions, 3243 deletions
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx
index 29522130e0f1..d52bcc69a258 100644
--- a/sc/source/core/data/attarray.cxx
+++ b/sc/source/core/data/attarray.cxx
@@ -40,7 +40,7 @@
#include "rechead.hxx"
#include "globstr.hrc"
#include "segmenttree.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "cellvalue.hxx"
#include "editutil.hxx"
#include <rtl/strbuf.hxx>
diff --git a/sc/source/core/data/autonamecache.cxx b/sc/source/core/data/autonamecache.cxx
index 6629c510555c..1dca11e62afe 100644
--- a/sc/source/core/data/autonamecache.cxx
+++ b/sc/source/core/data/autonamecache.cxx
@@ -22,7 +22,7 @@
#include "autonamecache.hxx"
#include "dociter.hxx"
#include "queryparam.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "cellvalue.hxx"
#include "editutil.hxx"
#include "document.hxx"
diff --git a/sc/source/core/data/cell.cxx b/sc/source/core/data/cell.cxx
index 66032a8471d8..aa6e2a215ba1 100644
--- a/sc/source/core/data/cell.cxx
+++ b/sc/source/core/data/cell.cxx
@@ -19,45 +19,15 @@
#include "cell.hxx"
-#include "scitems.hxx"
-#include "attrib.hxx"
-#include "compiler.hxx"
-#include "interpre.hxx"
#include "document.hxx"
-#include "docoptio.hxx"
-#include "rechead.hxx"
-#include "rangenam.hxx"
-#include "brdcst.hxx"
-#include "ddelink.hxx"
-#include "validat.hxx"
-#include "progress.hxx"
-#include "editutil.hxx"
-#include "recursionhelper.hxx"
-#include "externalrefmgr.hxx"
-#include "macromgr.hxx"
-#include "dbdata.hxx"
-#include "globalnames.hxx"
-#include "cellvalue.hxx"
+#include "formulacell.hxx"
-#include <svl/intitem.hxx>
#include <svl/broadcast.hxx>
-#include <svl/zforlist.hxx>
-
-using namespace formula;
-// More or less arbitrary, of course all recursions must fit into available
-// stack space (which is what on all systems we don't know yet?). Choosing a
-// lower value may be better than trying a much higher value that also isn't
-// sufficient but temporarily leads to high memory consumption. On the other
-// hand, if the value fits all recursions, execution is quicker as no resumes
-// are necessary. Could be made a configurable option.
-// Allow for a year's calendar (366).
-const sal_uInt16 MAXRECURSION = 400;
// STATIC DATA -----------------------------------------------------------
#ifdef USE_MEMPOOL
IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell )
-IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell )
IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell )
IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell )
#endif
@@ -104,108 +74,6 @@ ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, cons
return 0;
}
-void adjustRangeName(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc, const ScAddress& aNewPos, const ScAddress& aOldPos)
-{
- bool bOldGlobal = pToken->IsGlobal();
- SCTAB aOldTab = aOldPos.Tab();
- rtl::OUString aRangeName;
- int nOldIndex = pToken->GetIndex();
- ScRangeData* pOldRangeData = NULL;
-
- //search the name of the RangeName
- if (!bOldGlobal)
- {
- pOldRangeData = pOldDoc->GetRangeName(aOldTab)->findByIndex(nOldIndex);
- if (!pOldRangeData)
- return; //might be an error in the formula array
- aRangeName = pOldRangeData->GetUpperName();
- }
- else
- {
- pOldRangeData = pOldDoc->GetRangeName()->findByIndex(nOldIndex);
- if (!pOldRangeData)
- return; //might be an error in the formula array
- aRangeName = pOldRangeData->GetUpperName();
- }
-
- //find corresponding range name in new document
- //first search for local range name then global range names
- SCTAB aNewTab = aNewPos.Tab();
- ScRangeName* pRangeName = rNewDoc.GetRangeName(aNewTab);
- ScRangeData* pRangeData = NULL;
- bool bNewGlobal = false;
- //search local range names
- if (pRangeName)
- {
- pRangeData = pRangeName->findByUpperName(aRangeName);
- }
- //search global range names
- if (!pRangeData)
- {
- bNewGlobal = true;
- pRangeName = rNewDoc.GetRangeName();
- if (pRangeName)
- pRangeData = pRangeName->findByUpperName(aRangeName);
- }
- //if no range name was found copy it
- if (!pRangeData)
- {
- bNewGlobal = bOldGlobal;
- pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc);
- ScTokenArray* pRangeNameToken = pRangeData->GetCode();
- if (rNewDoc.GetPool() != const_cast<ScDocument*>(pOldDoc)->GetPool())
- {
- pRangeNameToken->ReadjustAbsolute3DReferences(pOldDoc, &rNewDoc, pRangeData->GetPos(), true);
- pRangeNameToken->AdjustAbsoluteRefs(pOldDoc, aOldPos, aNewPos, false, true);
- }
-
- bool bInserted;
- if (bNewGlobal)
- bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
- else
- bInserted = rNewDoc.GetRangeName(aNewTab)->insert(pRangeData);
- if (!bInserted)
- {
- //if this happened we have a real problem
- pRangeData = NULL;
- pToken->SetIndex(0);
- OSL_FAIL("inserting the range name should not fail");
- return;
- }
- }
- sal_Int32 nIndex = pRangeData->GetIndex();
- pToken->SetIndex(nIndex);
- pToken->SetGlobal(bNewGlobal);
-}
-
-void adjustDBRange(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
-{
- ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
- if (!pOldDBCollection)
- return;//strange error case, don't do anything
- ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
- ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
- if (!pDBData)
- return; //invalid index
- rtl::OUString aDBName = pDBData->GetUpperName();
-
- //search in new document
- ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
- if (!pNewDBCollection)
- {
- pNewDBCollection = new ScDBCollection(&rNewDoc);
- rNewDoc.SetDBCollection(pNewDBCollection);
- }
- ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
- ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
- if (!pNewDBData)
- {
- pNewDBData = new ScDBData(*pDBData);
- aNewNamedDBs.insert(pNewDBData);
- }
- pToken->SetIndex(pNewDBData->GetIndex());
-}
-
} // namespace
ScBaseCell* ScBaseCell::Clone( ScDocument& rDestDoc, int nCloneFlags ) const
@@ -388,1329 +256,4 @@ ScStringCell::~ScStringCell()
}
#endif
-// ============================================================================
-
-//
-// ScFormulaCell
-//
-
-ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
- const rtl::OUString& rFormula,
- const FormulaGrammar::Grammar eGrammar,
- sal_uInt8 cMatInd ) :
- ScBaseCell( CELLTYPE_FORMULA ),
- eTempGrammar( eGrammar),
- pCode( NULL ),
- pDocument( pDoc ),
- pPrevious(0),
- pNext(0),
- pPreviousTrack(0),
- pNextTrack(0),
- nFormatIndex(0),
- nFormatType( NUMBERFORMAT_NUMBER ),
- nSeenInIteration(0),
- cMatrixFlag ( cMatInd ),
- bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
- bChanged( false ),
- bRunning( false ),
- bCompile( false ),
- bSubTotal( false ),
- bIsIterCell( false ),
- bInChangeTrack( false ),
- bTableOpDirty( false ),
- bNeedListening( false ),
- aPos( rPos )
-{
- Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
- if (!pCode)
- // We need to have a non-NULL token array instance at all times.
- pCode = new ScTokenArray;
-}
-
-// Used by import filters
-
-ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
- const ScTokenArray* pArr,
- const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) :
- ScBaseCell( CELLTYPE_FORMULA ),
- eTempGrammar( eGrammar),
- pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ),
- pDocument( pDoc ),
- pPrevious(0),
- pNext(0),
- pPreviousTrack(0),
- pNextTrack(0),
- nFormatIndex(0),
- nFormatType( NUMBERFORMAT_NUMBER ),
- nSeenInIteration(0),
- cMatrixFlag ( cInd ),
- bDirty( NULL != pArr ), // -> Because of the use of the Auto Pilot Function was: cInd != 0
- bChanged( false ),
- bRunning( false ),
- bCompile( false ),
- bSubTotal( false ),
- bIsIterCell( false ),
- bInChangeTrack( false ),
- bTableOpDirty( false ),
- bNeedListening( false ),
- aPos( rPos )
-{
- // UPN-Array generation
- if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
- {
- ScCompiler aComp( pDocument, aPos, *pCode);
- aComp.SetGrammar(eTempGrammar);
- bSubTotal = aComp.CompileTokenArray();
- nFormatType = aComp.GetNumFormatType();
- }
- else
- {
- pCode->Reset();
- if ( pCode->GetNextOpCodeRPN( ocSubTotal ) )
- bSubTotal = true;
- }
-
- if (bSubTotal)
- pDocument->AddSubTotalCell(this);
-}
-
-ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
- ScBaseCell( rCell ),
- SvtListener(),
- aResult( rCell.aResult ),
- eTempGrammar( rCell.eTempGrammar),
- pDocument( &rDoc ),
- pPrevious(0),
- pNext(0),
- pPreviousTrack(0),
- pNextTrack(0),
- nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ),
- nFormatType( rCell.nFormatType ),
- nSeenInIteration(0),
- cMatrixFlag ( rCell.cMatrixFlag ),
- bDirty( rCell.bDirty ),
- bChanged( rCell.bChanged ),
- bRunning( false ),
- bCompile( rCell.bCompile ),
- bSubTotal( rCell.bSubTotal ),
- bIsIterCell( false ),
- bInChangeTrack( false ),
- bTableOpDirty( false ),
- bNeedListening( false ),
- aPos( rPos )
-{
- pCode = rCell.pCode->Clone();
-
- // set back any errors and recompile
- // not in the Clipboard - it must keep the received error flag
- // Special Length=0: as bad cells are generated, then they are also retained
- if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
- {
- pCode->SetCodeError( 0 );
- bCompile = true;
- }
- //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
- bool bCompileLater = false;
- bool bClipMode = rCell.pDocument->IsClipboard();
-
- //update ScNameTokens
- if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
- {
- if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
- {
- ScToken* pToken = NULL;
- while((pToken = static_cast<ScToken*>(pCode->GetNextName()))!= NULL)
- {
- OpCode eOpCode = pToken->GetOpCode();
- if (eOpCode == ocName)
- adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos);
- else if (eOpCode == ocDBArea)
- adjustDBRange(pToken, rDoc, rCell.pDocument);
- }
- }
-
- bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
- if (bCopyBetweenDocs && !(nCloneFlags & SC_CLONECELL_NOMAKEABS_EXTERNAL))
- {
- pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
- }
-
- pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, false, bCopyBetweenDocs );
- }
-
- if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
- pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
-
- if( !bCompile )
- { // Name references with references and ColRowNames
- pCode->Reset();
- ScToken* t;
- while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
- {
- if ( t->IsExternalRef() )
- {
- // External name, cell, and area references.
- bCompile = true;
- }
- else if ( t->GetType() == svIndex )
- {
- ScRangeData* pRangeData = rDoc.GetRangeName()->findByIndex( t->GetIndex() );
- if( pRangeData )
- {
- if( pRangeData->HasReferences() )
- bCompile = true;
- }
- else
- bCompile = true; // invalid reference!
- }
- else if ( t->GetOpCode() == ocColRowName )
- {
- bCompile = true; // new lookup needed
- bCompileLater = bClipMode;
- }
- }
- }
- if( bCompile )
- {
- if ( !bCompileLater && bClipMode )
- {
- // Merging ranges needs the actual positions after UpdateReference.
- // ColRowNames need new lookup after positions are adjusted.
- bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName);
- }
- if ( !bCompileLater )
- {
- // bNoListening, not at all if in Clipboard/Undo,
- // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
- CompileTokenArray( true );
- }
- }
-
- if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
- StartListeningTo( &rDoc );
-
- if (bSubTotal)
- pDocument->AddSubTotalCell(this);
-}
-
-ScFormulaCell::~ScFormulaCell()
-{
- pDocument->RemoveFromFormulaTree( this );
- pDocument->RemoveSubTotalCell(this);
- if (pCode->HasOpCode(ocMacro))
- pDocument->GetMacroManager()->RemoveDependentCell(this);
-
- if (pDocument->HasExternalRefManager())
- pDocument->GetExternalRefManager()->removeRefCell(this);
-
- delete pCode;
-#if OSL_DEBUG_LEVEL > 0
- eCellType = CELLTYPE_DESTROYED;
-#endif
-}
-
-ScFormulaCell* ScFormulaCell::Clone() const
-{
- return new ScFormulaCell(*this, *pDocument, aPos);
-}
-
-size_t ScFormulaCell::GetHash() const
-{
- return pCode->GetHash();
-}
-
-ScFormulaVectorState ScFormulaCell::GetVectorState() const
-{
- return pCode->GetVectorState();
-}
-
-void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer,
- const FormulaGrammar::Grammar eGrammar ) const
-{
- if( pCode->GetCodeError() && !pCode->GetLen() )
- {
- rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
- return;
- }
- else if( cMatrixFlag == MM_REFERENCE )
- {
- // Reference to another cell that contains a matrix formula.
- pCode->Reset();
- ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
- if( p )
- {
- /* FIXME: original GetFormula() code obtained
- * pCell only if (!this->IsInChangeTrack()),
- * GetEnglishFormula() omitted that test.
- * Can we live without in all cases? */
- ScFormulaCell* pCell = NULL;
- ScSingleRefData& rRef = p->GetSingleRef();
- rRef.CalcAbsIfRel( aPos );
- if ( rRef.Valid() )
- pCell = pDocument->GetFormulaCell(
- ScAddress(rRef.nCol, rRef.nRow, rRef.nTab));
-
- if (pCell)
- {
- pCell->GetFormula( rBuffer, eGrammar);
- return;
- }
- else
- {
- ScCompiler aComp( pDocument, aPos, *pCode);
- aComp.SetGrammar(eGrammar);
- aComp.CreateStringFromTokenArray( rBuffer );
- }
- }
- else
- {
- OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
- }
- }
- else
- {
- ScCompiler aComp( pDocument, aPos, *pCode);
- aComp.SetGrammar(eGrammar);
- aComp.CreateStringFromTokenArray( rBuffer );
- }
-
- sal_Unicode ch('=');
- rBuffer.insert( 0, &ch, 1 );
- if( cMatrixFlag )
- {
- sal_Unicode ch2('{');
- rBuffer.insert( 0, &ch2, 1);
- rBuffer.append( sal_Unicode('}'));
- }
-}
-
-void ScFormulaCell::GetFormula( rtl::OUString& rFormula, const FormulaGrammar::Grammar eGrammar ) const
-{
- rtl::OUStringBuffer rBuffer( rFormula );
- GetFormula( rBuffer, eGrammar );
- rFormula = rBuffer.makeStringAndClear();
-}
-
-void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
-{
- MaybeInterpret();
-
- const ScMatrix* pMat = NULL;
- if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell &&
- ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0))
- pMat->GetDimensions( rCols, rRows );
- else
- {
- rCols = 0;
- rRows = 0;
- }
-}
-
-void ScFormulaCell::Compile( const rtl::OUString& rFormula, bool bNoListening,
- const FormulaGrammar::Grammar eGrammar )
-{
- if ( pDocument->IsClipOrUndo() )
- return;
- bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
- if ( bWasInFormulaTree )
- pDocument->RemoveFromFormulaTree( this );
- // pCode may not deleted for queries, but must be empty
- if ( pCode )
- pCode->Clear();
- ScTokenArray* pCodeOld = pCode;
- ScCompiler aComp( pDocument, aPos);
- aComp.SetGrammar(eGrammar);
- pCode = aComp.CompileString( rFormula );
- if ( pCodeOld )
- delete pCodeOld;
- if( !pCode->GetCodeError() )
- {
- if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
- { // not recursive CompileTokenArray/Compile/CompileTokenArray
- if ( rFormula[0] == '=' )
- pCode->AddBad( rFormula.copy(1) );
- else
- pCode->AddBad( rFormula );
- }
- bCompile = true;
- CompileTokenArray( bNoListening );
- }
- else
- {
- bChanged = true;
- pDocument->SetTextWidth(aPos, TEXTWIDTH_DIRTY);
- pDocument->SetScriptType(aPos, SC_SCRIPTTYPE_UNKNOWN);
- }
- if ( bWasInFormulaTree )
- pDocument->PutInFormulaTree( this );
-}
-
-
-void ScFormulaCell::CompileTokenArray( bool bNoListening )
-{
- // Not already compiled?
- if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
- Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
- else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
- {
- // RPN length may get changed
- bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
- if ( bWasInFormulaTree )
- pDocument->RemoveFromFormulaTree( this );
-
- // Loading from within filter? No listening yet!
- if( pDocument->IsInsertingFromOtherDoc() )
- bNoListening = true;
-
- if( !bNoListening && pCode->GetCodeLen() )
- EndListeningTo( pDocument );
- ScCompiler aComp(pDocument, aPos, *pCode);
- aComp.SetGrammar(pDocument->GetGrammar());
- bSubTotal = aComp.CompileTokenArray();
- if( !pCode->GetCodeError() )
- {
- nFormatType = aComp.GetNumFormatType();
- nFormatIndex = 0;
- bChanged = true;
- aResult.SetToken( NULL);
- bCompile = false;
- if ( !bNoListening )
- StartListeningTo( pDocument );
- }
- if ( bWasInFormulaTree )
- pDocument->PutInFormulaTree( this );
-
- if (bSubTotal)
- pDocument->AddSubTotalCell(this);
- }
-}
-
-
-void ScFormulaCell::CompileXML( ScProgress& rProgress )
-{
- if ( cMatrixFlag == MM_REFERENCE )
- { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
- // just establish listeners
- StartListeningTo( pDocument );
- return ;
- }
-
- ScCompiler aComp( pDocument, aPos, *pCode);
- aComp.SetGrammar(eTempGrammar);
- rtl::OUString aFormula, aFormulaNmsp;
- aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
- pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
- rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
- // pCode may not deleted for queries, but must be empty
- if ( pCode )
- pCode->Clear();
- ScTokenArray* pCodeOld = pCode;
- pCode = aComp.CompileString( aFormula, aFormulaNmsp );
- delete pCodeOld;
- if( !pCode->GetCodeError() )
- {
- if ( !pCode->GetLen() )
- {
- if ( aFormula[0] == '=' )
- pCode->AddBad( aFormula.copy( 1 ) );
- else
- pCode->AddBad( aFormula );
- }
- bSubTotal = aComp.CompileTokenArray();
- if( !pCode->GetCodeError() )
- {
- nFormatType = aComp.GetNumFormatType();
- nFormatIndex = 0;
- bChanged = true;
- bCompile = false;
- StartListeningTo( pDocument );
- }
-
- if (bSubTotal)
- pDocument->AddSubTotalCell(this);
- }
- else
- {
- bChanged = true;
- pDocument->SetTextWidth(aPos, TEXTWIDTH_DIRTY);
- pDocument->SetScriptType(aPos, SC_SCRIPTTYPE_UNKNOWN);
- }
-
- // Same as in Load: after loading, it must be known if ocMacro is in any formula
- // (for macro warning, CompileXML is called at the end of loading XML file)
- if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
- pDocument->SetHasMacroFunc( true );
-
- //volatile cells must be added here for import
- if( pCode->IsRecalcModeAlways() || pCode->IsRecalcModeForced() ||
- pCode->IsRecalcModeOnLoad() || pCode->IsRecalcModeOnLoadOnce() )
- {
- // During load, only those cells that are marked explicitly dirty get
- // recalculated. So we need to set it dirty here.
- SetDirtyVar();
- pDocument->PutInFormulaTree(this);
- }
-}
-
-
-void ScFormulaCell::CalcAfterLoad()
-{
- bool bNewCompiled = false;
- // If a Calc 1.0-doc is read, we have a result, but no token array
- if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
- {
- Compile( aResult.GetHybridFormula(), true, eTempGrammar);
- aResult.SetToken( NULL);
- bDirty = true;
- bNewCompiled = true;
- }
- // The UPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
- if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
- {
- ScCompiler aComp(pDocument, aPos, *pCode);
- aComp.SetGrammar(pDocument->GetGrammar());
- bSubTotal = aComp.CompileTokenArray();
- nFormatType = aComp.GetNumFormatType();
- nFormatIndex = 0;
- bDirty = true;
- bCompile = false;
- bNewCompiled = true;
-
- if (bSubTotal)
- pDocument->AddSubTotalCell(this);
- }
-
- // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
- // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
- // We iron this out here for all systems, such that we also have an Err503 here.
- if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
- {
- OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
- aResult.SetResultError( errIllegalFPOperation );
- bDirty = true;
- }
-
- // DoubleRefs for binary operators were always a Matrix before version v5.0.
- // Now this is only the case when when in an array formula, otherwise it's an implicit intersection
- if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
- GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
- {
- cMatrixFlag = MM_FORMULA;
- SetMatColsRows( 1, 1);
- }
-
- // Do the cells need to be calculated? After Load cells can contain an error code, and then start
- // the listener and Recalculate (if needed) if not RECALCMODE_NORMAL
- if( !bNewCompiled || !pCode->GetCodeError() )
- {
- StartListeningTo( pDocument );
- if( !pCode->IsRecalcModeNormal() )
- bDirty = true;
- }
- if ( pCode->IsRecalcModeAlways() )
- { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
- // for each F9
- bDirty = true;
- }
- // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
-}
-
-
-bool ScFormulaCell::MarkUsedExternalReferences()
-{
- return pCode && pDocument->MarkUsedExternalReferences( *pCode);
-}
-
-
-void ScFormulaCell::Interpret()
-{
- if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
- return; // no double/triple processing
-
- //! HACK:
- // If the call originates from a Reschedule in DdeLink update, leave dirty
- // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
- if ( pDocument->IsInDdeLinkUpdate() )
- return;
-
- if (bRunning)
- {
- if (!pDocument->GetDocOptions().IsIter())
- {
- aResult.SetResultError( errCircularReference );
- return;
- }
-
- if (aResult.GetResultError() == errCircularReference)
- aResult.SetResultError( 0 );
-
- // Start or add to iteration list.
- if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
- !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
- pDocument->GetRecursionHelper().SetInIterationReturn( true);
-
- return;
- }
- // no multiple interprets for GetErrCode, IsValue, GetValue and
- // different entry point recursions. Would also lead to premature
- // convergence in iterations.
- if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
- pDocument->GetRecursionHelper().GetIteration())
- return ;
-
- ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
- bool bOldRunning = bRunning;
- if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
- {
- bRunning = true;
- rRecursionHelper.SetInRecursionReturn( true);
- }
- else
- {
- if ( ! InterpretFormulaGroup() )
- InterpretTail( SCITP_NORMAL);
- }
-
- // While leaving a recursion or iteration stack, insert its cells to the
- // recursion list in reverse order.
- if (rRecursionHelper.IsInReturn())
- {
- if (rRecursionHelper.GetRecursionCount() > 0 ||
- !rRecursionHelper.IsDoingRecursion())
- rRecursionHelper.Insert( this, bOldRunning, aResult);
- bool bIterationFromRecursion = false;
- bool bResumeIteration = false;
- do
- {
- if ((rRecursionHelper.IsInIterationReturn() &&
- rRecursionHelper.GetRecursionCount() == 0 &&
- !rRecursionHelper.IsDoingIteration()) ||
- bIterationFromRecursion || bResumeIteration)
- {
- ScFormulaCell* pIterCell = this; // scope for debug convenience
- bool & rDone = rRecursionHelper.GetConvergingReference();
- rDone = false;
- if (!bIterationFromRecursion && bResumeIteration)
- {
- bResumeIteration = false;
- // Resuming iteration expands the range.
- ScFormulaRecursionList::const_iterator aOldStart(
- rRecursionHelper.GetLastIterationStart());
- rRecursionHelper.ResumeIteration();
- // Mark new cells being in iteration.
- for (ScFormulaRecursionList::const_iterator aIter(
- rRecursionHelper.GetIterationStart()); aIter !=
- aOldStart; ++aIter)
- {
- pIterCell = (*aIter).pCell;
- pIterCell->bIsIterCell = true;
- }
- // Mark older cells dirty again, in case they converted
- // without accounting for all remaining cells in the circle
- // that weren't touched so far, e.g. conditional. Restore
- // backuped result.
- sal_uInt16 nIteration = rRecursionHelper.GetIteration();
- for (ScFormulaRecursionList::const_iterator aIter(
- aOldStart); aIter !=
- rRecursionHelper.GetIterationEnd(); ++aIter)
- {
- pIterCell = (*aIter).pCell;
- if (pIterCell->nSeenInIteration == nIteration)
- {
- if (!pIterCell->bDirty || aIter == aOldStart)
- {
- pIterCell->aResult = (*aIter).aPreviousResult;
- }
- --pIterCell->nSeenInIteration;
- }
- pIterCell->bDirty = true;
- }
- }
- else
- {
- bResumeIteration = false;
- // Close circle once.
- rRecursionHelper.GetList().back().pCell->InterpretTail(
- SCITP_CLOSE_ITERATION_CIRCLE);
- // Start at 1, init things.
- rRecursionHelper.StartIteration();
- // Mark all cells being in iteration.
- for (ScFormulaRecursionList::const_iterator aIter(
- rRecursionHelper.GetIterationStart()); aIter !=
- rRecursionHelper.GetIterationEnd(); ++aIter)
- {
- pIterCell = (*aIter).pCell;
- pIterCell->bIsIterCell = true;
- }
- }
- bIterationFromRecursion = false;
- sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
- for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
- rRecursionHelper.IncIteration())
- {
- rDone = true;
- for ( ScFormulaRecursionList::iterator aIter(
- rRecursionHelper.GetIterationStart()); aIter !=
- rRecursionHelper.GetIterationEnd() &&
- !rRecursionHelper.IsInReturn(); ++aIter)
- {
- pIterCell = (*aIter).pCell;
- if (pIterCell->IsDirtyOrInTableOpDirty() &&
- rRecursionHelper.GetIteration() !=
- pIterCell->GetSeenInIteration())
- {
- (*aIter).aPreviousResult = pIterCell->aResult;
- pIterCell->InterpretTail( SCITP_FROM_ITERATION);
- }
- rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
- }
- if (rRecursionHelper.IsInReturn())
- {
- bResumeIteration = true;
- break; // for
- // Don't increment iteration.
- }
- }
- if (!bResumeIteration)
- {
- if (rDone)
- {
- for (ScFormulaRecursionList::const_iterator aIter(
- rRecursionHelper.GetIterationStart());
- aIter != rRecursionHelper.GetIterationEnd();
- ++aIter)
- {
- pIterCell = (*aIter).pCell;
- pIterCell->bIsIterCell = false;
- pIterCell->nSeenInIteration = 0;
- pIterCell->bRunning = (*aIter).bOldRunning;
- }
- }
- else
- {
- for (ScFormulaRecursionList::const_iterator aIter(
- rRecursionHelper.GetIterationStart());
- aIter != rRecursionHelper.GetIterationEnd();
- ++aIter)
- {
- pIterCell = (*aIter).pCell;
- pIterCell->bIsIterCell = false;
- pIterCell->nSeenInIteration = 0;
- pIterCell->bRunning = (*aIter).bOldRunning;
- // If one cell didn't converge, all cells of this
- // circular dependency don't, no matter whether
- // single cells did.
- pIterCell->bDirty = false;
- pIterCell->bTableOpDirty = false;
- pIterCell->aResult.SetResultError( errNoConvergence);
- pIterCell->bChanged = true;
- pDocument->SetTextWidth(pIterCell->aPos, TEXTWIDTH_DIRTY);
- pDocument->SetScriptType(pIterCell->aPos, SC_SCRIPTTYPE_UNKNOWN);
- }
- }
- // End this iteration and remove entries.
- rRecursionHelper.EndIteration();
- bResumeIteration = rRecursionHelper.IsDoingIteration();
- }
- }
- if (rRecursionHelper.IsInRecursionReturn() &&
- rRecursionHelper.GetRecursionCount() == 0 &&
- !rRecursionHelper.IsDoingRecursion())
- {
- bIterationFromRecursion = false;
- // Iterate over cells known so far, start with the last cell
- // encountered, inserting new cells if another recursion limit
- // is reached. Repeat until solved.
- rRecursionHelper.SetDoingRecursion( true);
- do
- {
- rRecursionHelper.SetInRecursionReturn( false);
- for (ScFormulaRecursionList::const_iterator aIter(
- rRecursionHelper.GetIterationStart());
- !rRecursionHelper.IsInReturn() && aIter !=
- rRecursionHelper.GetIterationEnd(); ++aIter)
- {
- ScFormulaCell* pCell = (*aIter).pCell;
- if (pCell->IsDirtyOrInTableOpDirty())
- {
- pCell->InterpretTail( SCITP_NORMAL);
- if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
- pCell->bRunning = (*aIter).bOldRunning;
- }
- }
- } while (rRecursionHelper.IsInRecursionReturn());
- rRecursionHelper.SetDoingRecursion( false);
- if (rRecursionHelper.IsInIterationReturn())
- {
- if (!bResumeIteration)
- bIterationFromRecursion = true;
- }
- else if (bResumeIteration ||
- rRecursionHelper.IsDoingIteration())
- rRecursionHelper.GetList().erase(
- rRecursionHelper.GetIterationStart(),
- rRecursionHelper.GetLastIterationStart());
- else
- rRecursionHelper.Clear();
- }
- } while (bIterationFromRecursion || bResumeIteration);
- }
-}
-
-void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
-{
- class RecursionCounter
- {
- ScRecursionHelper& rRec;
- bool bStackedInIteration;
- public:
- RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
- {
- bStackedInIteration = rRec.IsDoingIteration();
- if (bStackedInIteration)
- rRec.GetRecursionInIterationStack().push( p);
- rRec.IncRecursionCount();
- }
- ~RecursionCounter()
- {
- rRec.DecRecursionCount();
- if (bStackedInIteration)
- rRec.GetRecursionInIterationStack().pop();
- }
- } aRecursionCounter( pDocument->GetRecursionHelper(), this);
- nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
- if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
- {
- // #i11719# no UPN and no error and no token code but result string present
- // => interpretation of this cell during name-compilation and unknown names
- // => can't exchange underlying code array in CompileTokenArray() /
- // Compile() because interpreter's token iterator would crash or pCode
- // would be deleted twice if this cell was interpreted during
- // compilation.
- // This should only be a temporary condition and, since we set an
- // error, if ran into it again we'd bump into the dirty-clearing
- // condition further down.
- if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
- {
- pCode->SetCodeError( errNoCode );
- // This is worth an assertion; if encountered in daily work
- // documents we might need another solution. Or just confirm correctness.
- OSL_FAIL( "ScFormulaCell::Interpret: no UPN, no error, no token, but hybrid formula string" );
- return;
- }
- CompileTokenArray();
- }
-
- if( pCode->GetCodeLen() && pDocument )
- {
- class StackCleaner
- {
- ScDocument* pDoc;
- ScInterpreter* pInt;
- public:
- StackCleaner( ScDocument* pD, ScInterpreter* pI )
- : pDoc(pD), pInt(pI)
- {}
- ~StackCleaner()
- {
- delete pInt;
- pDoc->DecInterpretLevel();
- }
- };
- pDocument->IncInterpretLevel();
- ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
- StackCleaner aStackCleaner( pDocument, p);
- sal_uInt16 nOldErrCode = aResult.GetResultError();
- if ( nSeenInIteration == 0 )
- { // Only the first time
- // With bChanged=false, if a newly compiled cell has a result of
- // 0.0, no change is detected and the cell will not be repainted.
- // bChanged = false;
- aResult.SetResultError( 0 );
- }
-
- switch ( aResult.GetResultError() )
- {
- case errCircularReference : // will be determined again if so
- aResult.SetResultError( 0 );
- break;
- }
-
- bool bOldRunning = bRunning;
- bRunning = true;
- p->Interpret();
- if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
- {
- if (nSeenInIteration > 0)
- --nSeenInIteration; // retry when iteration is resumed
- return;
- }
- bRunning = bOldRunning;
-
- // #i102616# For single-sheet saving consider only content changes, not format type,
- // because format type isn't set on loading (might be changed later)
- bool bContentChanged = false;
-
- // Do not create a HyperLink() cell if the formula results in an error.
- if( p->GetError() && pCode->IsHyperLink())
- pCode->SetHyperLink(false);
-
- if( p->GetError() && p->GetError() != errCircularReference)
- {
- bDirty = false;
- bTableOpDirty = false;
- bChanged = true;
- }
- if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
- {
- bool bIsValue = aResult.IsValue(); // the previous type
- // Did it converge?
- if ((bIsValue && p->GetResultType() == svDouble && fabs(
- p->GetNumResult() - aResult.GetDouble()) <=
- pDocument->GetDocOptions().GetIterEps()) ||
- (!bIsValue && p->GetResultType() == svString &&
- p->GetStringResult() == aResult.GetString()))
- {
- // A convergence in the first iteration doesn't necessarily
- // mean that it's done, it may be as not all related cells
- // of a circle changed their values yet. If the set really
- // converges it will do so also during the next iteration. This
- // fixes situations like of #i44115#. If this wasn't wanted an
- // initial "uncalculated" value would be needed for all cells
- // of a circular dependency => graph needed before calculation.
- if (nSeenInIteration > 1 ||
- pDocument->GetDocOptions().GetIterCount() == 1)
- {
- bDirty = false;
- bTableOpDirty = false;
- }
- }
- }
-
- // New error code?
- if( p->GetError() != nOldErrCode )
- {
- bChanged = true;
- // bContentChanged only has to be set if the file content would be changed
- if ( aResult.GetCellResultType() != svUnknown )
- bContentChanged = true;
- }
- // Different number format?
- if( nFormatType != p->GetRetFormatType() )
- {
- nFormatType = p->GetRetFormatType();
- bChanged = true;
- }
- if( nFormatIndex != p->GetRetFormatIndex() )
- {
- nFormatIndex = p->GetRetFormatIndex();
- bChanged = true;
- }
-
- // In case of changes just obtain the result, no temporary and
- // comparison needed anymore.
- if (bChanged)
- {
- // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
- // Also handle special cases of initial results after loading.
- if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
- {
- ScFormulaResult aNewResult( p->GetResultToken().get());
- StackVar eOld = aResult.GetCellResultType();
- StackVar eNew = aNewResult.GetCellResultType();
- if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
- {
- // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
- // -> no change
- }
- else
- {
- if ( eOld == svHybridCell || eOld == svHybridValueCell ) // string result from SetFormulaResultString?
- eOld = svString; // ScHybridCellToken has a valid GetString method
-
- // #i106045# use approxEqual to compare with stored value
- bContentChanged = (eOld != eNew ||
- (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
- (eNew == svString && aResult.GetString() != aNewResult.GetString()));
- }
- }
-
- aResult.SetToken( p->GetResultToken().get() );
- }
- else
- {
- ScFormulaResult aNewResult( p->GetResultToken().get());
- StackVar eOld = aResult.GetCellResultType();
- StackVar eNew = aNewResult.GetCellResultType();
- bChanged = (eOld != eNew ||
- (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
- (eNew == svString && aResult.GetString() != aNewResult.GetString()));
-
- // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged)
- if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
- {
- if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) ||
- ( (eOld == svHybridCell || eOld == svHybridValueCell) && eNew == svString && aResult.GetString() == aNewResult.GetString() ) ||
- ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) )
- {
- // no change, see above
- }
- else
- bContentChanged = true;
- }
-
- aResult.Assign( aNewResult);
- }
-
- // Precision as shown?
- if ( aResult.IsValue() && !p->GetError()
- && pDocument->GetDocOptions().IsCalcAsShown()
- && nFormatType != NUMBERFORMAT_DATE
- && nFormatType != NUMBERFORMAT_TIME
- && nFormatType != NUMBERFORMAT_DATETIME )
- {
- sal_uLong nFormat = pDocument->GetNumberFormat( aPos );
- if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
- nFormat = nFormatIndex;
- if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
- nFormat = ScGlobal::GetStandardFormat(
- *pDocument->GetFormatTable(), nFormat, nFormatType );
- aResult.SetDouble( pDocument->RoundValueAsShown(
- aResult.GetDouble(), nFormat));
- }
- if (eTailParam == SCITP_NORMAL)
- {
- bDirty = false;
- bTableOpDirty = false;
- }
- if( aResult.GetMatrix() )
- {
- // If the formula wasn't entered as a matrix formula, live on with
- // the upper left corner and let reference counting delete the matrix.
- if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
- aResult.SetToken( aResult.GetCellResultToken().get());
- }
- if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
- {
- // Coded double error may occur via filter import.
- sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble());
- aResult.SetResultError( nErr);
- bChanged = bContentChanged = true;
- }
- if( bChanged )
- {
- pDocument->SetTextWidth(aPos, TEXTWIDTH_DIRTY);
- pDocument->SetScriptType(aPos, SC_SCRIPTTYPE_UNKNOWN);
- }
- if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
- {
- // pass bIgnoreLock=true, because even if called from pending row height update,
- // a changed result must still reset the stream flag
- pDocument->SetStreamValid(aPos.Tab(), false, true);
- }
- if ( !pCode->IsRecalcModeAlways() )
- pDocument->RemoveFromFormulaTree( this );
-
- // FORCED cells also immediately tested for validity (start macro possibly)
-
- if ( pCode->IsRecalcModeForced() )
- {
- sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
- aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
- if ( nValidation )
- {
- const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
- ScRefCellValue aTmpCell(this);
- if ( pData && !pData->IsDataValid(aTmpCell, aPos))
- pData->DoCalcError( this );
- }
- }
-
- // Reschedule slows the whole thing down considerably, thus only execute on percent change
- ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
- pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
-
- switch (p->GetVolatileType())
- {
- case ScInterpreter::VOLATILE:
- // Volatile via built-in volatile functions. No actions needed.
- break;
- case ScInterpreter::VOLATILE_MACRO:
- // The formula contains a volatile macro.
- pCode->SetExclusiveRecalcModeAlways();
- pDocument->PutInFormulaTree(this);
- StartListeningTo(pDocument);
- break;
- case ScInterpreter::NOT_VOLATILE:
- if (pCode->IsRecalcModeAlways())
- {
- // The formula was previously volatile, but no more.
- EndListeningTo(pDocument);
- pCode->SetExclusiveRecalcModeNormal();
- }
- else
- {
- // non-volatile formula. End listening to the area in case
- // it's listening due to macro module change.
- pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
- }
- pDocument->RemoveFromFormulaTree(this);
- break;
- default:
- ;
- }
- }
- else
- {
- // Cells with compiler errors should not be marked dirty forever
- OSL_ENSURE( pCode->GetCodeError(), "no UPN-Code und no errors ?!?!" );
- bDirty = false;
- bTableOpDirty = false;
- }
-}
-
-
-void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows, bool bDirtyFlag )
-{
- ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
- if (pMat)
- pMat->SetMatColsRows( nCols, nRows );
- else if (nCols || nRows)
- {
- aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
- // Setting the new token actually forces an empty result at this top
- // left cell, so have that recalculated.
- SetDirty( bDirtyFlag );
- }
-}
-
-
-void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
-{
- const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
- if (pMat)
- pMat->GetMatColsRows( nCols, nRows);
- else
- {
- nCols = 0;
- nRows = 0;
- }
-}
-
-
-sal_uLong ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, sal_uLong nFormat ) const
-{
- if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
- return nFormatIndex;
- //! not ScFormulaCell::IsValue(), that could reinterpret the formula again.
- if ( aResult.IsValue() )
- return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType );
- else
- return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType );
-}
-
-
-void ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
-{
- if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
- {
- const ScHint* p = PTR_CAST( ScHint, &rHint );
- sal_uLong nHint = (p ? p->GetId() : 0);
- if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY))
- {
- bool bForceTrack = false;
- if ( nHint & SC_HINT_TABLEOPDIRTY )
- {
- bForceTrack = !bTableOpDirty;
- if ( !bTableOpDirty )
- {
- pDocument->AddTableOpFormulaCell( this );
- bTableOpDirty = true;
- }
- }
- else
- {
- bForceTrack = !bDirty;
- SetDirtyVar();
- }
- // Don't remove from FormulaTree to put in FormulaTrack to
- // put in FormulaTree again and again, only if necessary.
- // Any other means except RECALCMODE_ALWAYS by which a cell could
- // be in FormulaTree if it would notify other cells through
- // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
- // Yes. The new TableOpDirty made it necessary to have a
- // forced mode where formulas may still be in FormulaTree from
- // TableOpDirty but have to notify dependents for normal dirty.
- if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
- || pCode->IsRecalcModeAlways())
- && !pDocument->IsInFormulaTrack( this ) )
- pDocument->AppendToFormulaTrack( this );
- }
- }
-}
-
-void ScFormulaCell::SetDirty( bool bDirtyFlag )
-{
- if ( !IsInChangeTrack() )
- {
- if ( pDocument->GetHardRecalcState() )
- SetDirtyVar();
- else
- {
- // Multiple Formulas avoid tracking in Load and Copy compileAll
- // by Scenario and Copy Block From Clip.
- // If unconditional required Formula tracking is set before SetDirty
- // bDirty = false, eg in CompileTokenArray
- if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
- {
- if( bDirtyFlag )
- SetDirtyVar();
- pDocument->AppendToFormulaTrack( this );
- pDocument->TrackFormulas();
- }
- }
-
- if (pDocument->IsStreamValid(aPos.Tab()))
- pDocument->SetStreamValid(aPos.Tab(), false);
- }
-}
-
-void ScFormulaCell::SetDirtyVar()
-{
- bDirty = true;
- // mark the sheet of this cell to be calculated
- //#FIXME do we need to revert this remnant of old fake vba events? pDocument->AddCalculateTable( aPos.Tab() );
-}
-
-void ScFormulaCell::SetDirtyAfterLoad()
-{
- bDirty = true;
- if ( !pDocument->GetHardRecalcState() )
- pDocument->PutInFormulaTree( this );
-}
-
-void ScFormulaCell::SetTableOpDirty()
-{
- if ( !IsInChangeTrack() )
- {
- if ( pDocument->GetHardRecalcState() )
- bTableOpDirty = true;
- else
- {
- if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
- {
- if ( !bTableOpDirty )
- {
- pDocument->AddTableOpFormulaCell( this );
- bTableOpDirty = true;
- }
- pDocument->AppendToFormulaTrack( this );
- pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
- }
- }
- }
-}
-
-
-bool ScFormulaCell::IsDirtyOrInTableOpDirty() const
-{
- return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
-}
-
-
-void ScFormulaCell::SetErrCode( sal_uInt16 n )
-{
- /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
- * used whether it is solely for transport of a simple result error and get
- * rid of that abuse. */
- pCode->SetCodeError( n );
- // Hard set errors are transported as result type value per convention,
- // e.g. via clipboard. ScFormulaResult::IsValue() and
- // ScFormulaResult::GetDouble() handle that.
- aResult.SetResultError( n );
-}
-
-void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
-{
- if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL )
- bDirty = true;
- if ( nBits & RECALCMODE_ONLOAD_ONCE )
- { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
- nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL;
- }
- pCode->AddRecalcMode( nBits );
-}
-
-// Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
-void ScFormulaCell::GetURLResult( rtl::OUString& rURL, rtl::OUString& rCellText )
-{
- rtl::OUString aCellString;
-
- Color* pColor;
-
- // Cell Text uses the Cell format while the URL uses
- // the default format for the type.
- sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos );
- SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
-
- if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
- nCellFormat = GetStandardFormat( *pFormatter,nCellFormat );
-
- sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER);
-
- if ( IsValue() )
- {
- double fValue = GetValue();
- pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
- }
- else
- {
- aCellString = GetString();
- pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
- }
- ScConstMatrixRef xMat( aResult.GetMatrix());
- if (xMat)
- {
- // determine if the matrix result is a string or value.
- if (!xMat->IsValue(0, 1))
- rURL = xMat->GetString(0, 1);
- else
- pFormatter->GetOutputString(
- xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
- }
-
- if(rURL.isEmpty())
- {
- if(IsValue())
- pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
- else
- pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
- }
-}
-
-bool ScFormulaCell::IsMultilineResult()
-{
- if (!IsValue())
- return aResult.IsMultiline();
- return false;
-}
-
-void ScFormulaCell::MaybeInterpret()
-{
- if (!IsDirtyOrInTableOpDirty())
- return;
-
- if (pDocument->GetAutoCalc() || (cMatrixFlag != MM_NONE))
- Interpret();
-}
-
-EditTextObject* ScFormulaCell::CreateURLObject()
-{
- rtl::OUString aCellText;
- rtl::OUString aURL;
- GetURLResult( aURL, aCellText );
-
- return ScEditUtil::CreateURLObjectFromURL( *pDocument, aURL, aCellText );
-}
-
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/cell2.cxx b/sc/source/core/data/cell2.cxx
index b443d658c087..9d8b1b9de07d 100644
--- a/sc/source/core/data/cell2.cxx
+++ b/sc/source/core/data/cell2.cxx
@@ -17,34 +17,14 @@
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
-#include <algorithm>
-#include <deque>
-
-#include <boost/bind.hpp>
-#include <sal/macros.h>
-#include <vcl/mapmod.hxx>
-#include <editeng/editobj.hxx>
-#include <editeng/editstat.hxx>
-#include "editeng/fieldupdater.hxx"
-
#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"
-#include "scitems.hxx"
-#include "patattr.hxx"
-#include <rtl/strbuf.hxx>
-#include <cstdio>
-
-using namespace formula;
+#include "editeng/editobj.hxx"
+#include "editeng/editstat.hxx"
+#include "editeng/fieldupdater.hxx"
// STATIC DATA -----------------------------------------------------------
@@ -260,1725 +240,4 @@ SCROW ScEditDataArray::Item::GetRow() const
return mnRow;
}
-// ============================================================================
-
-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<ScToken*>::const_iterator aBegin,
- const deque<ScToken*>::const_iterator aEnd,
- bool& bCol, bool& bRow, bool& bTab)
-{
- deque<ScToken*>::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)
- {
- 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<ScToken*>& rReferences,
- const DimensionSelector aWhich)
-{
- typedef deque<ScToken*>::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<ScToken*>& 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<ScToken*>& 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_FAIL( "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;
-}
-
-}
-
-bool ScFormulaCell::IsEmpty()
-{
- MaybeInterpret();
- return aResult.GetCellResultType() == formula::svEmptyCell;
-}
-
-bool ScFormulaCell::IsEmptyDisplayedAsString()
-{
- MaybeInterpret();
- return aResult.IsEmptyDisplayedAsString();
-}
-
-bool ScFormulaCell::IsValue()
-{
- MaybeInterpret();
- return aResult.IsValue();
-}
-
-bool ScFormulaCell::IsHybridValueCell()
-{
- return aResult.GetType() == formula::svHybridValueCell;
-}
-
-double ScFormulaCell::GetValue()
-{
- MaybeInterpret();
- 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
- MaybeInterpret();
- return aResult.GetDouble();
-}
-
-rtl::OUString ScFormulaCell::GetString()
-{
- MaybeInterpret();
- if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
- !aResult.GetResultError())
- return aResult.GetString();
- return rtl::OUString();
-}
-
-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()))
- Interpret();
- }
- return aResult.GetMatrix().get();
-}
-
-bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
-{
- switch ( cMatrixFlag )
- {
- case MM_FORMULA :
- rPos = aPos;
- return true;
- case MM_REFERENCE :
- {
- pCode->Reset();
- ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
- if( t )
- {
- ScSingleRefData& rRef = t->GetSingleRef();
- rRef.CalcAbsIfRel( aPos );
- if ( rRef.Valid() )
- {
- rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
- return true;
- }
- }
- }
- break;
- }
- return false;
-}
-
-
-/*
- Edge-Values:
-
- 8
- 4 16
- 2
-
- inside: 1
- outside: 0
- (reserved: open: 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; // bad luck..
- if ( aOrg != rOrgPos )
- { // First time or a different matrix than last time.
- rOrgPos = aOrg;
- ScFormulaCell* pFCell;
- if ( cMatrixFlag == MM_REFERENCE )
- pFCell = pDocument->GetFormulaCell(aOrg);
- else
- pFCell = this; // this MM_FORMULA
- // There's only one this, don't compare pFCell==this.
- if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA
- && pFCell->cMatrixFlag == MM_FORMULA )
- {
- pFCell->GetMatColsRows( nC, nR );
- if ( nC == 0 || nR == 0 )
- {
- // No ScMatrixFormulaCellToken available yet, calculate new.
- nC = 1;
- nR = 1;
- ScAddress aTmpOrg;
- ScFormulaCell* pCell;
- ScAddress aAdr( aOrg );
- aAdr.IncCol();
- bool bCont = true;
- do
- {
- pCell = pDocument->GetFormulaCell(aAdr);
- if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
- pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
- {
- nC++;
- aAdr.IncCol();
- }
- else
- bCont = false;
- } while ( bCont );
- aAdr = aOrg;
- aAdr.IncRow();
- bCont = true;
- do
- {
- pCell = pDocument->GetFormulaCell(aAdr);
- if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
- pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
- {
- nR++;
- aAdr.IncRow();
- }
- else
- bCont = false;
- } while ( bCont );
- pFCell->SetMatColsRows( nC, nR );
- }
- }
- else
- {
-#if OSL_DEBUG_LEVEL > 0
- rtl::OUString aTmp;
- rtl::OStringBuffer aMsg(RTL_CONSTASCII_STRINGPARAM(
- "broken Matrix, no MatFormula at origin, Pos: "));
- aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
- aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
- aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
- aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
- aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
- OSL_FAIL(aMsg.getStr());
-#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; // left edge
- if ( dC+1 == nC )
- nEdges |= 16; // right edge
- if ( dR == 0 )
- nEdges |= 8; // top edge
- if ( dR+1 == nR )
- nEdges |= 2; // bottom edge
- if ( !nEdges )
- nEdges = 1; // inside
- }
-#if OSL_DEBUG_LEVEL > 0
- else
- {
- rtl::OUString aTmp;
- rtl::OStringBuffer aMsg( "broken Matrix, Pos: " );
- aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
- aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
- aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
- aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
- aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
- aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatCols: "));
- aMsg.append(static_cast<sal_Int32>( nC ));
- aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatRows: "));
- aMsg.append(static_cast<sal_Int32>( nR ));
- aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffCols: "));
- aMsg.append(static_cast<sal_Int32>( dC ));
- aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffRows: "));
- aMsg.append(static_cast<sal_Int32>( dR ));
- OSL_FAIL( aMsg.makeStringAndClear().getStr());
- }
-#endif
- return nEdges;
-// break;
- }
- default:
- return 0;
- }
-}
-
-sal_uInt16 ScFormulaCell::GetErrCode()
-{
- MaybeInterpret();
-
- /* 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();
-}
-
-bool ScFormulaCell::HasOneReference( ScRange& r ) const
-{
- pCode->Reset();
- ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
- if( p && !pCode->GetNextReferenceRPN() ) // only one!
- {
- 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 true;
- }
- else
- return 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<ScToken*>(pCode->GetNextReferenceRPN()));
- if (pFirstReference)
- {
- // Collect all consecutive references, starting by the one
- // already found
- std::deque<ScToken*> aReferences;
- aReferences.push_back(pFirstReference);
- FormulaToken* pToken(pCode->NextRPN());
- FormulaToken* pFunction(0);
- while (pToken)
- {
- if (lcl_isReference(*pToken))
- {
- aReferences.push_back(dynamic_cast<ScToken*>(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;
-}
-
-bool ScFormulaCell::HasRelNameReference() const
-{
- pCode->Reset();
- ScToken* t;
- while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
- {
- if ( t->GetSingleRef().IsRelName() ||
- (t->GetType() == formula::svDoubleRef &&
- t->GetDoubleRef().Ref2.IsRelName()) )
- return true;
- }
- return false;
-}
-
-bool ScFormulaCell::HasColRowName() const
-{
- pCode->Reset();
- return (pCode->GetNextColRowName() != NULL);
-}
-
-bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode,
- const ScRange& r,
- SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
- ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
-{
- bool bCellStateChanged = false;
-
- SCCOL nCol1 = r.aStart.Col();
- SCROW nRow1 = r.aStart.Row();
- 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 );
-// bool bPosChanged = false; // if this cell was moved
- bool bIsInsert = (eUpdateRefMode == URM_INSDEL &&
- nDx >= 0 && nDy >= 0 && nDz >= 0);
- if (eUpdateRefMode == URM_INSDEL && r.In( aPos ))
- {
- aPos.Move(nDx, nDy, nDz);
- bCellStateChanged = aPos != aOldPos;
- }
- else if ( r.In( aPos ) )
- {
- aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
- }
-
- bool bHasRefs = false;
- bool bHasColRowNames = false;
- bool bOnRefMove = 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;
- ScRangeData* pRangeData;
- bool bValChanged = false;
- bool bRangeModified = false; // any range, not only shared formula
- bool bRefSizeChanged = false;
- 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 = false;
- pRangeData = NULL;
- bRangeModified = false;
- bRefSizeChanged = false;
- }
-
- bCellStateChanged |= bValChanged;
-
- if ( bOnRefMove )
- bOnRefMove = (bValChanged || (aPos != aOldPos));
- // Cell may reference itself, e.g. ocColumn, ocRow without parameter
-
- 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 = false;
- ScToken* t;
- ScRangePairList* pColList = pDocument->GetColNameRanges();
- ScRangePairList* pRowList = pDocument->GetRowNameRanges();
- pCode->Reset();
- while ( !bColRowNameCompile && (t = static_cast<ScToken*>(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 )
- { // defined
- if ( pR->GetRange(1).aStart.Row() == nRow1 )
- bColRowNameCompile = true;
- }
- else
- { // on the fly
- if ( rRef.nRow + 1 == nRow1 )
- bColRowNameCompile = 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 )
- { // defined
- if ( pR->GetRange(1).aStart.Col() == nCol1 )
- bColRowNameCompile = true;
- }
- else
- { // on the fly
- if ( rRef.nCol + 1 == nCol1 )
- bColRowNameCompile = true;
- }
- }
- }
- }
- else if ( eUpdateRefMode == URM_MOVE )
- { // Recomplie for Move/D&D when ColRowName was moved or this Cell
- // points to one and was moved.
- bColRowNameCompile = bCompile; // Possibly from Copy ctor
- if ( !bColRowNameCompile )
- {
- bool bMoved = (aPos != aOldPos);
- pCode->Reset();
- ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
- if ( t && bMoved )
- bColRowNameCompile = 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 = true;
- }
- t = static_cast<ScToken*>(pCode->GetNextColRowName());
- }
- }
- }
- else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
- {
- bColRowNameCompile = true;
- }
- ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
- if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
- bInDeleteUndo = true;
- else
- bInDeleteUndo = 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 =
- false;
- }
-
- bool bNeedDirty = false;
- // NeedDirty for changes except for Copy and Move/Insert without RelNames
- if ( bRangeModified || pRangeData || bColRowNameCompile ||
- (bValChanged && eUpdateRefMode != URM_COPY &&
- (eUpdateRefMode != URM_MOVE || bHasRelName) &&
- (!bIsInsert || bHasRelName || bInDeleteUndo ||
- bRefSizeChanged)) || bOnRefMove)
- bNeedDirty = true;
- else
- bNeedDirty = 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 );
- }
- }
- bValChanged = 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 = true;
- bNeedDirty = true;
- }
- if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
- {
- CompileTokenArray( bNewListening ); // no Listening
- bNeedDirty = 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( true);
- }
- else
- StartListeningTo( pDocument );
- }
- }
- if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) )
- { // Cut off references, invalid or similar?
- bool bOldAutoCalc = pDocument->GetAutoCalc();
- // No Interpret in SubMinimalRecalc because of eventual wrong reference
- pDocument->SetAutoCalc( false );
- SetDirty();
- pDocument->SetAutoCalc( bOldAutoCalc );
- }
-
- delete pOld;
- }
- return bCellStateChanged;
-}
-
-void ScFormulaCell::UpdateInsertTab(SCTAB nTable, SCTAB nNewSheets)
-{
- bool bPosChanged = ( aPos.Tab() >= nTable ? true : false );
- pCode->Reset();
- if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
- {
- EndListeningTo( pDocument );
- // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
- if ( bPosChanged )
- aPos.IncTab(nNewSheets);
- ScRangeData* pRangeData;
- ScCompiler aComp(pDocument, aPos, *pCode);
- aComp.SetGrammar(pDocument->GetGrammar());
- pRangeData = aComp.UpdateInsertTab( nTable, false, nNewSheets );
- if (pRangeData) // Exchange Shared Formula with real Formula
- {
- 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, false, nNewSheets );
- // If the shared formula contained a named range/formula containing
- // an absolute reference to a sheet, those have to be readjusted.
- aComp2.UpdateDeleteTab( nTable, false, true, bRefChanged, nNewSheets );
- bCompile = true;
- }
- // no StartListeningTo because pTab[nTab] does not exsist!
- }
- else if ( bPosChanged )
- aPos.IncTab();
-}
-
-bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, bool bIsMove, SCTAB nSheets)
-{
- bool bRefChanged = false;
- bool bPosChanged = ( aPos.Tab() >= nTable + nSheets ? true : false );
- pCode->Reset();
- if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
- {
- EndListeningTo( pDocument );
- // IncTab _after_ EndListeningTo und _before_ Compiler UpdateDeleteTab!
- if ( bPosChanged )
- aPos.IncTab(-1*nSheets);
- ScRangeData* pRangeData;
- ScCompiler aComp(pDocument, aPos, *pCode);
- aComp.SetGrammar(pDocument->GetGrammar());
- pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, false, bRefChanged, nSheets);
- if (pRangeData) // Exchange Shared Formula with real Formula
- {
- 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, false, false, bRefChanged, nSheets );
- // If the shared formula contained a named range/formula containing
- // an absolute reference to a sheet, those have to be readjusted.
- aComp2.UpdateInsertTab( nTable,true, nSheets );
- // bRefChanged could have been reset at the last UpdateDeleteTab
- bRefChanged = true;
- bCompile = true;
- }
- // no StartListeningTo because pTab[nTab] not yet correct!
- }
- else if ( bPosChanged )
- aPos.IncTab(-1*nSheets);
-
- return bRefChanged;
-}
-
-void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
-{
- pCode->Reset();
- if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
- {
- EndListeningTo( pDocument );
- // SetTab _after_ EndListeningTo und _before_ Compiler UpdateMoveTab !
- aPos.SetTab( nTabNo );
- ScRangeData* pRangeData;
- ScCompiler aComp(pDocument, aPos, *pCode);
- aComp.SetGrammar(pDocument->GetGrammar());
- pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, false );
- if (pRangeData) // Exchange Shared Formula with real Formula
- {
- 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, true );
- bCompile = true;
- }
- // no StartListeningTo because pTab[nTab] not yet correct!
- }
- else
- aPos.SetTab( nTabNo );
-}
-
-void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
-{
- if( !pDocument->IsClipOrUndo() )
- {
- pCode->Reset();
- ScToken* p = static_cast<ScToken*>(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<ScToken*>(pCode->GetNextReferenceRPN());
- }
- }
-}
-
-bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
-{
- bool bRet = false;
- if( !pDocument->IsClipOrUndo() )
- {
- pCode->Reset();
- ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
- while( p )
- {
- ScSingleRefData& rRef1 = p->GetSingleRef();
- if( !rRef1.IsTabRel() )
- {
- if( (SCsTAB) nTable != rRef1.nTab )
- bRet = 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 = true;
- else if (nTable != aPos.Tab())
- rRef2.nTab = aPos.Tab();
- }
- }
- p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
- }
- }
- return bRet;
-}
-
-void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
-{
- if ( bForceIfNameInUse && !bCompile )
- bCompile = pCode->HasNameOrColRowName();
- if ( bCompile )
- pCode->SetCodeError( 0 ); // make sure it will really be compiled
- CompileTokenArray();
-}
-
-// Reference transposition is only called in Clipboard Document
-void ScFormulaCell::TransposeReference()
-{
- bool bFound = false;
- pCode->Reset();
- ScToken* t;
- while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
- {
- ScSingleRefData& rRef1 = t->GetSingleRef();
- if ( rRef1.IsColRel() && rRef1.IsRowRel() )
- {
- 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<SCCOL>(rRef1.nRelRow);
- rRef1.nRelRow = static_cast<SCROW>(nTemp);
-
- if ( bDouble )
- {
- nTemp = rRef2.nRelCol;
- rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
- rRef2.nRelRow = static_cast<SCROW>(nTemp);
- }
-
- bFound = true;
- }
- }
- }
-
- if (bFound)
- bCompile = true;
-}
-
-void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
- ScDocument* pUndoDoc )
-{
- EndListeningTo( pDocument );
-
- ScAddress aOldPos = aPos;
- bool bPosChanged = false; // Whether this cell has been moved
-
- ScRange aDestRange( rDest, ScAddress(
- static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
- static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
- rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
- if ( aDestRange.In( aOldPos ) )
- {
- // Count back Positions
- 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 = true;
- }
-
- ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
- bool bRefChanged = false;
- ScToken* t;
-
- ScRangeData* pShared = NULL;
- pCode->Reset();
- while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
- {
- if( t->GetOpCode() == ocName )
- {
- ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
- if (pName)
- {
- if (pName->IsModified())
- bRefChanged = true;
- if (pName->HasType(RT_SHAREDMOD))
- pShared = pName;
- }
- }
- else if( t->GetType() != svIndex )
- {
- t->CalcAbsIfRel( aOldPos );
- 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 = true;
- }
- }
- }
-
- if (pShared) // Exchange Shared Formula with real Formula
- {
- pDocument->RemoveFromFormulaTree( this ); // update formula count
- delete pCode;
- pCode = new ScTokenArray( *pShared->GetCode() );
- bRefChanged = true;
- pCode->Reset();
- while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
- {
- if( t->GetType() != svIndex )
- {
- t->CalcAbsIfRel( aOldPos );
- 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->SetFormulaCell(aPos, pFCell);
- }
-
- bCompile = true;
- CompileTokenArray(); // also call StartListeningTo
- SetDirty();
- }
- else
- StartListeningTo( pDocument ); // Listener as previous
-
- delete pOld;
-}
-
-void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
-{
- EndListeningTo( pDocument );
-
- bool bRefChanged = false;
- ScToken* t;
- ScRangeData* pShared = NULL;
-
- pCode->Reset();
- while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
- {
- if( t->GetOpCode() == ocName )
- {
- ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
- if (pName)
- {
- if (pName->IsModified())
- bRefChanged = true;
- if (pName->HasType(RT_SHAREDMOD))
- pShared = pName;
- }
- }
- else if( t->GetType() != svIndex )
- {
- t->CalcAbsIfRel( aPos );
- 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 = true;
- }
- }
- }
-
- if (pShared) // Exchange Shared Formula with real Formula
- {
- pDocument->RemoveFromFormulaTree( this ); // Update formula count
- delete pCode;
- pCode = new ScTokenArray( *pShared->GetCode() );
- bRefChanged = true;
- pCode->Reset();
- while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
- {
- if( t->GetType() != svIndex )
- {
- t->CalcAbsIfRel( aPos );
- 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 = true;
- CompileTokenArray(); // Also call StartListeningTo
- SetDirty();
- }
- else
- StartListeningTo( pDocument ); // Listener as previous
-}
-
-static void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& 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->findByIndex(p->GetIndex());
- if (pSubName)
- lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
- }
- }
-}
-
-void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
-{
- lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
-}
-
-bool ScFormulaCell::IsChanged() const
-{
- return bChanged;
-}
-
-void ScFormulaCell::ResetChanged()
-{
- bChanged = false;
-}
-
-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 = true;
- CompileTokenArray();
- SetDirty();
- break;
- }
- }
-}
-
-void ScFormulaCell::CompileDBFormula( bool bCreateFormulaString )
-{
- // Two phases must be called after each other
- // 1. Formula String with old generated names
- // 2. Formula String with new generated names
- if ( bCreateFormulaString )
- {
- bool bRecompile = false;
- pCode->Reset();
- for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
- {
- switch ( p->GetOpCode() )
- {
- case ocBad: // DB Area eventually goes bad
- case ocColRowName: // in case of the same names
- case ocDBArea: // DB Area
- bRecompile = true;
- break;
- case ocName:
- if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
- bRecompile = true; // DB Area
- break;
- default:
- ; // nothing
- }
- }
- if ( bRecompile )
- {
- rtl::OUString aFormula;
- GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
- if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
- {
- if ( aFormula[ aFormula.getLength()-1 ] == '}' )
- aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
- if ( aFormula[0] == '{' )
- aFormula = aFormula.copy( 1 );
- }
- EndListeningTo( pDocument );
- pDocument->RemoveFromFormulaTree( this );
- pCode->Clear();
- SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
- }
- }
- else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
- {
- Compile( aResult.GetHybridFormula(), false, eTempGrammar );
- aResult.SetToken( NULL);
- SetDirty();
- }
-}
-
-void ScFormulaCell::CompileNameFormula( bool bCreateFormulaString )
-{
- // Two phases must be called after each other
- // 1. Formula String with old generated names
- // 2. Formula String with new generated names
- if ( bCreateFormulaString )
- {
- bool bRecompile = false;
- pCode->Reset();
- for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
- {
- switch ( p->GetOpCode() )
- {
- case ocBad: // in case RangeName goes bad
- case ocColRowName: // in case the names are the same
- bRecompile = true;
- break;
- default:
- if ( p->GetType() == svIndex )
- bRecompile = true; // RangeName
- }
- }
- if ( bRecompile )
- {
- rtl::OUString aFormula;
- GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
- if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
- {
- if ( aFormula[ aFormula.getLength()-1 ] == '}' )
- aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
- if ( aFormula[0] == '{' )
- aFormula = aFormula.copy( 1 );
- }
- EndListeningTo( pDocument );
- pDocument->RemoveFromFormulaTree( this );
- pCode->Clear();
- SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
- }
- }
- else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
- {
- Compile( aResult.GetHybridFormula(), 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 = true;
- CompileTokenArray();
- SetDirty();
- break;
- }
- }
-}
-
-// we really want to be a lot more descriptive than this
-struct ScSimilarFormulaDelta : std::vector< size_t >
-{
- bool IsCompatible( ScSimilarFormulaDelta *pDelta )
- {
- if ( size() != pDelta->size() )
- return false;
- for ( size_t i = 0; i < size(); i++ )
- {
- if ( (*this)[ i ] != (*pDelta)[ i ] )
- return false;
- }
- return true;
- }
-
- void push_delta( const ScSingleRefData& a, const ScSingleRefData& b )
- {
- push_back( b.nCol - a.nCol );
- push_back( b.nRow - a.nRow );
- push_back( b.nTab - a.nTab );
- }
-
- /// if the vector is zero then nothing changes down the column.
- bool IsInvariant() const
- {
- for ( size_t i = 0; i < size(); i++ )
- {
- if ( (*this)[ i ] != 0 )
- return false;
- }
- return true;
- }
-};
-
-bool ScFormulaCellGroup::IsCompatible( ScSimilarFormulaDelta *pDelta )
-{
- return pDelta && mpDelta && mpDelta->IsCompatible( pDelta );
-}
-
-/// compare formulae tokens and build a series of deltas describing
-/// the difference - ie. the result, when added to this
-/// formulae should produce pOther
-ScSimilarFormulaDelta *ScFormulaCell::BuildDeltaTo( ScFormulaCell *pOtherCell )
-{
- // no Matrix formulae yet.
- if ( GetMatrixFlag() != MM_NONE )
- return NULL;
-
- // are these formule at all similar ?
- if ( GetHash() != pOtherCell->GetHash() )
- return NULL;
-
- FormulaToken **pThis = pCode->GetCode();
- sal_uInt16 pThisLen = pCode->GetCodeLen();
- FormulaToken **pOther = pOtherCell->pCode->GetCode();
- sal_uInt16 pOtherLen = pOtherCell->pCode->GetCodeLen();
-
- if ( !pThis || !pOther )
- {
-// fprintf( stderr, "Error: no compiled code for cells !" );
- return NULL;
- }
-
- if ( pThisLen != pOtherLen )
- return NULL;
-
- // check we are basically the same function
- for ( sal_uInt16 i = 0; i < pThisLen; i++ )
- {
- if ( pThis[ i ]->GetType() != pOther[ i ]->GetType() ||
- pThis[ i ]->GetOpCode() != pOther[ i ]->GetOpCode() ||
- pThis[ i ]->GetParamCount() != pOther[ i ]->GetParamCount() )
- {
-// fprintf( stderr, "Incompatible type, op-code or param counts\n" );
- return NULL;
- }
- switch( pThis[ i ]->GetType() )
- {
- case formula::svMatrix:
- case formula::svExternalSingleRef:
- case formula::svExternalDoubleRef:
-// fprintf( stderr, "Ignoring matrix and external references for now\n" );
- return NULL;
- default:
- break;
- }
- }
-
- ScSimilarFormulaDelta *pDelta = new ScSimilarFormulaDelta();
-
- for ( sal_uInt16 i = 0; i < pThisLen; i++ )
- {
- ScToken *pThisTok = static_cast< ScToken * >( pThis[ i ] );
- ScToken *pOtherTok = static_cast< ScToken * >( pOther[ i ] );
-
- if ( pThis[i]->GetType() == formula::svSingleRef ||
- pThis[i]->GetType() == formula::svDoubleRef )
- {
- const ScSingleRefData& aThisRef = pThisTok->GetSingleRef();
- const ScSingleRefData& aOtherRef = pOtherTok->GetSingleRef();
- pDelta->push_delta( aThisRef, aOtherRef );
- }
- if ( pThis[i]->GetType() == formula::svDoubleRef )
- {
- const ScSingleRefData& aThisRef2 = pThisTok->GetSingleRef2();
- const ScSingleRefData& aOtherRef2 = pOtherTok->GetSingleRef2();
- pDelta->push_delta( aThisRef2, aOtherRef2 );
- }
- }
-
- return pDelta;
-}
-
-/// To avoid exposing impl. details of ScSimilarFormulaDelta publicly
-void ScFormulaCell::ReleaseDelta( ScSimilarFormulaDelta *pDelta )
-{
- delete pDelta;
-}
-
-bool ScFormulaCell::InterpretFormulaGroup()
-{
- // Re-build formulae groups if necessary - ideally this is done at
- // import / insert / delete etc. and is integral to the data structures
- pDocument->RebuildFormulaGroups();
-
- if( !xGroup.get() )
- return false;
-
-// fprintf( stderr, "Interpret cell %d, %d\n", (int)aPos.Col(), (int)aPos.Row() );
-
- if ( xGroup->mpDelta->IsInvariant() )
- {
-// fprintf( stderr, "struck gold - completely invariant for %d items !\n",
-// (int)xGroup->mnLength );
-
- // calculate ourselves:
- InterpretTail( SCITP_NORMAL );
- for ( sal_Int32 i = 0; i < xGroup->mnLength; i++ )
- {
- ScAddress aTmpPos = aPos;
- aTmpPos.SetRow(xGroup->mnStart + i);
- ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
- assert( pCell != NULL );
-
- // FIXME: this set of horrors is unclear to me ... certainly
- // the above GetCell is profoundly nasty & slow ...
-
- // Ensure the cell truly has a result:
- pCell->aResult = aResult;
- pCell->ResetDirty();
-
- // FIXME: there is a view / refresh missing here it appears.
- }
- return true;
- }
- else
- {
- // scan the formula ...
- // have a document method: "Get2DRangeAsDoublesArray" that does the
- // column-based heavy lifting call it for each absolute range from the
- // first cell pos in the formula group.
- //
- // Project single references to ranges by adding their vector * xGroup->mnLength
- //
- // TODO:
- // elide multiple dimensional movement in vectors eg. =SUM(A1<1,1>)
- // produces a diagonal 'column' that serves no useful purpose for us.
- // these should be very rare. Should elide in GetDeltas anyway and
- // assert here.
- //
- // Having built our input data ...
- // Throw it, and the formula over to some 'OpenCLCalculage' hook
- //
- // on return - release references on these double buffers
- //
- // transfer the result to the formula cells (as above)
- // store the doubles in the columns' maDoubles array for
- // dependent formulae
- //
- // TODO:
- // need to abort/fail when we get errors returned and fallback to
- // stock interpreting [ I guess ], unless we can use NaN etc. to
- // signal errors.
-
- return false;
- }
-}
-
-void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
-{
- if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
- return;
-
- pDoc->SetDetectiveDirty(true); // It has changed something
-
- ScTokenArray* pArr = GetCode();
- if( pArr->IsRecalcModeAlways() )
- {
- pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, this);
- SetNeedsListening( false);
- return;
- }
-
- pArr->Reset();
- ScToken* t;
- while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
- {
- StackVar eType = t->GetType();
- ScSingleRefData& rRef1 = t->GetSingleRef();
- ScSingleRefData& rRef2 = (eType == svDoubleRef ?
- t->GetDoubleRef().Ref2 : rRef1);
- switch( eType )
- {
- case svSingleRef:
- rRef1.CalcAbsIfRel(aPos);
- if ( rRef1.Valid() )
- {
- pDoc->StartListeningCell(
- ScAddress( rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab ), this );
- }
- break;
- case svDoubleRef:
- t->CalcAbsIfRel(aPos);
- if ( rRef1.Valid() && rRef2.Valid() )
- {
- if ( t->GetOpCode() == ocColRowNameAuto )
- { // automagically
- if ( rRef1.IsColRel() )
- { // ColName
- pDoc->StartListeningArea( ScRange (
- rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab,
- rRef2.nCol,
- MAXROW,
- rRef2.nTab ), this );
- }
- else
- { // RowName
- pDoc->StartListeningArea( ScRange (
- rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab,
- MAXCOL,
- rRef2.nRow,
- rRef2.nTab ), this );
- }
- }
- else
- {
- pDoc->StartListeningArea( ScRange (
- rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab,
- rRef2.nCol,
- rRef2.nRow,
- rRef2.nTab ), this );
- }
- }
- break;
- default:
- ; // nothing
- }
- }
- SetNeedsListening( false);
-}
-
-// pArr gesetzt -> Referenzen von anderer Zelle nehmen
-// Then aPos must also be commited
-
-void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
- ScAddress aCellPos )
-{
- if (pDoc->IsClipOrUndo() || IsInChangeTrack())
- return;
-
- pDoc->SetDetectiveDirty(true); // It has changed something
-
- if ( GetCode()->IsRecalcModeAlways() )
- {
- pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, this );
- return;
- }
-
- if (!pArr)
- {
- pArr = GetCode();
- aCellPos = aPos;
- }
- pArr->Reset();
- ScToken* t;
- while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
- {
- StackVar eType = t->GetType();
- ScSingleRefData& rRef1 = t->GetSingleRef();
- ScSingleRefData& rRef2 = (eType == svDoubleRef ?
- t->GetDoubleRef().Ref2 : rRef1);
- switch( eType )
- {
- case svSingleRef:
- rRef1.CalcAbsIfRel( aCellPos );
- if ( rRef1.Valid() )
- {
- pDoc->EndListeningCell(
- ScAddress( rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab ), this );
- }
- break;
- case svDoubleRef:
- t->CalcAbsIfRel( aCellPos );
- if ( rRef1.Valid() && rRef2.Valid() )
- {
- if ( t->GetOpCode() == ocColRowNameAuto )
- { // automagically
- if ( rRef1.IsColRel() )
- { // ColName
- pDoc->EndListeningArea( ScRange (
- rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab,
- rRef2.nCol,
- MAXROW,
- rRef2.nTab ), this );
- }
- else
- { // RowName
- pDoc->EndListeningArea( ScRange (
- rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab,
- MAXCOL,
- rRef2.nRow,
- rRef2.nTab ), this );
- }
- }
- else
- {
- pDoc->EndListeningArea( ScRange (
- rRef1.nCol,
- rRef1.nRow,
- rRef1.nTab,
- rRef2.nCol,
- rRef2.nRow,
- rRef2.nTab ), this );
- }
- }
- break;
- default:
- ; // nothing
- }
- }
-}
-
-// ============================================================================
-
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/cellvalue.cxx b/sc/source/core/data/cellvalue.cxx
index 7ef5bda45a84..c287f10e1fb5 100644
--- a/sc/source/core/data/cellvalue.cxx
+++ b/sc/source/core/data/cellvalue.cxx
@@ -10,6 +10,7 @@
#include "cellvalue.hxx"
#include "document.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
#include "editeng/editobj.hxx"
#include "editeng/editstat.hxx"
#include "stringutil.hxx"
diff --git a/sc/source/core/data/colorscale.cxx b/sc/source/core/data/colorscale.cxx
index f7d3493e83c5..c9aa3cbc86ae 100644
--- a/sc/source/core/data/colorscale.cxx
+++ b/sc/source/core/data/colorscale.cxx
@@ -28,7 +28,7 @@
#include "colorscale.hxx"
#include "document.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "fillinfo.hxx"
#include "iconsets.hrc"
#include "scresid.hxx"
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index 1712fbe04b1c..c04619210baf 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -20,6 +20,7 @@
#include "column.hxx"
#include "scitems.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "docpool.hxx"
#include "attarray.hxx"
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index e4a9931a256f..2e878e28b280 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -37,6 +37,7 @@
#include "column.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "docpool.hxx"
#include "attarray.hxx"
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 9bd0ee9400f8..bd7586e963d1 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -29,6 +29,7 @@
#include "scitems.hxx"
#include "column.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "attarray.hxx"
#include "patattr.hxx"
@@ -2089,15 +2090,6 @@ xub_StrLen ScColumn::GetMaxNumberStringLen(
return nStringLen;
}
-
-ScFormulaCellGroup::ScFormulaCellGroup()
-{
-}
-
-ScFormulaCellGroup::~ScFormulaCellGroup()
-{
-}
-
// Very[!] slow way to look for and merge contiguous runs
// of similar formulae into a formulagroup
void ScColumn::RebuildFormulaGroups()
diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx
index 0ce3088eddb1..237c7f08da68 100644
--- a/sc/source/core/data/conditio.cxx
+++ b/sc/source/core/data/conditio.cxx
@@ -27,7 +27,7 @@
#include <com/sun/star/sheet/ConditionOperator2.hpp>
#include "conditio.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "hints.hxx"
#include "compiler.hxx"
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
index 187551893717..553c38f2aa5d 100644
--- a/sc/source/core/data/dociter.cxx
+++ b/sc/source/core/data/dociter.cxx
@@ -26,6 +26,7 @@
#include "table.hxx"
#include "column.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
#include "attarray.hxx"
#include "patattr.hxx"
#include "docoptio.hxx"
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index 90cc920dbd32..bc6174018dfe 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -85,6 +85,7 @@
#include "clipparam.hxx"
#include "macromgr.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
using namespace com::sun::star;
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
index 3196b2e446d3..07bc970136dd 100644
--- a/sc/source/core/data/documen3.cxx
+++ b/sc/source/core/data/documen3.cxx
@@ -69,6 +69,7 @@
#include "sheetevents.hxx"
#include "colorscale.hxx"
#include "queryentry.hxx"
+#include "formulacell.hxx"
#include "globalnames.hxx"
#include <memory>
diff --git a/sc/source/core/data/documen4.cxx b/sc/source/core/data/documen4.cxx
index 27fd9fa7a8ff..6dfad050c071 100644
--- a/sc/source/core/data/documen4.cxx
+++ b/sc/source/core/data/documen4.cxx
@@ -42,6 +42,7 @@
#include "colorscale.hxx"
#include "attrib.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
using namespace formula;
diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx
index 96ebc365279d..85c3cf9061c1 100644
--- a/sc/source/core/data/documen7.cxx
+++ b/sc/source/core/data/documen7.cxx
@@ -23,6 +23,7 @@
#include "brdcst.hxx"
#include "bcaslot.hxx"
#include "cell.hxx"
+#include "formulacell.hxx"
#include "formula/errorcodes.hxx" // errCircularReference
#include "scerrors.hxx"
#include "docoptio.hxx"
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 9aa8e6a3bfae..81ca67618e2d 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -89,6 +89,7 @@
#include "editutil.hxx"
#include "stringutil.hxx"
#include "formulaiter.hxx"
+#include "formulacell.hxx"
#include <map>
#include <limits>
diff --git a/sc/source/core/data/dpfilteredcache.cxx b/sc/source/core/data/dpfilteredcache.cxx
index 0df77891f40e..64381e07ea78 100644
--- a/sc/source/core/data/dpfilteredcache.cxx
+++ b/sc/source/core/data/dpfilteredcache.cxx
@@ -20,7 +20,7 @@
#include "dpfilteredcache.hxx"
#include "document.hxx"
#include "address.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "dptabdat.hxx"
#include "dptabsrc.hxx"
#include "dpobject.hxx"
diff --git a/sc/source/core/data/dpitemdata.cxx b/sc/source/core/data/dpitemdata.cxx
index 4c04106f20eb..a2d7bb74fb1e 100644
--- a/sc/source/core/data/dpitemdata.cxx
+++ b/sc/source/core/data/dpitemdata.cxx
@@ -21,7 +21,7 @@
#include "document.hxx"
#include "dpobject.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "globstr.hrc"
#include "dptabdat.hxx"
#include "rtl/math.hxx"
diff --git a/sc/source/core/data/dpshttab.cxx b/sc/source/core/data/dpshttab.cxx
index ccf4659cdf94..f1cf22735dcd 100644
--- a/sc/source/core/data/dpshttab.cxx
+++ b/sc/source/core/data/dpshttab.cxx
@@ -22,7 +22,7 @@
#include "dpshttab.hxx"
#include "dptabres.hxx"
#include "document.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "dpfilteredcache.hxx"
#include "dpobject.hxx"
#include "globstr.hrc"
diff --git a/sc/source/core/data/dptabsrc.cxx b/sc/source/core/data/dptabsrc.cxx
index 20ff80b3a391..91dcacc363e2 100644
--- a/sc/source/core/data/dptabsrc.cxx
+++ b/sc/source/core/data/dptabsrc.cxx
@@ -34,7 +34,7 @@
#include "document.hxx"
#include "docpool.hxx"
#include "patattr.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "dptabres.hxx"
#include "dptabdat.hxx"
diff --git a/sc/source/core/data/fillinfo.cxx b/sc/source/core/data/fillinfo.cxx
index 5a04751839af..1e37c6deaa9e 100644
--- a/sc/source/core/data/fillinfo.cxx
+++ b/sc/source/core/data/fillinfo.cxx
@@ -27,7 +27,7 @@
#include "fillinfo.hxx"
#include "document.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "table.hxx"
#include "attrib.hxx"
#include "attarray.hxx"
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
new file mode 100644
index 000000000000..00912d2f51e7
--- /dev/null
+++ b/sc/source/core/data/formulacell.cxx
@@ -0,0 +1,3208 @@
+/* -*- 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 "formulacell.hxx"
+
+#include "compiler.hxx"
+#include "document.hxx"
+#include "globalnames.hxx"
+#include "cellvalue.hxx"
+#include "interpre.hxx"
+#include "macromgr.hxx"
+#include "refupdat.hxx"
+#include "recursionhelper.hxx"
+#include "docoptio.hxx"
+#include "rangenam.hxx"
+#include "dbdata.hxx"
+#include "progress.hxx"
+#include "scmatrix.hxx"
+#include "rechead.hxx"
+#include "scitems.hxx"
+#include "validat.hxx"
+#include "editutil.hxx"
+#include "chgtrack.hxx"
+
+#include "formula/errorcodes.hxx"
+#include "svl/intitem.hxx"
+
+#include <boost/bind.hpp>
+
+using namespace formula;
+
+#ifdef USE_MEMPOOL
+IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell )
+#endif
+
+namespace {
+
+// More or less arbitrary, of course all recursions must fit into available
+// stack space (which is what on all systems we don't know yet?). Choosing a
+// lower value may be better than trying a much higher value that also isn't
+// sufficient but temporarily leads to high memory consumption. On the other
+// hand, if the value fits all recursions, execution is quicker as no resumes
+// are necessary. Could be made a configurable option.
+// Allow for a year's calendar (366).
+const sal_uInt16 MAXRECURSION = 400;
+
+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<ScToken*>::const_iterator aBegin,
+ const deque<ScToken*>::const_iterator aEnd,
+ bool& bCol, bool& bRow, bool& bTab)
+{
+ deque<ScToken*>::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)
+ {
+ 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<ScToken*>& rReferences,
+ const DimensionSelector aWhich)
+{
+ typedef deque<ScToken*>::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<ScToken*>& 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<ScToken*>& 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_FAIL( "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;
+}
+
+void adjustRangeName(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc, const ScAddress& aNewPos, const ScAddress& aOldPos)
+{
+ bool bOldGlobal = pToken->IsGlobal();
+ SCTAB aOldTab = aOldPos.Tab();
+ rtl::OUString aRangeName;
+ int nOldIndex = pToken->GetIndex();
+ ScRangeData* pOldRangeData = NULL;
+
+ //search the name of the RangeName
+ if (!bOldGlobal)
+ {
+ pOldRangeData = pOldDoc->GetRangeName(aOldTab)->findByIndex(nOldIndex);
+ if (!pOldRangeData)
+ return; //might be an error in the formula array
+ aRangeName = pOldRangeData->GetUpperName();
+ }
+ else
+ {
+ pOldRangeData = pOldDoc->GetRangeName()->findByIndex(nOldIndex);
+ if (!pOldRangeData)
+ return; //might be an error in the formula array
+ aRangeName = pOldRangeData->GetUpperName();
+ }
+
+ //find corresponding range name in new document
+ //first search for local range name then global range names
+ SCTAB aNewTab = aNewPos.Tab();
+ ScRangeName* pRangeName = rNewDoc.GetRangeName(aNewTab);
+ ScRangeData* pRangeData = NULL;
+ bool bNewGlobal = false;
+ //search local range names
+ if (pRangeName)
+ {
+ pRangeData = pRangeName->findByUpperName(aRangeName);
+ }
+ //search global range names
+ if (!pRangeData)
+ {
+ bNewGlobal = true;
+ pRangeName = rNewDoc.GetRangeName();
+ if (pRangeName)
+ pRangeData = pRangeName->findByUpperName(aRangeName);
+ }
+ //if no range name was found copy it
+ if (!pRangeData)
+ {
+ bNewGlobal = bOldGlobal;
+ pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc);
+ ScTokenArray* pRangeNameToken = pRangeData->GetCode();
+ if (rNewDoc.GetPool() != const_cast<ScDocument*>(pOldDoc)->GetPool())
+ {
+ pRangeNameToken->ReadjustAbsolute3DReferences(pOldDoc, &rNewDoc, pRangeData->GetPos(), true);
+ pRangeNameToken->AdjustAbsoluteRefs(pOldDoc, aOldPos, aNewPos, false, true);
+ }
+
+ bool bInserted;
+ if (bNewGlobal)
+ bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
+ else
+ bInserted = rNewDoc.GetRangeName(aNewTab)->insert(pRangeData);
+ if (!bInserted)
+ {
+ //if this happened we have a real problem
+ pRangeData = NULL;
+ pToken->SetIndex(0);
+ OSL_FAIL("inserting the range name should not fail");
+ return;
+ }
+ }
+ sal_Int32 nIndex = pRangeData->GetIndex();
+ pToken->SetIndex(nIndex);
+ pToken->SetGlobal(bNewGlobal);
+}
+
+void adjustDBRange(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
+{
+ ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
+ if (!pOldDBCollection)
+ return;//strange error case, don't do anything
+ ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
+ ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
+ if (!pDBData)
+ return; //invalid index
+ rtl::OUString aDBName = pDBData->GetUpperName();
+
+ //search in new document
+ ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
+ if (!pNewDBCollection)
+ {
+ pNewDBCollection = new ScDBCollection(&rNewDoc);
+ rNewDoc.SetDBCollection(pNewDBCollection);
+ }
+ ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
+ ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
+ if (!pNewDBData)
+ {
+ pNewDBData = new ScDBData(*pDBData);
+ aNewNamedDBs.insert(pNewDBData);
+ }
+ pToken->SetIndex(pNewDBData->GetIndex());
+}
+
+}
+
+ScFormulaCellGroup::ScFormulaCellGroup()
+{
+}
+
+ScFormulaCellGroup::~ScFormulaCellGroup()
+{
+}
+
+// ============================================================================
+
+ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
+ const rtl::OUString& rFormula,
+ const FormulaGrammar::Grammar eGrammar,
+ sal_uInt8 cMatInd ) :
+ ScBaseCell( CELLTYPE_FORMULA ),
+ eTempGrammar( eGrammar),
+ pCode( NULL ),
+ pDocument( pDoc ),
+ pPrevious(0),
+ pNext(0),
+ pPreviousTrack(0),
+ pNextTrack(0),
+ nFormatIndex(0),
+ nFormatType( NUMBERFORMAT_NUMBER ),
+ nSeenInIteration(0),
+ cMatrixFlag ( cMatInd ),
+ bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
+ bChanged( false ),
+ bRunning( false ),
+ bCompile( false ),
+ bSubTotal( false ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bTableOpDirty( false ),
+ bNeedListening( false ),
+ aPos( rPos )
+{
+ Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
+ if (!pCode)
+ // We need to have a non-NULL token array instance at all times.
+ pCode = new ScTokenArray;
+}
+
+// Used by import filters
+
+ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
+ const ScTokenArray* pArr,
+ const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) :
+ ScBaseCell( CELLTYPE_FORMULA ),
+ eTempGrammar( eGrammar),
+ pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ),
+ pDocument( pDoc ),
+ pPrevious(0),
+ pNext(0),
+ pPreviousTrack(0),
+ pNextTrack(0),
+ nFormatIndex(0),
+ nFormatType( NUMBERFORMAT_NUMBER ),
+ nSeenInIteration(0),
+ cMatrixFlag ( cInd ),
+ bDirty( NULL != pArr ), // -> Because of the use of the Auto Pilot Function was: cInd != 0
+ bChanged( false ),
+ bRunning( false ),
+ bCompile( false ),
+ bSubTotal( false ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bTableOpDirty( false ),
+ bNeedListening( false ),
+ aPos( rPos )
+{
+ // UPN-Array generation
+ if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
+ {
+ ScCompiler aComp( pDocument, aPos, *pCode);
+ aComp.SetGrammar(eTempGrammar);
+ bSubTotal = aComp.CompileTokenArray();
+ nFormatType = aComp.GetNumFormatType();
+ }
+ else
+ {
+ pCode->Reset();
+ if ( pCode->GetNextOpCodeRPN( ocSubTotal ) )
+ bSubTotal = true;
+ }
+
+ if (bSubTotal)
+ pDocument->AddSubTotalCell(this);
+}
+
+ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
+ ScBaseCell( rCell ),
+ SvtListener(),
+ aResult( rCell.aResult ),
+ eTempGrammar( rCell.eTempGrammar),
+ pDocument( &rDoc ),
+ pPrevious(0),
+ pNext(0),
+ pPreviousTrack(0),
+ pNextTrack(0),
+ nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ),
+ nFormatType( rCell.nFormatType ),
+ nSeenInIteration(0),
+ cMatrixFlag ( rCell.cMatrixFlag ),
+ bDirty( rCell.bDirty ),
+ bChanged( rCell.bChanged ),
+ bRunning( false ),
+ bCompile( rCell.bCompile ),
+ bSubTotal( rCell.bSubTotal ),
+ bIsIterCell( false ),
+ bInChangeTrack( false ),
+ bTableOpDirty( false ),
+ bNeedListening( false ),
+ aPos( rPos )
+{
+ pCode = rCell.pCode->Clone();
+
+ // set back any errors and recompile
+ // not in the Clipboard - it must keep the received error flag
+ // Special Length=0: as bad cells are generated, then they are also retained
+ if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
+ {
+ pCode->SetCodeError( 0 );
+ bCompile = true;
+ }
+ //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
+ bool bCompileLater = false;
+ bool bClipMode = rCell.pDocument->IsClipboard();
+
+ //update ScNameTokens
+ if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
+ {
+ if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
+ {
+ ScToken* pToken = NULL;
+ while((pToken = static_cast<ScToken*>(pCode->GetNextName()))!= NULL)
+ {
+ OpCode eOpCode = pToken->GetOpCode();
+ if (eOpCode == ocName)
+ adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos);
+ else if (eOpCode == ocDBArea)
+ adjustDBRange(pToken, rDoc, rCell.pDocument);
+ }
+ }
+
+ bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
+ if (bCopyBetweenDocs && !(nCloneFlags & SC_CLONECELL_NOMAKEABS_EXTERNAL))
+ {
+ pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
+ }
+
+ pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, false, bCopyBetweenDocs );
+ }
+
+ if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
+ pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
+
+ if( !bCompile )
+ { // Name references with references and ColRowNames
+ pCode->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
+ {
+ if ( t->IsExternalRef() )
+ {
+ // External name, cell, and area references.
+ bCompile = true;
+ }
+ else if ( t->GetType() == svIndex )
+ {
+ ScRangeData* pRangeData = rDoc.GetRangeName()->findByIndex( t->GetIndex() );
+ if( pRangeData )
+ {
+ if( pRangeData->HasReferences() )
+ bCompile = true;
+ }
+ else
+ bCompile = true; // invalid reference!
+ }
+ else if ( t->GetOpCode() == ocColRowName )
+ {
+ bCompile = true; // new lookup needed
+ bCompileLater = bClipMode;
+ }
+ }
+ }
+ if( bCompile )
+ {
+ if ( !bCompileLater && bClipMode )
+ {
+ // Merging ranges needs the actual positions after UpdateReference.
+ // ColRowNames need new lookup after positions are adjusted.
+ bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName);
+ }
+ if ( !bCompileLater )
+ {
+ // bNoListening, not at all if in Clipboard/Undo,
+ // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
+ CompileTokenArray( true );
+ }
+ }
+
+ if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
+ StartListeningTo( &rDoc );
+
+ if (bSubTotal)
+ pDocument->AddSubTotalCell(this);
+}
+
+ScFormulaCell::~ScFormulaCell()
+{
+ pDocument->RemoveFromFormulaTree( this );
+ pDocument->RemoveSubTotalCell(this);
+ if (pCode->HasOpCode(ocMacro))
+ pDocument->GetMacroManager()->RemoveDependentCell(this);
+
+ if (pDocument->HasExternalRefManager())
+ pDocument->GetExternalRefManager()->removeRefCell(this);
+
+ delete pCode;
+#if OSL_DEBUG_LEVEL > 0
+ eCellType = CELLTYPE_DESTROYED;
+#endif
+}
+
+ScFormulaCell* ScFormulaCell::Clone() const
+{
+ return new ScFormulaCell(*this, *pDocument, aPos);
+}
+
+size_t ScFormulaCell::GetHash() const
+{
+ return pCode->GetHash();
+}
+
+ScFormulaVectorState ScFormulaCell::GetVectorState() const
+{
+ return pCode->GetVectorState();
+}
+
+void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer,
+ const FormulaGrammar::Grammar eGrammar ) const
+{
+ if( pCode->GetCodeError() && !pCode->GetLen() )
+ {
+ rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
+ return;
+ }
+ else if( cMatrixFlag == MM_REFERENCE )
+ {
+ // Reference to another cell that contains a matrix formula.
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if( p )
+ {
+ /* FIXME: original GetFormula() code obtained
+ * pCell only if (!this->IsInChangeTrack()),
+ * GetEnglishFormula() omitted that test.
+ * Can we live without in all cases? */
+ ScFormulaCell* pCell = NULL;
+ ScSingleRefData& rRef = p->GetSingleRef();
+ rRef.CalcAbsIfRel( aPos );
+ if ( rRef.Valid() )
+ pCell = pDocument->GetFormulaCell(
+ ScAddress(rRef.nCol, rRef.nRow, rRef.nTab));
+
+ if (pCell)
+ {
+ pCell->GetFormula( rBuffer, eGrammar);
+ return;
+ }
+ else
+ {
+ ScCompiler aComp( pDocument, aPos, *pCode);
+ aComp.SetGrammar(eGrammar);
+ aComp.CreateStringFromTokenArray( rBuffer );
+ }
+ }
+ else
+ {
+ OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
+ }
+ }
+ else
+ {
+ ScCompiler aComp( pDocument, aPos, *pCode);
+ aComp.SetGrammar(eGrammar);
+ aComp.CreateStringFromTokenArray( rBuffer );
+ }
+
+ sal_Unicode ch('=');
+ rBuffer.insert( 0, &ch, 1 );
+ if( cMatrixFlag )
+ {
+ sal_Unicode ch2('{');
+ rBuffer.insert( 0, &ch2, 1);
+ rBuffer.append( sal_Unicode('}'));
+ }
+}
+
+void ScFormulaCell::GetFormula( rtl::OUString& rFormula, const FormulaGrammar::Grammar eGrammar ) const
+{
+ rtl::OUStringBuffer rBuffer( rFormula );
+ GetFormula( rBuffer, eGrammar );
+ rFormula = rBuffer.makeStringAndClear();
+}
+
+void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
+{
+ MaybeInterpret();
+
+ const ScMatrix* pMat = NULL;
+ if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell &&
+ ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0))
+ pMat->GetDimensions( rCols, rRows );
+ else
+ {
+ rCols = 0;
+ rRows = 0;
+ }
+}
+
+void ScFormulaCell::Compile( const rtl::OUString& rFormula, bool bNoListening,
+ const FormulaGrammar::Grammar eGrammar )
+{
+ if ( pDocument->IsClipOrUndo() )
+ return;
+ bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ pDocument->RemoveFromFormulaTree( this );
+ // pCode may not deleted for queries, but must be empty
+ if ( pCode )
+ pCode->Clear();
+ ScTokenArray* pCodeOld = pCode;
+ ScCompiler aComp( pDocument, aPos);
+ aComp.SetGrammar(eGrammar);
+ pCode = aComp.CompileString( rFormula );
+ if ( pCodeOld )
+ delete pCodeOld;
+ if( !pCode->GetCodeError() )
+ {
+ if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
+ { // not recursive CompileTokenArray/Compile/CompileTokenArray
+ if ( rFormula[0] == '=' )
+ pCode->AddBad( rFormula.copy(1) );
+ else
+ pCode->AddBad( rFormula );
+ }
+ bCompile = true;
+ CompileTokenArray( bNoListening );
+ }
+ else
+ {
+ bChanged = true;
+ pDocument->SetTextWidth(aPos, TEXTWIDTH_DIRTY);
+ pDocument->SetScriptType(aPos, SC_SCRIPTTYPE_UNKNOWN);
+ }
+ if ( bWasInFormulaTree )
+ pDocument->PutInFormulaTree( this );
+}
+
+
+void ScFormulaCell::CompileTokenArray( bool bNoListening )
+{
+ // Not already compiled?
+ if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
+ else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
+ {
+ // RPN length may get changed
+ bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ pDocument->RemoveFromFormulaTree( this );
+
+ // Loading from within filter? No listening yet!
+ if( pDocument->IsInsertingFromOtherDoc() )
+ bNoListening = true;
+
+ if( !bNoListening && pCode->GetCodeLen() )
+ EndListeningTo( pDocument );
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ bSubTotal = aComp.CompileTokenArray();
+ if( !pCode->GetCodeError() )
+ {
+ nFormatType = aComp.GetNumFormatType();
+ nFormatIndex = 0;
+ bChanged = true;
+ aResult.SetToken( NULL);
+ bCompile = false;
+ if ( !bNoListening )
+ StartListeningTo( pDocument );
+ }
+ if ( bWasInFormulaTree )
+ pDocument->PutInFormulaTree( this );
+
+ if (bSubTotal)
+ pDocument->AddSubTotalCell(this);
+ }
+}
+
+
+void ScFormulaCell::CompileXML( ScProgress& rProgress )
+{
+ if ( cMatrixFlag == MM_REFERENCE )
+ { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
+ // just establish listeners
+ StartListeningTo( pDocument );
+ return ;
+ }
+
+ ScCompiler aComp( pDocument, aPos, *pCode);
+ aComp.SetGrammar(eTempGrammar);
+ rtl::OUString aFormula, aFormulaNmsp;
+ aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
+ pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
+ rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
+ // pCode may not deleted for queries, but must be empty
+ if ( pCode )
+ pCode->Clear();
+ ScTokenArray* pCodeOld = pCode;
+ pCode = aComp.CompileString( aFormula, aFormulaNmsp );
+ delete pCodeOld;
+ if( !pCode->GetCodeError() )
+ {
+ if ( !pCode->GetLen() )
+ {
+ if ( aFormula[0] == '=' )
+ pCode->AddBad( aFormula.copy( 1 ) );
+ else
+ pCode->AddBad( aFormula );
+ }
+ bSubTotal = aComp.CompileTokenArray();
+ if( !pCode->GetCodeError() )
+ {
+ nFormatType = aComp.GetNumFormatType();
+ nFormatIndex = 0;
+ bChanged = true;
+ bCompile = false;
+ StartListeningTo( pDocument );
+ }
+
+ if (bSubTotal)
+ pDocument->AddSubTotalCell(this);
+ }
+ else
+ {
+ bChanged = true;
+ pDocument->SetTextWidth(aPos, TEXTWIDTH_DIRTY);
+ pDocument->SetScriptType(aPos, SC_SCRIPTTYPE_UNKNOWN);
+ }
+
+ // Same as in Load: after loading, it must be known if ocMacro is in any formula
+ // (for macro warning, CompileXML is called at the end of loading XML file)
+ if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
+ pDocument->SetHasMacroFunc( true );
+
+ //volatile cells must be added here for import
+ if( pCode->IsRecalcModeAlways() || pCode->IsRecalcModeForced() ||
+ pCode->IsRecalcModeOnLoad() || pCode->IsRecalcModeOnLoadOnce() )
+ {
+ // During load, only those cells that are marked explicitly dirty get
+ // recalculated. So we need to set it dirty here.
+ SetDirtyVar();
+ pDocument->PutInFormulaTree(this);
+ }
+}
+
+
+void ScFormulaCell::CalcAfterLoad()
+{
+ bool bNewCompiled = false;
+ // If a Calc 1.0-doc is read, we have a result, but no token array
+ if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ Compile( aResult.GetHybridFormula(), true, eTempGrammar);
+ aResult.SetToken( NULL);
+ bDirty = true;
+ bNewCompiled = true;
+ }
+ // The UPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
+ if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
+ {
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ bSubTotal = aComp.CompileTokenArray();
+ nFormatType = aComp.GetNumFormatType();
+ nFormatIndex = 0;
+ bDirty = true;
+ bCompile = false;
+ bNewCompiled = true;
+
+ if (bSubTotal)
+ pDocument->AddSubTotalCell(this);
+ }
+
+ // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
+ // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
+ // We iron this out here for all systems, such that we also have an Err503 here.
+ if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
+ {
+ OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
+ aResult.SetResultError( errIllegalFPOperation );
+ bDirty = true;
+ }
+
+ // DoubleRefs for binary operators were always a Matrix before version v5.0.
+ // Now this is only the case when when in an array formula, otherwise it's an implicit intersection
+ if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
+ GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
+ {
+ cMatrixFlag = MM_FORMULA;
+ SetMatColsRows( 1, 1);
+ }
+
+ // Do the cells need to be calculated? After Load cells can contain an error code, and then start
+ // the listener and Recalculate (if needed) if not RECALCMODE_NORMAL
+ if( !bNewCompiled || !pCode->GetCodeError() )
+ {
+ StartListeningTo( pDocument );
+ if( !pCode->IsRecalcModeNormal() )
+ bDirty = true;
+ }
+ if ( pCode->IsRecalcModeAlways() )
+ { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
+ // for each F9
+ bDirty = true;
+ }
+ // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
+}
+
+
+bool ScFormulaCell::MarkUsedExternalReferences()
+{
+ return pCode && pDocument->MarkUsedExternalReferences( *pCode);
+}
+
+
+void ScFormulaCell::Interpret()
+{
+ if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
+ return; // no double/triple processing
+
+ //! HACK:
+ // If the call originates from a Reschedule in DdeLink update, leave dirty
+ // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
+ if ( pDocument->IsInDdeLinkUpdate() )
+ return;
+
+ if (bRunning)
+ {
+ if (!pDocument->GetDocOptions().IsIter())
+ {
+ aResult.SetResultError( errCircularReference );
+ return;
+ }
+
+ if (aResult.GetResultError() == errCircularReference)
+ aResult.SetResultError( 0 );
+
+ // Start or add to iteration list.
+ if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
+ !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
+ pDocument->GetRecursionHelper().SetInIterationReturn( true);
+
+ return;
+ }
+ // no multiple interprets for GetErrCode, IsValue, GetValue and
+ // different entry point recursions. Would also lead to premature
+ // convergence in iterations.
+ if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
+ pDocument->GetRecursionHelper().GetIteration())
+ return ;
+
+ ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
+ bool bOldRunning = bRunning;
+ if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
+ {
+ bRunning = true;
+ rRecursionHelper.SetInRecursionReturn( true);
+ }
+ else
+ {
+ if ( ! InterpretFormulaGroup() )
+ InterpretTail( SCITP_NORMAL);
+ }
+
+ // While leaving a recursion or iteration stack, insert its cells to the
+ // recursion list in reverse order.
+ if (rRecursionHelper.IsInReturn())
+ {
+ if (rRecursionHelper.GetRecursionCount() > 0 ||
+ !rRecursionHelper.IsDoingRecursion())
+ rRecursionHelper.Insert( this, bOldRunning, aResult);
+ bool bIterationFromRecursion = false;
+ bool bResumeIteration = false;
+ do
+ {
+ if ((rRecursionHelper.IsInIterationReturn() &&
+ rRecursionHelper.GetRecursionCount() == 0 &&
+ !rRecursionHelper.IsDoingIteration()) ||
+ bIterationFromRecursion || bResumeIteration)
+ {
+ ScFormulaCell* pIterCell = this; // scope for debug convenience
+ bool & rDone = rRecursionHelper.GetConvergingReference();
+ rDone = false;
+ if (!bIterationFromRecursion && bResumeIteration)
+ {
+ bResumeIteration = false;
+ // Resuming iteration expands the range.
+ ScFormulaRecursionList::const_iterator aOldStart(
+ rRecursionHelper.GetLastIterationStart());
+ rRecursionHelper.ResumeIteration();
+ // Mark new cells being in iteration.
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart()); aIter !=
+ aOldStart; ++aIter)
+ {
+ pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = true;
+ }
+ // Mark older cells dirty again, in case they converted
+ // without accounting for all remaining cells in the circle
+ // that weren't touched so far, e.g. conditional. Restore
+ // backuped result.
+ sal_uInt16 nIteration = rRecursionHelper.GetIteration();
+ for (ScFormulaRecursionList::const_iterator aIter(
+ aOldStart); aIter !=
+ rRecursionHelper.GetIterationEnd(); ++aIter)
+ {
+ pIterCell = (*aIter).pCell;
+ if (pIterCell->nSeenInIteration == nIteration)
+ {
+ if (!pIterCell->bDirty || aIter == aOldStart)
+ {
+ pIterCell->aResult = (*aIter).aPreviousResult;
+ }
+ --pIterCell->nSeenInIteration;
+ }
+ pIterCell->bDirty = true;
+ }
+ }
+ else
+ {
+ bResumeIteration = false;
+ // Close circle once.
+ rRecursionHelper.GetList().back().pCell->InterpretTail(
+ SCITP_CLOSE_ITERATION_CIRCLE);
+ // Start at 1, init things.
+ rRecursionHelper.StartIteration();
+ // Mark all cells being in iteration.
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart()); aIter !=
+ rRecursionHelper.GetIterationEnd(); ++aIter)
+ {
+ pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = true;
+ }
+ }
+ bIterationFromRecursion = false;
+ sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
+ for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
+ rRecursionHelper.IncIteration())
+ {
+ rDone = true;
+ for ( ScFormulaRecursionList::iterator aIter(
+ rRecursionHelper.GetIterationStart()); aIter !=
+ rRecursionHelper.GetIterationEnd() &&
+ !rRecursionHelper.IsInReturn(); ++aIter)
+ {
+ pIterCell = (*aIter).pCell;
+ if (pIterCell->IsDirtyOrInTableOpDirty() &&
+ rRecursionHelper.GetIteration() !=
+ pIterCell->GetSeenInIteration())
+ {
+ (*aIter).aPreviousResult = pIterCell->aResult;
+ pIterCell->InterpretTail( SCITP_FROM_ITERATION);
+ }
+ rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
+ }
+ if (rRecursionHelper.IsInReturn())
+ {
+ bResumeIteration = true;
+ break; // for
+ // Don't increment iteration.
+ }
+ }
+ if (!bResumeIteration)
+ {
+ if (rDone)
+ {
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart());
+ aIter != rRecursionHelper.GetIterationEnd();
+ ++aIter)
+ {
+ pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = false;
+ pIterCell->nSeenInIteration = 0;
+ pIterCell->bRunning = (*aIter).bOldRunning;
+ }
+ }
+ else
+ {
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart());
+ aIter != rRecursionHelper.GetIterationEnd();
+ ++aIter)
+ {
+ pIterCell = (*aIter).pCell;
+ pIterCell->bIsIterCell = false;
+ pIterCell->nSeenInIteration = 0;
+ pIterCell->bRunning = (*aIter).bOldRunning;
+ // If one cell didn't converge, all cells of this
+ // circular dependency don't, no matter whether
+ // single cells did.
+ pIterCell->bDirty = false;
+ pIterCell->bTableOpDirty = false;
+ pIterCell->aResult.SetResultError( errNoConvergence);
+ pIterCell->bChanged = true;
+ pDocument->SetTextWidth(pIterCell->aPos, TEXTWIDTH_DIRTY);
+ pDocument->SetScriptType(pIterCell->aPos, SC_SCRIPTTYPE_UNKNOWN);
+ }
+ }
+ // End this iteration and remove entries.
+ rRecursionHelper.EndIteration();
+ bResumeIteration = rRecursionHelper.IsDoingIteration();
+ }
+ }
+ if (rRecursionHelper.IsInRecursionReturn() &&
+ rRecursionHelper.GetRecursionCount() == 0 &&
+ !rRecursionHelper.IsDoingRecursion())
+ {
+ bIterationFromRecursion = false;
+ // Iterate over cells known so far, start with the last cell
+ // encountered, inserting new cells if another recursion limit
+ // is reached. Repeat until solved.
+ rRecursionHelper.SetDoingRecursion( true);
+ do
+ {
+ rRecursionHelper.SetInRecursionReturn( false);
+ for (ScFormulaRecursionList::const_iterator aIter(
+ rRecursionHelper.GetIterationStart());
+ !rRecursionHelper.IsInReturn() && aIter !=
+ rRecursionHelper.GetIterationEnd(); ++aIter)
+ {
+ ScFormulaCell* pCell = (*aIter).pCell;
+ if (pCell->IsDirtyOrInTableOpDirty())
+ {
+ pCell->InterpretTail( SCITP_NORMAL);
+ if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
+ pCell->bRunning = (*aIter).bOldRunning;
+ }
+ }
+ } while (rRecursionHelper.IsInRecursionReturn());
+ rRecursionHelper.SetDoingRecursion( false);
+ if (rRecursionHelper.IsInIterationReturn())
+ {
+ if (!bResumeIteration)
+ bIterationFromRecursion = true;
+ }
+ else if (bResumeIteration ||
+ rRecursionHelper.IsDoingIteration())
+ rRecursionHelper.GetList().erase(
+ rRecursionHelper.GetIterationStart(),
+ rRecursionHelper.GetLastIterationStart());
+ else
+ rRecursionHelper.Clear();
+ }
+ } while (bIterationFromRecursion || bResumeIteration);
+ }
+}
+
+void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
+{
+ class RecursionCounter
+ {
+ ScRecursionHelper& rRec;
+ bool bStackedInIteration;
+ public:
+ RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
+ {
+ bStackedInIteration = rRec.IsDoingIteration();
+ if (bStackedInIteration)
+ rRec.GetRecursionInIterationStack().push( p);
+ rRec.IncRecursionCount();
+ }
+ ~RecursionCounter()
+ {
+ rRec.DecRecursionCount();
+ if (bStackedInIteration)
+ rRec.GetRecursionInIterationStack().pop();
+ }
+ } aRecursionCounter( pDocument->GetRecursionHelper(), this);
+ nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
+ if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
+ {
+ // #i11719# no UPN and no error and no token code but result string present
+ // => interpretation of this cell during name-compilation and unknown names
+ // => can't exchange underlying code array in CompileTokenArray() /
+ // Compile() because interpreter's token iterator would crash or pCode
+ // would be deleted twice if this cell was interpreted during
+ // compilation.
+ // This should only be a temporary condition and, since we set an
+ // error, if ran into it again we'd bump into the dirty-clearing
+ // condition further down.
+ if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ pCode->SetCodeError( errNoCode );
+ // This is worth an assertion; if encountered in daily work
+ // documents we might need another solution. Or just confirm correctness.
+ OSL_FAIL( "ScFormulaCell::Interpret: no UPN, no error, no token, but hybrid formula string" );
+ return;
+ }
+ CompileTokenArray();
+ }
+
+ if( pCode->GetCodeLen() && pDocument )
+ {
+ class StackCleaner
+ {
+ ScDocument* pDoc;
+ ScInterpreter* pInt;
+ public:
+ StackCleaner( ScDocument* pD, ScInterpreter* pI )
+ : pDoc(pD), pInt(pI)
+ {}
+ ~StackCleaner()
+ {
+ delete pInt;
+ pDoc->DecInterpretLevel();
+ }
+ };
+ pDocument->IncInterpretLevel();
+ ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
+ StackCleaner aStackCleaner( pDocument, p);
+ sal_uInt16 nOldErrCode = aResult.GetResultError();
+ if ( nSeenInIteration == 0 )
+ { // Only the first time
+ // With bChanged=false, if a newly compiled cell has a result of
+ // 0.0, no change is detected and the cell will not be repainted.
+ // bChanged = false;
+ aResult.SetResultError( 0 );
+ }
+
+ switch ( aResult.GetResultError() )
+ {
+ case errCircularReference : // will be determined again if so
+ aResult.SetResultError( 0 );
+ break;
+ }
+
+ bool bOldRunning = bRunning;
+ bRunning = true;
+ p->Interpret();
+ if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
+ {
+ if (nSeenInIteration > 0)
+ --nSeenInIteration; // retry when iteration is resumed
+ return;
+ }
+ bRunning = bOldRunning;
+
+ // #i102616# For single-sheet saving consider only content changes, not format type,
+ // because format type isn't set on loading (might be changed later)
+ bool bContentChanged = false;
+
+ // Do not create a HyperLink() cell if the formula results in an error.
+ if( p->GetError() && pCode->IsHyperLink())
+ pCode->SetHyperLink(false);
+
+ if( p->GetError() && p->GetError() != errCircularReference)
+ {
+ bDirty = false;
+ bTableOpDirty = false;
+ bChanged = true;
+ }
+ if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
+ {
+ bool bIsValue = aResult.IsValue(); // the previous type
+ // Did it converge?
+ if ((bIsValue && p->GetResultType() == svDouble && fabs(
+ p->GetNumResult() - aResult.GetDouble()) <=
+ pDocument->GetDocOptions().GetIterEps()) ||
+ (!bIsValue && p->GetResultType() == svString &&
+ p->GetStringResult() == aResult.GetString()))
+ {
+ // A convergence in the first iteration doesn't necessarily
+ // mean that it's done, it may be as not all related cells
+ // of a circle changed their values yet. If the set really
+ // converges it will do so also during the next iteration. This
+ // fixes situations like of #i44115#. If this wasn't wanted an
+ // initial "uncalculated" value would be needed for all cells
+ // of a circular dependency => graph needed before calculation.
+ if (nSeenInIteration > 1 ||
+ pDocument->GetDocOptions().GetIterCount() == 1)
+ {
+ bDirty = false;
+ bTableOpDirty = false;
+ }
+ }
+ }
+
+ // New error code?
+ if( p->GetError() != nOldErrCode )
+ {
+ bChanged = true;
+ // bContentChanged only has to be set if the file content would be changed
+ if ( aResult.GetCellResultType() != svUnknown )
+ bContentChanged = true;
+ }
+ // Different number format?
+ if( nFormatType != p->GetRetFormatType() )
+ {
+ nFormatType = p->GetRetFormatType();
+ bChanged = true;
+ }
+ if( nFormatIndex != p->GetRetFormatIndex() )
+ {
+ nFormatIndex = p->GetRetFormatIndex();
+ bChanged = true;
+ }
+
+ // In case of changes just obtain the result, no temporary and
+ // comparison needed anymore.
+ if (bChanged)
+ {
+ // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
+ // Also handle special cases of initial results after loading.
+ if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
+ {
+ ScFormulaResult aNewResult( p->GetResultToken().get());
+ StackVar eOld = aResult.GetCellResultType();
+ StackVar eNew = aNewResult.GetCellResultType();
+ if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
+ {
+ // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
+ // -> no change
+ }
+ else
+ {
+ if ( eOld == svHybridCell || eOld == svHybridValueCell ) // string result from SetFormulaResultString?
+ eOld = svString; // ScHybridCellToken has a valid GetString method
+
+ // #i106045# use approxEqual to compare with stored value
+ bContentChanged = (eOld != eNew ||
+ (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
+ (eNew == svString && aResult.GetString() != aNewResult.GetString()));
+ }
+ }
+
+ aResult.SetToken( p->GetResultToken().get() );
+ }
+ else
+ {
+ ScFormulaResult aNewResult( p->GetResultToken().get());
+ StackVar eOld = aResult.GetCellResultType();
+ StackVar eNew = aNewResult.GetCellResultType();
+ bChanged = (eOld != eNew ||
+ (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
+ (eNew == svString && aResult.GetString() != aNewResult.GetString()));
+
+ // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged)
+ if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
+ {
+ if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) ||
+ ( (eOld == svHybridCell || eOld == svHybridValueCell) && eNew == svString && aResult.GetString() == aNewResult.GetString() ) ||
+ ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) )
+ {
+ // no change, see above
+ }
+ else
+ bContentChanged = true;
+ }
+
+ aResult.Assign( aNewResult);
+ }
+
+ // Precision as shown?
+ if ( aResult.IsValue() && !p->GetError()
+ && pDocument->GetDocOptions().IsCalcAsShown()
+ && nFormatType != NUMBERFORMAT_DATE
+ && nFormatType != NUMBERFORMAT_TIME
+ && nFormatType != NUMBERFORMAT_DATETIME )
+ {
+ sal_uLong nFormat = pDocument->GetNumberFormat( aPos );
+ if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
+ nFormat = nFormatIndex;
+ if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
+ nFormat = ScGlobal::GetStandardFormat(
+ *pDocument->GetFormatTable(), nFormat, nFormatType );
+ aResult.SetDouble( pDocument->RoundValueAsShown(
+ aResult.GetDouble(), nFormat));
+ }
+ if (eTailParam == SCITP_NORMAL)
+ {
+ bDirty = false;
+ bTableOpDirty = false;
+ }
+ if( aResult.GetMatrix() )
+ {
+ // If the formula wasn't entered as a matrix formula, live on with
+ // the upper left corner and let reference counting delete the matrix.
+ if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
+ aResult.SetToken( aResult.GetCellResultToken().get());
+ }
+ if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
+ {
+ // Coded double error may occur via filter import.
+ sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble());
+ aResult.SetResultError( nErr);
+ bChanged = bContentChanged = true;
+ }
+ if( bChanged )
+ {
+ pDocument->SetTextWidth(aPos, TEXTWIDTH_DIRTY);
+ pDocument->SetScriptType(aPos, SC_SCRIPTTYPE_UNKNOWN);
+ }
+ if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
+ {
+ // pass bIgnoreLock=true, because even if called from pending row height update,
+ // a changed result must still reset the stream flag
+ pDocument->SetStreamValid(aPos.Tab(), false, true);
+ }
+ if ( !pCode->IsRecalcModeAlways() )
+ pDocument->RemoveFromFormulaTree( this );
+
+ // FORCED cells also immediately tested for validity (start macro possibly)
+
+ if ( pCode->IsRecalcModeForced() )
+ {
+ sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
+ aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
+ if ( nValidation )
+ {
+ const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
+ ScRefCellValue aTmpCell(this);
+ if ( pData && !pData->IsDataValid(aTmpCell, aPos))
+ pData->DoCalcError( this );
+ }
+ }
+
+ // Reschedule slows the whole thing down considerably, thus only execute on percent change
+ ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
+ pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
+
+ switch (p->GetVolatileType())
+ {
+ case ScInterpreter::VOLATILE:
+ // Volatile via built-in volatile functions. No actions needed.
+ break;
+ case ScInterpreter::VOLATILE_MACRO:
+ // The formula contains a volatile macro.
+ pCode->SetExclusiveRecalcModeAlways();
+ pDocument->PutInFormulaTree(this);
+ StartListeningTo(pDocument);
+ break;
+ case ScInterpreter::NOT_VOLATILE:
+ if (pCode->IsRecalcModeAlways())
+ {
+ // The formula was previously volatile, but no more.
+ EndListeningTo(pDocument);
+ pCode->SetExclusiveRecalcModeNormal();
+ }
+ else
+ {
+ // non-volatile formula. End listening to the area in case
+ // it's listening due to macro module change.
+ pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
+ }
+ pDocument->RemoveFromFormulaTree(this);
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ {
+ // Cells with compiler errors should not be marked dirty forever
+ OSL_ENSURE( pCode->GetCodeError(), "no UPN-Code und no errors ?!?!" );
+ bDirty = false;
+ bTableOpDirty = false;
+ }
+}
+
+
+void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows, bool bDirtyFlag )
+{
+ ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
+ if (pMat)
+ pMat->SetMatColsRows( nCols, nRows );
+ else if (nCols || nRows)
+ {
+ aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
+ // Setting the new token actually forces an empty result at this top
+ // left cell, so have that recalculated.
+ SetDirty( bDirtyFlag );
+ }
+}
+
+
+void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
+{
+ const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
+ if (pMat)
+ pMat->GetMatColsRows( nCols, nRows);
+ else
+ {
+ nCols = 0;
+ nRows = 0;
+ }
+}
+
+
+sal_uLong ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, sal_uLong nFormat ) const
+{
+ if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
+ return nFormatIndex;
+ //! not ScFormulaCell::IsValue(), that could reinterpret the formula again.
+ if ( aResult.IsValue() )
+ return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType );
+ else
+ return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType );
+}
+
+
+void ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
+{
+ if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
+ {
+ const ScHint* p = PTR_CAST( ScHint, &rHint );
+ sal_uLong nHint = (p ? p->GetId() : 0);
+ if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY))
+ {
+ bool bForceTrack = false;
+ if ( nHint & SC_HINT_TABLEOPDIRTY )
+ {
+ bForceTrack = !bTableOpDirty;
+ if ( !bTableOpDirty )
+ {
+ pDocument->AddTableOpFormulaCell( this );
+ bTableOpDirty = true;
+ }
+ }
+ else
+ {
+ bForceTrack = !bDirty;
+ SetDirtyVar();
+ }
+ // Don't remove from FormulaTree to put in FormulaTrack to
+ // put in FormulaTree again and again, only if necessary.
+ // Any other means except RECALCMODE_ALWAYS by which a cell could
+ // be in FormulaTree if it would notify other cells through
+ // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
+ // Yes. The new TableOpDirty made it necessary to have a
+ // forced mode where formulas may still be in FormulaTree from
+ // TableOpDirty but have to notify dependents for normal dirty.
+ if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
+ || pCode->IsRecalcModeAlways())
+ && !pDocument->IsInFormulaTrack( this ) )
+ pDocument->AppendToFormulaTrack( this );
+ }
+ }
+}
+
+void ScFormulaCell::SetDirty( bool bDirtyFlag )
+{
+ if ( !IsInChangeTrack() )
+ {
+ if ( pDocument->GetHardRecalcState() )
+ SetDirtyVar();
+ else
+ {
+ // Multiple Formulas avoid tracking in Load and Copy compileAll
+ // by Scenario and Copy Block From Clip.
+ // If unconditional required Formula tracking is set before SetDirty
+ // bDirty = false, eg in CompileTokenArray
+ if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
+ {
+ if( bDirtyFlag )
+ SetDirtyVar();
+ pDocument->AppendToFormulaTrack( this );
+ pDocument->TrackFormulas();
+ }
+ }
+
+ if (pDocument->IsStreamValid(aPos.Tab()))
+ pDocument->SetStreamValid(aPos.Tab(), false);
+ }
+}
+
+void ScFormulaCell::SetDirtyVar()
+{
+ bDirty = true;
+ // mark the sheet of this cell to be calculated
+ //#FIXME do we need to revert this remnant of old fake vba events? pDocument->AddCalculateTable( aPos.Tab() );
+}
+
+void ScFormulaCell::SetDirtyAfterLoad()
+{
+ bDirty = true;
+ if ( !pDocument->GetHardRecalcState() )
+ pDocument->PutInFormulaTree( this );
+}
+
+void ScFormulaCell::SetTableOpDirty()
+{
+ if ( !IsInChangeTrack() )
+ {
+ if ( pDocument->GetHardRecalcState() )
+ bTableOpDirty = true;
+ else
+ {
+ if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
+ {
+ if ( !bTableOpDirty )
+ {
+ pDocument->AddTableOpFormulaCell( this );
+ bTableOpDirty = true;
+ }
+ pDocument->AppendToFormulaTrack( this );
+ pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
+ }
+ }
+ }
+}
+
+
+bool ScFormulaCell::IsDirtyOrInTableOpDirty() const
+{
+ return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
+}
+
+
+void ScFormulaCell::SetErrCode( sal_uInt16 n )
+{
+ /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
+ * used whether it is solely for transport of a simple result error and get
+ * rid of that abuse. */
+ pCode->SetCodeError( n );
+ // Hard set errors are transported as result type value per convention,
+ // e.g. via clipboard. ScFormulaResult::IsValue() and
+ // ScFormulaResult::GetDouble() handle that.
+ aResult.SetResultError( n );
+}
+
+void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
+{
+ if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL )
+ bDirty = true;
+ if ( nBits & RECALCMODE_ONLOAD_ONCE )
+ { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
+ nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL;
+ }
+ pCode->AddRecalcMode( nBits );
+}
+
+// Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
+void ScFormulaCell::GetURLResult( rtl::OUString& rURL, rtl::OUString& rCellText )
+{
+ rtl::OUString aCellString;
+
+ Color* pColor;
+
+ // Cell Text uses the Cell format while the URL uses
+ // the default format for the type.
+ sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos );
+ SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
+
+ if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
+ nCellFormat = GetStandardFormat( *pFormatter,nCellFormat );
+
+ sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER);
+
+ if ( IsValue() )
+ {
+ double fValue = GetValue();
+ pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
+ }
+ else
+ {
+ aCellString = GetString();
+ pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
+ }
+ ScConstMatrixRef xMat( aResult.GetMatrix());
+ if (xMat)
+ {
+ // determine if the matrix result is a string or value.
+ if (!xMat->IsValue(0, 1))
+ rURL = xMat->GetString(0, 1);
+ else
+ pFormatter->GetOutputString(
+ xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
+ }
+
+ if(rURL.isEmpty())
+ {
+ if(IsValue())
+ pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
+ else
+ pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
+ }
+}
+
+bool ScFormulaCell::IsMultilineResult()
+{
+ if (!IsValue())
+ return aResult.IsMultiline();
+ return false;
+}
+
+void ScFormulaCell::MaybeInterpret()
+{
+ if (!IsDirtyOrInTableOpDirty())
+ return;
+
+ if (pDocument->GetAutoCalc() || (cMatrixFlag != MM_NONE))
+ Interpret();
+}
+
+EditTextObject* ScFormulaCell::CreateURLObject()
+{
+ rtl::OUString aCellText;
+ rtl::OUString aURL;
+ GetURLResult( aURL, aCellText );
+
+ return ScEditUtil::CreateURLObjectFromURL( *pDocument, aURL, aCellText );
+}
+
+bool ScFormulaCell::IsEmpty()
+{
+ MaybeInterpret();
+ return aResult.GetCellResultType() == formula::svEmptyCell;
+}
+
+bool ScFormulaCell::IsEmptyDisplayedAsString()
+{
+ MaybeInterpret();
+ return aResult.IsEmptyDisplayedAsString();
+}
+
+bool ScFormulaCell::IsValue()
+{
+ MaybeInterpret();
+ return aResult.IsValue();
+}
+
+bool ScFormulaCell::IsHybridValueCell()
+{
+ return aResult.GetType() == formula::svHybridValueCell;
+}
+
+double ScFormulaCell::GetValue()
+{
+ MaybeInterpret();
+ 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
+ MaybeInterpret();
+ return aResult.GetDouble();
+}
+
+rtl::OUString ScFormulaCell::GetString()
+{
+ MaybeInterpret();
+ if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
+ !aResult.GetResultError())
+ return aResult.GetString();
+ return rtl::OUString();
+}
+
+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()))
+ Interpret();
+ }
+ return aResult.GetMatrix().get();
+}
+
+bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
+{
+ switch ( cMatrixFlag )
+ {
+ case MM_FORMULA :
+ rPos = aPos;
+ return true;
+ case MM_REFERENCE :
+ {
+ pCode->Reset();
+ ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if( t )
+ {
+ ScSingleRefData& rRef = t->GetSingleRef();
+ rRef.CalcAbsIfRel( aPos );
+ if ( rRef.Valid() )
+ {
+ rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ return false;
+}
+
+
+/*
+ Edge-Values:
+
+ 8
+ 4 16
+ 2
+
+ inside: 1
+ outside: 0
+ (reserved: open: 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; // bad luck..
+ if ( aOrg != rOrgPos )
+ { // First time or a different matrix than last time.
+ rOrgPos = aOrg;
+ ScFormulaCell* pFCell;
+ if ( cMatrixFlag == MM_REFERENCE )
+ pFCell = pDocument->GetFormulaCell(aOrg);
+ else
+ pFCell = this; // this MM_FORMULA
+ // There's only one this, don't compare pFCell==this.
+ if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA
+ && pFCell->cMatrixFlag == MM_FORMULA )
+ {
+ pFCell->GetMatColsRows( nC, nR );
+ if ( nC == 0 || nR == 0 )
+ {
+ // No ScMatrixFormulaCellToken available yet, calculate new.
+ nC = 1;
+ nR = 1;
+ ScAddress aTmpOrg;
+ ScFormulaCell* pCell;
+ ScAddress aAdr( aOrg );
+ aAdr.IncCol();
+ bool bCont = true;
+ do
+ {
+ pCell = pDocument->GetFormulaCell(aAdr);
+ if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
+ pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
+ {
+ nC++;
+ aAdr.IncCol();
+ }
+ else
+ bCont = false;
+ } while ( bCont );
+ aAdr = aOrg;
+ aAdr.IncRow();
+ bCont = true;
+ do
+ {
+ pCell = pDocument->GetFormulaCell(aAdr);
+ if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
+ pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
+ {
+ nR++;
+ aAdr.IncRow();
+ }
+ else
+ bCont = false;
+ } while ( bCont );
+ pFCell->SetMatColsRows( nC, nR );
+ }
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 0
+ rtl::OUString aTmp;
+ rtl::OStringBuffer aMsg(RTL_CONSTASCII_STRINGPARAM(
+ "broken Matrix, no MatFormula at origin, Pos: "));
+ aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
+ aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
+ aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
+ OSL_FAIL(aMsg.getStr());
+#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; // left edge
+ if ( dC+1 == nC )
+ nEdges |= 16; // right edge
+ if ( dR == 0 )
+ nEdges |= 8; // top edge
+ if ( dR+1 == nR )
+ nEdges |= 2; // bottom edge
+ if ( !nEdges )
+ nEdges = 1; // inside
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ rtl::OUString aTmp;
+ rtl::OStringBuffer aMsg( "broken Matrix, Pos: " );
+ aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
+ aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
+ aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg.append(rtl::OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
+ aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatCols: "));
+ aMsg.append(static_cast<sal_Int32>( nC ));
+ aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatRows: "));
+ aMsg.append(static_cast<sal_Int32>( nR ));
+ aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffCols: "));
+ aMsg.append(static_cast<sal_Int32>( dC ));
+ aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffRows: "));
+ aMsg.append(static_cast<sal_Int32>( dR ));
+ OSL_FAIL( aMsg.makeStringAndClear().getStr());
+ }
+#endif
+ return nEdges;
+// break;
+ }
+ default:
+ return 0;
+ }
+}
+
+sal_uInt16 ScFormulaCell::GetErrCode()
+{
+ MaybeInterpret();
+
+ /* 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();
+}
+
+bool ScFormulaCell::HasOneReference( ScRange& r ) const
+{
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if( p && !pCode->GetNextReferenceRPN() ) // only one!
+ {
+ 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 true;
+ }
+ else
+ return 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<ScToken*>(pCode->GetNextReferenceRPN()));
+ if (pFirstReference)
+ {
+ // Collect all consecutive references, starting by the one
+ // already found
+ std::deque<ScToken*> aReferences;
+ aReferences.push_back(pFirstReference);
+ FormulaToken* pToken(pCode->NextRPN());
+ FormulaToken* pFunction(0);
+ while (pToken)
+ {
+ if (lcl_isReference(*pToken))
+ {
+ aReferences.push_back(dynamic_cast<ScToken*>(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;
+}
+
+bool ScFormulaCell::HasRelNameReference() const
+{
+ pCode->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
+ {
+ if ( t->GetSingleRef().IsRelName() ||
+ (t->GetType() == formula::svDoubleRef &&
+ t->GetDoubleRef().Ref2.IsRelName()) )
+ return true;
+ }
+ return false;
+}
+
+bool ScFormulaCell::HasColRowName() const
+{
+ pCode->Reset();
+ return (pCode->GetNextColRowName() != NULL);
+}
+
+bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode,
+ const ScRange& r,
+ SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
+ ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
+{
+ bool bCellStateChanged = false;
+
+ SCCOL nCol1 = r.aStart.Col();
+ SCROW nRow1 = r.aStart.Row();
+ 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 );
+// bool bPosChanged = false; // if this cell was moved
+ bool bIsInsert = (eUpdateRefMode == URM_INSDEL &&
+ nDx >= 0 && nDy >= 0 && nDz >= 0);
+ if (eUpdateRefMode == URM_INSDEL && r.In( aPos ))
+ {
+ aPos.Move(nDx, nDy, nDz);
+ bCellStateChanged = aPos != aOldPos;
+ }
+ else if ( r.In( aPos ) )
+ {
+ aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
+ }
+
+ bool bHasRefs = false;
+ bool bHasColRowNames = false;
+ bool bOnRefMove = 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;
+ ScRangeData* pRangeData;
+ bool bValChanged = false;
+ bool bRangeModified = false; // any range, not only shared formula
+ bool bRefSizeChanged = false;
+ 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 = false;
+ pRangeData = NULL;
+ bRangeModified = false;
+ bRefSizeChanged = false;
+ }
+
+ bCellStateChanged |= bValChanged;
+
+ if ( bOnRefMove )
+ bOnRefMove = (bValChanged || (aPos != aOldPos));
+ // Cell may reference itself, e.g. ocColumn, ocRow without parameter
+
+ 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 = false;
+ ScToken* t;
+ ScRangePairList* pColList = pDocument->GetColNameRanges();
+ ScRangePairList* pRowList = pDocument->GetRowNameRanges();
+ pCode->Reset();
+ while ( !bColRowNameCompile && (t = static_cast<ScToken*>(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 )
+ { // defined
+ if ( pR->GetRange(1).aStart.Row() == nRow1 )
+ bColRowNameCompile = true;
+ }
+ else
+ { // on the fly
+ if ( rRef.nRow + 1 == nRow1 )
+ bColRowNameCompile = 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 )
+ { // defined
+ if ( pR->GetRange(1).aStart.Col() == nCol1 )
+ bColRowNameCompile = true;
+ }
+ else
+ { // on the fly
+ if ( rRef.nCol + 1 == nCol1 )
+ bColRowNameCompile = true;
+ }
+ }
+ }
+ }
+ else if ( eUpdateRefMode == URM_MOVE )
+ { // Recomplie for Move/D&D when ColRowName was moved or this Cell
+ // points to one and was moved.
+ bColRowNameCompile = bCompile; // Possibly from Copy ctor
+ if ( !bColRowNameCompile )
+ {
+ bool bMoved = (aPos != aOldPos);
+ pCode->Reset();
+ ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
+ if ( t && bMoved )
+ bColRowNameCompile = 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 = true;
+ }
+ t = static_cast<ScToken*>(pCode->GetNextColRowName());
+ }
+ }
+ }
+ else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
+ {
+ bColRowNameCompile = true;
+ }
+ ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
+ if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
+ bInDeleteUndo = true;
+ else
+ bInDeleteUndo = 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 =
+ false;
+ }
+
+ bool bNeedDirty = false;
+ // NeedDirty for changes except for Copy and Move/Insert without RelNames
+ if ( bRangeModified || pRangeData || bColRowNameCompile ||
+ (bValChanged && eUpdateRefMode != URM_COPY &&
+ (eUpdateRefMode != URM_MOVE || bHasRelName) &&
+ (!bIsInsert || bHasRelName || bInDeleteUndo ||
+ bRefSizeChanged)) || bOnRefMove)
+ bNeedDirty = true;
+ else
+ bNeedDirty = 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 );
+ }
+ }
+ bValChanged = 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 = true;
+ bNeedDirty = true;
+ }
+ if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
+ {
+ CompileTokenArray( bNewListening ); // no Listening
+ bNeedDirty = 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( true);
+ }
+ else
+ StartListeningTo( pDocument );
+ }
+ }
+ if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) )
+ { // Cut off references, invalid or similar?
+ bool bOldAutoCalc = pDocument->GetAutoCalc();
+ // No Interpret in SubMinimalRecalc because of eventual wrong reference
+ pDocument->SetAutoCalc( false );
+ SetDirty();
+ pDocument->SetAutoCalc( bOldAutoCalc );
+ }
+
+ delete pOld;
+ }
+ return bCellStateChanged;
+}
+
+void ScFormulaCell::UpdateInsertTab(SCTAB nTable, SCTAB nNewSheets)
+{
+ bool bPosChanged = ( aPos.Tab() >= nTable ? true : false );
+ pCode->Reset();
+ if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
+ {
+ EndListeningTo( pDocument );
+ // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
+ if ( bPosChanged )
+ aPos.IncTab(nNewSheets);
+ ScRangeData* pRangeData;
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateInsertTab( nTable, false, nNewSheets );
+ if (pRangeData) // Exchange Shared Formula with real Formula
+ {
+ 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, false, nNewSheets );
+ // If the shared formula contained a named range/formula containing
+ // an absolute reference to a sheet, those have to be readjusted.
+ aComp2.UpdateDeleteTab( nTable, false, true, bRefChanged, nNewSheets );
+ bCompile = true;
+ }
+ // no StartListeningTo because pTab[nTab] does not exsist!
+ }
+ else if ( bPosChanged )
+ aPos.IncTab();
+}
+
+bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, bool bIsMove, SCTAB nSheets)
+{
+ bool bRefChanged = false;
+ bool bPosChanged = ( aPos.Tab() >= nTable + nSheets ? true : false );
+ pCode->Reset();
+ if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
+ {
+ EndListeningTo( pDocument );
+ // IncTab _after_ EndListeningTo und _before_ Compiler UpdateDeleteTab!
+ if ( bPosChanged )
+ aPos.IncTab(-1*nSheets);
+ ScRangeData* pRangeData;
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, false, bRefChanged, nSheets);
+ if (pRangeData) // Exchange Shared Formula with real Formula
+ {
+ 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, false, false, bRefChanged, nSheets );
+ // If the shared formula contained a named range/formula containing
+ // an absolute reference to a sheet, those have to be readjusted.
+ aComp2.UpdateInsertTab( nTable,true, nSheets );
+ // bRefChanged could have been reset at the last UpdateDeleteTab
+ bRefChanged = true;
+ bCompile = true;
+ }
+ // no StartListeningTo because pTab[nTab] not yet correct!
+ }
+ else if ( bPosChanged )
+ aPos.IncTab(-1*nSheets);
+
+ return bRefChanged;
+}
+
+void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
+{
+ pCode->Reset();
+ if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
+ {
+ EndListeningTo( pDocument );
+ // SetTab _after_ EndListeningTo und _before_ Compiler UpdateMoveTab !
+ aPos.SetTab( nTabNo );
+ ScRangeData* pRangeData;
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, false );
+ if (pRangeData) // Exchange Shared Formula with real Formula
+ {
+ 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, true );
+ bCompile = true;
+ }
+ // no StartListeningTo because pTab[nTab] not yet correct!
+ }
+ else
+ aPos.SetTab( nTabNo );
+}
+
+void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
+{
+ if( !pDocument->IsClipOrUndo() )
+ {
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(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<ScToken*>(pCode->GetNextReferenceRPN());
+ }
+ }
+}
+
+bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
+{
+ bool bRet = false;
+ if( !pDocument->IsClipOrUndo() )
+ {
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ while( p )
+ {
+ ScSingleRefData& rRef1 = p->GetSingleRef();
+ if( !rRef1.IsTabRel() )
+ {
+ if( (SCsTAB) nTable != rRef1.nTab )
+ bRet = 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 = true;
+ else if (nTable != aPos.Tab())
+ rRef2.nTab = aPos.Tab();
+ }
+ }
+ p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ }
+ }
+ return bRet;
+}
+
+void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
+{
+ if ( bForceIfNameInUse && !bCompile )
+ bCompile = pCode->HasNameOrColRowName();
+ if ( bCompile )
+ pCode->SetCodeError( 0 ); // make sure it will really be compiled
+ CompileTokenArray();
+}
+
+// Reference transposition is only called in Clipboard Document
+void ScFormulaCell::TransposeReference()
+{
+ bool bFound = false;
+ pCode->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
+ {
+ ScSingleRefData& rRef1 = t->GetSingleRef();
+ if ( rRef1.IsColRel() && rRef1.IsRowRel() )
+ {
+ 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<SCCOL>(rRef1.nRelRow);
+ rRef1.nRelRow = static_cast<SCROW>(nTemp);
+
+ if ( bDouble )
+ {
+ nTemp = rRef2.nRelCol;
+ rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
+ rRef2.nRelRow = static_cast<SCROW>(nTemp);
+ }
+
+ bFound = true;
+ }
+ }
+ }
+
+ if (bFound)
+ bCompile = true;
+}
+
+void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
+ ScDocument* pUndoDoc )
+{
+ EndListeningTo( pDocument );
+
+ ScAddress aOldPos = aPos;
+ bool bPosChanged = false; // Whether this cell has been moved
+
+ ScRange aDestRange( rDest, ScAddress(
+ static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
+ static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
+ rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
+ if ( aDestRange.In( aOldPos ) )
+ {
+ // Count back Positions
+ 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 = true;
+ }
+
+ ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
+ bool bRefChanged = false;
+ ScToken* t;
+
+ ScRangeData* pShared = NULL;
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
+ {
+ if( t->GetOpCode() == ocName )
+ {
+ ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
+ if (pName)
+ {
+ if (pName->IsModified())
+ bRefChanged = true;
+ if (pName->HasType(RT_SHAREDMOD))
+ pShared = pName;
+ }
+ }
+ else if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aOldPos );
+ 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 = true;
+ }
+ }
+ }
+
+ if (pShared) // Exchange Shared Formula with real Formula
+ {
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = new ScTokenArray( *pShared->GetCode() );
+ bRefChanged = true;
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
+ {
+ if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aOldPos );
+ 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->SetFormulaCell(aPos, pFCell);
+ }
+
+ bCompile = true;
+ CompileTokenArray(); // also call StartListeningTo
+ SetDirty();
+ }
+ else
+ StartListeningTo( pDocument ); // Listener as previous
+
+ delete pOld;
+}
+
+void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ EndListeningTo( pDocument );
+
+ bool bRefChanged = false;
+ ScToken* t;
+ ScRangeData* pShared = NULL;
+
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
+ {
+ if( t->GetOpCode() == ocName )
+ {
+ ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
+ if (pName)
+ {
+ if (pName->IsModified())
+ bRefChanged = true;
+ if (pName->HasType(RT_SHAREDMOD))
+ pShared = pName;
+ }
+ }
+ else if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aPos );
+ 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 = true;
+ }
+ }
+ }
+
+ if (pShared) // Exchange Shared Formula with real Formula
+ {
+ pDocument->RemoveFromFormulaTree( this ); // Update formula count
+ delete pCode;
+ pCode = new ScTokenArray( *pShared->GetCode() );
+ bRefChanged = true;
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
+ {
+ if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aPos );
+ 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 = true;
+ CompileTokenArray(); // Also call StartListeningTo
+ SetDirty();
+ }
+ else
+ StartListeningTo( pDocument ); // Listener as previous
+}
+
+static void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& 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->findByIndex(p->GetIndex());
+ if (pSubName)
+ lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
+ }
+ }
+}
+
+void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
+{
+ lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
+}
+
+bool ScFormulaCell::IsChanged() const
+{
+ return bChanged;
+}
+
+void ScFormulaCell::ResetChanged()
+{
+ bChanged = false;
+}
+
+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 = true;
+ CompileTokenArray();
+ SetDirty();
+ break;
+ }
+ }
+}
+
+void ScFormulaCell::CompileDBFormula( bool bCreateFormulaString )
+{
+ // Two phases must be called after each other
+ // 1. Formula String with old generated names
+ // 2. Formula String with new generated names
+ if ( bCreateFormulaString )
+ {
+ bool bRecompile = false;
+ pCode->Reset();
+ for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
+ {
+ switch ( p->GetOpCode() )
+ {
+ case ocBad: // DB Area eventually goes bad
+ case ocColRowName: // in case of the same names
+ case ocDBArea: // DB Area
+ bRecompile = true;
+ break;
+ case ocName:
+ if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
+ bRecompile = true; // DB Area
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ if ( bRecompile )
+ {
+ rtl::OUString aFormula;
+ GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
+ {
+ if ( aFormula[ aFormula.getLength()-1 ] == '}' )
+ aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
+ if ( aFormula[0] == '{' )
+ aFormula = aFormula.copy( 1 );
+ }
+ EndListeningTo( pDocument );
+ pDocument->RemoveFromFormulaTree( this );
+ pCode->Clear();
+ SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ }
+ }
+ else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ Compile( aResult.GetHybridFormula(), false, eTempGrammar );
+ aResult.SetToken( NULL);
+ SetDirty();
+ }
+}
+
+void ScFormulaCell::CompileNameFormula( bool bCreateFormulaString )
+{
+ // Two phases must be called after each other
+ // 1. Formula String with old generated names
+ // 2. Formula String with new generated names
+ if ( bCreateFormulaString )
+ {
+ bool bRecompile = false;
+ pCode->Reset();
+ for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
+ {
+ switch ( p->GetOpCode() )
+ {
+ case ocBad: // in case RangeName goes bad
+ case ocColRowName: // in case the names are the same
+ bRecompile = true;
+ break;
+ default:
+ if ( p->GetType() == svIndex )
+ bRecompile = true; // RangeName
+ }
+ }
+ if ( bRecompile )
+ {
+ rtl::OUString aFormula;
+ GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
+ {
+ if ( aFormula[ aFormula.getLength()-1 ] == '}' )
+ aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
+ if ( aFormula[0] == '{' )
+ aFormula = aFormula.copy( 1 );
+ }
+ EndListeningTo( pDocument );
+ pDocument->RemoveFromFormulaTree( this );
+ pCode->Clear();
+ SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ }
+ }
+ else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+ {
+ Compile( aResult.GetHybridFormula(), 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 = true;
+ CompileTokenArray();
+ SetDirty();
+ break;
+ }
+ }
+}
+
+// we really want to be a lot more descriptive than this
+struct ScSimilarFormulaDelta : std::vector< size_t >
+{
+ bool IsCompatible( ScSimilarFormulaDelta *pDelta )
+ {
+ if ( size() != pDelta->size() )
+ return false;
+ for ( size_t i = 0; i < size(); i++ )
+ {
+ if ( (*this)[ i ] != (*pDelta)[ i ] )
+ return false;
+ }
+ return true;
+ }
+
+ void push_delta( const ScSingleRefData& a, const ScSingleRefData& b )
+ {
+ push_back( b.nCol - a.nCol );
+ push_back( b.nRow - a.nRow );
+ push_back( b.nTab - a.nTab );
+ }
+
+ /// if the vector is zero then nothing changes down the column.
+ bool IsInvariant() const
+ {
+ for ( size_t i = 0; i < size(); i++ )
+ {
+ if ( (*this)[ i ] != 0 )
+ return false;
+ }
+ return true;
+ }
+};
+
+bool ScFormulaCellGroup::IsCompatible( ScSimilarFormulaDelta *pDelta )
+{
+ return pDelta && mpDelta && mpDelta->IsCompatible( pDelta );
+}
+
+/// compare formulae tokens and build a series of deltas describing
+/// the difference - ie. the result, when added to this
+/// formulae should produce pOther
+ScSimilarFormulaDelta *ScFormulaCell::BuildDeltaTo( ScFormulaCell *pOtherCell )
+{
+ // no Matrix formulae yet.
+ if ( GetMatrixFlag() != MM_NONE )
+ return NULL;
+
+ // are these formule at all similar ?
+ if ( GetHash() != pOtherCell->GetHash() )
+ return NULL;
+
+ FormulaToken **pThis = pCode->GetCode();
+ sal_uInt16 pThisLen = pCode->GetCodeLen();
+ FormulaToken **pOther = pOtherCell->pCode->GetCode();
+ sal_uInt16 pOtherLen = pOtherCell->pCode->GetCodeLen();
+
+ if ( !pThis || !pOther )
+ {
+// fprintf( stderr, "Error: no compiled code for cells !" );
+ return NULL;
+ }
+
+ if ( pThisLen != pOtherLen )
+ return NULL;
+
+ // check we are basically the same function
+ for ( sal_uInt16 i = 0; i < pThisLen; i++ )
+ {
+ if ( pThis[ i ]->GetType() != pOther[ i ]->GetType() ||
+ pThis[ i ]->GetOpCode() != pOther[ i ]->GetOpCode() ||
+ pThis[ i ]->GetParamCount() != pOther[ i ]->GetParamCount() )
+ {
+// fprintf( stderr, "Incompatible type, op-code or param counts\n" );
+ return NULL;
+ }
+ switch( pThis[ i ]->GetType() )
+ {
+ case formula::svMatrix:
+ case formula::svExternalSingleRef:
+ case formula::svExternalDoubleRef:
+// fprintf( stderr, "Ignoring matrix and external references for now\n" );
+ return NULL;
+ default:
+ break;
+ }
+ }
+
+ ScSimilarFormulaDelta *pDelta = new ScSimilarFormulaDelta();
+
+ for ( sal_uInt16 i = 0; i < pThisLen; i++ )
+ {
+ ScToken *pThisTok = static_cast< ScToken * >( pThis[ i ] );
+ ScToken *pOtherTok = static_cast< ScToken * >( pOther[ i ] );
+
+ if ( pThis[i]->GetType() == formula::svSingleRef ||
+ pThis[i]->GetType() == formula::svDoubleRef )
+ {
+ const ScSingleRefData& aThisRef = pThisTok->GetSingleRef();
+ const ScSingleRefData& aOtherRef = pOtherTok->GetSingleRef();
+ pDelta->push_delta( aThisRef, aOtherRef );
+ }
+ if ( pThis[i]->GetType() == formula::svDoubleRef )
+ {
+ const ScSingleRefData& aThisRef2 = pThisTok->GetSingleRef2();
+ const ScSingleRefData& aOtherRef2 = pOtherTok->GetSingleRef2();
+ pDelta->push_delta( aThisRef2, aOtherRef2 );
+ }
+ }
+
+ return pDelta;
+}
+
+/// To avoid exposing impl. details of ScSimilarFormulaDelta publicly
+void ScFormulaCell::ReleaseDelta( ScSimilarFormulaDelta *pDelta )
+{
+ delete pDelta;
+}
+
+bool ScFormulaCell::InterpretFormulaGroup()
+{
+ // Re-build formulae groups if necessary - ideally this is done at
+ // import / insert / delete etc. and is integral to the data structures
+ pDocument->RebuildFormulaGroups();
+
+ if( !xGroup.get() )
+ return false;
+
+// fprintf( stderr, "Interpret cell %d, %d\n", (int)aPos.Col(), (int)aPos.Row() );
+
+ if ( xGroup->mpDelta->IsInvariant() )
+ {
+// fprintf( stderr, "struck gold - completely invariant for %d items !\n",
+// (int)xGroup->mnLength );
+
+ // calculate ourselves:
+ InterpretTail( SCITP_NORMAL );
+ for ( sal_Int32 i = 0; i < xGroup->mnLength; i++ )
+ {
+ ScAddress aTmpPos = aPos;
+ aTmpPos.SetRow(xGroup->mnStart + i);
+ ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
+ assert( pCell != NULL );
+
+ // FIXME: this set of horrors is unclear to me ... certainly
+ // the above GetCell is profoundly nasty & slow ...
+
+ // Ensure the cell truly has a result:
+ pCell->aResult = aResult;
+ pCell->ResetDirty();
+
+ // FIXME: there is a view / refresh missing here it appears.
+ }
+ return true;
+ }
+ else
+ {
+ // scan the formula ...
+ // have a document method: "Get2DRangeAsDoublesArray" that does the
+ // column-based heavy lifting call it for each absolute range from the
+ // first cell pos in the formula group.
+ //
+ // Project single references to ranges by adding their vector * xGroup->mnLength
+ //
+ // TODO:
+ // elide multiple dimensional movement in vectors eg. =SUM(A1<1,1>)
+ // produces a diagonal 'column' that serves no useful purpose for us.
+ // these should be very rare. Should elide in GetDeltas anyway and
+ // assert here.
+ //
+ // Having built our input data ...
+ // Throw it, and the formula over to some 'OpenCLCalculage' hook
+ //
+ // on return - release references on these double buffers
+ //
+ // transfer the result to the formula cells (as above)
+ // store the doubles in the columns' maDoubles array for
+ // dependent formulae
+ //
+ // TODO:
+ // need to abort/fail when we get errors returned and fallback to
+ // stock interpreting [ I guess ], unless we can use NaN etc. to
+ // signal errors.
+
+ return false;
+ }
+}
+
+void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
+{
+ if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
+ return;
+
+ pDoc->SetDetectiveDirty(true); // It has changed something
+
+ ScTokenArray* pArr = GetCode();
+ if( pArr->IsRecalcModeAlways() )
+ {
+ pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, this);
+ SetNeedsListening( false);
+ return;
+ }
+
+ pArr->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
+ {
+ StackVar eType = t->GetType();
+ ScSingleRefData& rRef1 = t->GetSingleRef();
+ ScSingleRefData& rRef2 = (eType == svDoubleRef ?
+ t->GetDoubleRef().Ref2 : rRef1);
+ switch( eType )
+ {
+ case svSingleRef:
+ rRef1.CalcAbsIfRel(aPos);
+ if ( rRef1.Valid() )
+ {
+ pDoc->StartListeningCell(
+ ScAddress( rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab ), this );
+ }
+ break;
+ case svDoubleRef:
+ t->CalcAbsIfRel(aPos);
+ if ( rRef1.Valid() && rRef2.Valid() )
+ {
+ if ( t->GetOpCode() == ocColRowNameAuto )
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ pDoc->StartListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ rRef2.nCol,
+ MAXROW,
+ rRef2.nTab ), this );
+ }
+ else
+ { // RowName
+ pDoc->StartListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ MAXCOL,
+ rRef2.nRow,
+ rRef2.nTab ), this );
+ }
+ }
+ else
+ {
+ pDoc->StartListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ rRef2.nCol,
+ rRef2.nRow,
+ rRef2.nTab ), this );
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ SetNeedsListening( false);
+}
+
+// pArr gesetzt -> Referenzen von anderer Zelle nehmen
+// Then aPos must also be commited
+
+void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
+ ScAddress aCellPos )
+{
+ if (pDoc->IsClipOrUndo() || IsInChangeTrack())
+ return;
+
+ pDoc->SetDetectiveDirty(true); // It has changed something
+
+ if ( GetCode()->IsRecalcModeAlways() )
+ {
+ pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, this );
+ return;
+ }
+
+ if (!pArr)
+ {
+ pArr = GetCode();
+ aCellPos = aPos;
+ }
+ pArr->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
+ {
+ StackVar eType = t->GetType();
+ ScSingleRefData& rRef1 = t->GetSingleRef();
+ ScSingleRefData& rRef2 = (eType == svDoubleRef ?
+ t->GetDoubleRef().Ref2 : rRef1);
+ switch( eType )
+ {
+ case svSingleRef:
+ rRef1.CalcAbsIfRel( aCellPos );
+ if ( rRef1.Valid() )
+ {
+ pDoc->EndListeningCell(
+ ScAddress( rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab ), this );
+ }
+ break;
+ case svDoubleRef:
+ t->CalcAbsIfRel( aCellPos );
+ if ( rRef1.Valid() && rRef2.Valid() )
+ {
+ if ( t->GetOpCode() == ocColRowNameAuto )
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ pDoc->EndListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ rRef2.nCol,
+ MAXROW,
+ rRef2.nTab ), this );
+ }
+ else
+ { // RowName
+ pDoc->EndListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ MAXCOL,
+ rRef2.nRow,
+ rRef2.nTab ), this );
+ }
+ }
+ else
+ {
+ pDoc->EndListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ rRef2.nCol,
+ rRef2.nRow,
+ rRef2.nTab ), this );
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/formulaiter.cxx b/sc/source/core/data/formulaiter.cxx
index 4a28acf2156d..29c2bb83f5b5 100644
--- a/sc/source/core/data/formulaiter.cxx
+++ b/sc/source/core/data/formulaiter.cxx
@@ -19,7 +19,7 @@
#include "formulaiter.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "formula/token.hxx"
using namespace formula;
diff --git a/sc/source/core/data/postit.cxx b/sc/source/core/data/postit.cxx
index 00ec727240c8..ea885b31dd26 100644
--- a/sc/source/core/data/postit.cxx
+++ b/sc/source/core/data/postit.cxx
@@ -41,7 +41,7 @@
#include "document.hxx"
#include "docpool.hxx"
#include "patattr.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "drwlayer.hxx"
#include "userdat.hxx"
#include "detfunc.hxx"
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index 12ab3d383f62..2044014f838b 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -25,7 +25,7 @@
#include "attrib.hxx"
#include "patattr.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "table.hxx"
#include "document.hxx"
#include "drwlayer.hxx"
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 9c95be5efcf2..85b494629a76 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -29,7 +29,7 @@
#include "table.hxx"
#include "patattr.hxx"
#include "docpool.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "drwlayer.hxx"
#include "olinetab.hxx"
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index a16d945d0d0f..5468ecb89394 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -31,7 +31,7 @@
#include "table.hxx"
#include "scitems.hxx"
#include "attrib.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "globstr.hrc"
#include "global.hxx"
diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx
index b467745b8a24..ea82e75f9bd3 100644
--- a/sc/source/core/data/table4.cxx
+++ b/sc/source/core/data/table4.cxx
@@ -44,7 +44,7 @@
#include "attrib.hxx"
#include "patattr.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "table.hxx"
#include "globstr.hrc"
#include "global.hxx"
diff --git a/sc/source/core/data/table5.cxx b/sc/source/core/data/table5.cxx
index 86e6b4fed71a..7e3fd7f57214 100644
--- a/sc/source/core/data/table5.cxx
+++ b/sc/source/core/data/table5.cxx
@@ -21,7 +21,7 @@
#include "attrib.hxx"
#include "patattr.hxx"
#include "docpool.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "table.hxx"
#include "column.hxx"
#include "document.hxx"
diff --git a/sc/source/core/data/table6.cxx b/sc/source/core/data/table6.cxx
index 6df2249d89a4..0354e09d8264 100644
--- a/sc/source/core/data/table6.cxx
+++ b/sc/source/core/data/table6.cxx
@@ -24,7 +24,7 @@
#include <editeng/editobj.hxx>
#include "table.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "stlpool.hxx"
#include "markdata.hxx"
diff --git a/sc/source/core/data/validat.cxx b/sc/source/core/data/validat.cxx
index cff7da794831..16267bfd7a4f 100644
--- a/sc/source/core/data/validat.cxx
+++ b/sc/source/core/data/validat.cxx
@@ -33,7 +33,7 @@
#include "validat.hxx"
#include "document.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "patattr.hxx"
#include "rechead.hxx"
#include "globstr.hrc"
diff --git a/sc/source/core/tool/cellform.cxx b/sc/source/core/tool/cellform.cxx
index 1ddfaaa4645c..cb7ce81996e6 100644
--- a/sc/source/core/tool/cellform.cxx
+++ b/sc/source/core/tool/cellform.cxx
@@ -22,7 +22,7 @@
#include <svl/zforlist.hxx>
#include "cellform.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "cellvalue.hxx"
#include "formula/errorcodes.hxx"
diff --git a/sc/source/core/tool/chartarr.cxx b/sc/source/core/tool/chartarr.cxx
index 34f4605810a6..26716c1a8e65 100644
--- a/sc/source/core/tool/chartarr.cxx
+++ b/sc/source/core/tool/chartarr.cxx
@@ -26,7 +26,7 @@
#include "document.hxx"
#include "rechead.hxx"
#include "globstr.hrc"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "docoptio.hxx"
#include <vector>
diff --git a/sc/source/core/tool/chgtrack.cxx b/sc/source/core/tool/chgtrack.cxx
index b5ce9c917cdb..3c41eb65bb99 100644
--- a/sc/source/core/tool/chgtrack.cxx
+++ b/sc/source/core/tool/chgtrack.cxx
@@ -18,7 +18,7 @@
*/
#include "chgtrack.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "dociter.hxx"
#include "global.hxx"
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index b8bf5dde27c4..7cee7f6d5449 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -51,7 +51,7 @@
#include "scresid.hxx"
#include "sc.hrc"
#include "globstr.hrc"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "dociter.hxx"
#include "docoptio.hxx"
#include <formula/errorcodes.hxx>
diff --git a/sc/source/core/tool/consoli.cxx b/sc/source/core/tool/consoli.cxx
index 2d0e129f50aa..a27be8529abc 100644
--- a/sc/source/core/tool/consoli.cxx
+++ b/sc/source/core/tool/consoli.cxx
@@ -23,7 +23,7 @@
#include "globstr.hrc"
#include "subtotal.hxx"
#include "formula/errorcodes.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include <math.h>
#include <string.h>
diff --git a/sc/source/core/tool/detfunc.cxx b/sc/source/core/tool/detfunc.cxx
index cd75cddcc3a5..517a892363b0 100644
--- a/sc/source/core/tool/detfunc.cxx
+++ b/sc/source/core/tool/detfunc.cxx
@@ -58,7 +58,7 @@
#include "drwlayer.hxx"
#include "userdat.hxx"
#include "validat.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "docpool.hxx"
#include "patattr.hxx"
#include "attrib.hxx"
diff --git a/sc/source/core/tool/doubleref.cxx b/sc/source/core/tool/doubleref.cxx
index 522853d74320..aa07053f52b9 100644
--- a/sc/source/core/tool/doubleref.cxx
+++ b/sc/source/core/tool/doubleref.cxx
@@ -18,7 +18,7 @@
*/
#include "doubleref.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "global.hxx"
#include "document.hxx"
#include "queryparam.hxx"
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index f0bfbe59c73d..1d9e857fcc88 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -38,7 +38,7 @@
#include "global.hxx"
#include "document.hxx"
#include "dociter.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "scmatrix.hxx"
#include "docoptio.hxx"
#include "globstr.hrc"
diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx
index c7496b503027..7768a08cce91 100644
--- a/sc/source/core/tool/interpr2.cxx
+++ b/sc/source/core/tool/interpr2.cxx
@@ -32,7 +32,7 @@
#include "ddelink.hxx"
#include "scmatrix.hxx"
#include "compiler.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "dociter.hxx"
#include "docoptio.hxx"
diff --git a/sc/source/core/tool/interpr3.cxx b/sc/source/core/tool/interpr3.cxx
index 3fadc45b22a4..9fa7763ea00d 100644
--- a/sc/source/core/tool/interpr3.cxx
+++ b/sc/source/core/tool/interpr3.cxx
@@ -25,7 +25,7 @@
#include "interpre.hxx"
#include "global.hxx"
#include "compiler.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "dociter.hxx"
#include "scmatrix.hxx"
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index fe05654bf2fe..83d02f16060b 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -41,7 +41,7 @@
#include "interpre.hxx"
#include "global.hxx"
#include "dbdata.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "callform.hxx"
#include "addincol.hxx"
#include "document.hxx"
diff --git a/sc/source/core/tool/interpr5.cxx b/sc/source/core/tool/interpr5.cxx
index 7dc9b57a5305..fc904a4d5648 100644
--- a/sc/source/core/tool/interpr5.cxx
+++ b/sc/source/core/tool/interpr5.cxx
@@ -29,7 +29,7 @@
#include "interpre.hxx"
#include "global.hxx"
#include "compiler.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
#include "document.hxx"
#include "dociter.hxx"
#include "scmatrix.hxx"
diff --git a/sc/source/core/tool/rangeseq.cxx b/sc/source/core/tool/rangeseq.cxx
index 9e08c896749e..957548121f43 100644
--- a/sc/source/core/tool/rangeseq.cxx
+++ b/sc/source/core/tool/rangeseq.cxx
@@ -27,7 +27,7 @@
#include "document.hxx"
#include "dociter.hxx"
#include "scmatrix.hxx"
-#include "cell.hxx"
+#include "formulacell.hxx"
using namespace com::sun::star;