summaryrefslogtreecommitdiff
path: root/sc/source/core/data/cell.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/cell.cxx')
-rw-r--r--sc/source/core/data/cell.cxx2036
1 files changed, 2036 insertions, 0 deletions
diff --git a/sc/source/core/data/cell.cxx b/sc/source/core/data/cell.cxx
new file mode 100644
index 000000000000..cfd7caafa3f3
--- /dev/null
+++ b/sc/source/core/data/cell.cxx
@@ -0,0 +1,2036 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: cell.cxx,v $
+ * $Revision: 1.44.38.6 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sc.hxx"
+
+// INCLUDE ---------------------------------------------------------------
+
+#include <svtools/zforlist.hxx>
+
+#include "scitems.hxx"
+#include "attrib.hxx"
+#include "cell.hxx"
+#include "compiler.hxx"
+#include "interpre.hxx"
+#include "document.hxx"
+#include "scmatrix.hxx"
+#include "dociter.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 "postit.hxx"
+#include "externalrefmgr.hxx"
+#include <svx/editobj.hxx>
+#include <svtools/intitem.hxx>
+#include <svx/flditem.hxx>
+#include <svtools/broadcast.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 USHORT MAXRECURSION = 400;
+
+// STATIC DATA -----------------------------------------------------------
+
+#ifdef USE_MEMPOOL
+// MemPools auf 4k Boundaries - 64 Bytes ausrichten
+const USHORT nMemPoolValueCell = (0x8000 - 64) / sizeof(ScValueCell);
+const USHORT nMemPoolFormulaCell = (0x8000 - 64) / sizeof(ScFormulaCell);
+const USHORT nMemPoolStringCell = (0x4000 - 64) / sizeof(ScStringCell);
+const USHORT nMemPoolNoteCell = (0x1000 - 64) / sizeof(ScNoteCell);
+IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell, nMemPoolValueCell, nMemPoolValueCell )
+IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell, nMemPoolFormulaCell, nMemPoolFormulaCell )
+IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell, nMemPoolStringCell, nMemPoolStringCell )
+IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell, nMemPoolNoteCell, nMemPoolNoteCell )
+#endif
+
+// ============================================================================
+
+ScBaseCell::ScBaseCell( CellType eNewType ) :
+ mpNote( 0 ),
+ mpBroadcaster( 0 ),
+ nTextWidth( TEXTWIDTH_DIRTY ),
+ eCellType( sal::static_int_cast<BYTE>(eNewType) ),
+ nScriptType( SC_SCRIPTTYPE_UNKNOWN )
+{
+}
+
+ScBaseCell::ScBaseCell( const ScBaseCell& rCell ) :
+ mpNote( 0 ),
+ mpBroadcaster( 0 ),
+ nTextWidth( rCell.nTextWidth ),
+ eCellType( rCell.eCellType ),
+ nScriptType( SC_SCRIPTTYPE_UNKNOWN )
+{
+}
+
+ScBaseCell::~ScBaseCell()
+{
+ delete mpNote;
+ delete mpBroadcaster;
+ DBG_ASSERT( eCellType == CELLTYPE_DESTROYED, "BaseCell Destructor" );
+}
+
+namespace {
+
+ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags )
+{
+ switch( rSrcCell.GetCellType() )
+ {
+ case CELLTYPE_VALUE:
+ return new ScValueCell( static_cast< const ScValueCell& >( rSrcCell ) );
+ case CELLTYPE_STRING:
+ return new ScStringCell( static_cast< const ScStringCell& >( rSrcCell ) );
+ case CELLTYPE_EDIT:
+ return new ScEditCell( static_cast< const ScEditCell& >( rSrcCell ), rDestDoc );
+ case CELLTYPE_FORMULA:
+ return new ScFormulaCell( static_cast< const ScFormulaCell& >( rSrcCell ), rDestDoc, rDestPos, nCloneFlags );
+ case CELLTYPE_NOTE:
+ return new ScNoteCell;
+ default:;
+ }
+ DBG_ERROR( "lclCloneCell - unknown cell type" );
+ return 0;
+}
+
+} // namespace
+
+ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, int nCloneFlags ) const
+{
+ // notes will not be cloned -> cell address only needed for formula cells
+ ScAddress aDestPos;
+ if( eCellType == CELLTYPE_FORMULA )
+ aDestPos = static_cast< const ScFormulaCell* >( this )->aPos;
+ return lclCloneCell( *this, rDestDoc, aDestPos, nCloneFlags );
+}
+
+ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const
+{
+ return lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags );
+}
+
+ScBaseCell* ScBaseCell::CloneWithNote( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const
+{
+ ScBaseCell* pNewCell = lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags );
+ if( mpNote )
+ {
+ if( !pNewCell )
+ pNewCell = new ScNoteCell;
+ bool bCloneCaption = (nCloneFlags & SC_CLONECELL_NOCAPTION) == 0;
+ pNewCell->TakeNote( mpNote->Clone( rOwnPos, rDestDoc, rDestPos, bCloneCaption ) );
+ }
+ return pNewCell;
+}
+
+void ScBaseCell::Delete()
+{
+ DeleteNote();
+ switch (eCellType)
+ {
+ case CELLTYPE_VALUE:
+ delete (ScValueCell*) this;
+ break;
+ case CELLTYPE_STRING:
+ delete (ScStringCell*) this;
+ break;
+ case CELLTYPE_EDIT:
+ delete (ScEditCell*) this;
+ break;
+ case CELLTYPE_FORMULA:
+ delete (ScFormulaCell*) this;
+ break;
+ case CELLTYPE_NOTE:
+ delete (ScNoteCell*) this;
+ break;
+ default:
+ DBG_ERROR("Unbekannter Zellentyp");
+ break;
+ }
+}
+
+bool ScBaseCell::IsBlank( bool bIgnoreNotes ) const
+{
+ return (eCellType == CELLTYPE_NOTE) && (bIgnoreNotes || !mpNote);
+}
+
+void ScBaseCell::TakeNote( ScPostIt* pNote )
+{
+ delete mpNote;
+ mpNote = pNote;
+}
+
+ScPostIt* ScBaseCell::ReleaseNote()
+{
+ ScPostIt* pNote = mpNote;
+ mpNote = 0;
+ return pNote;
+}
+
+void ScBaseCell::DeleteNote()
+{
+ DELETEZ( mpNote );
+}
+
+void ScBaseCell::TakeBroadcaster( SvtBroadcaster* pBroadcaster )
+{
+ delete mpBroadcaster;
+ mpBroadcaster = pBroadcaster;
+}
+
+SvtBroadcaster* ScBaseCell::ReleaseBroadcaster()
+{
+ SvtBroadcaster* pBroadcaster = mpBroadcaster;
+ mpBroadcaster = 0;
+ return pBroadcaster;
+}
+
+void ScBaseCell::DeleteBroadcaster()
+{
+ DELETEZ( mpBroadcaster );
+}
+
+ScBaseCell* ScBaseCell::CreateTextCell( const String& rString, ScDocument* pDoc )
+{
+ if ( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND )
+ return new ScEditCell( rString, pDoc );
+ else
+ return new ScStringCell( rString );
+}
+
+void ScBaseCell::StartListeningTo( ScDocument* pDoc )
+{
+ if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo()
+ && !pDoc->GetNoListening()
+ && !((ScFormulaCell*)this)->IsInChangeTrack()
+ )
+ {
+ pDoc->SetDetectiveDirty(TRUE); // es hat sich was geaendert...
+
+ ScFormulaCell* pFormCell = (ScFormulaCell*)this;
+ ScTokenArray* pArr = pFormCell->GetCode();
+ if( pArr->IsRecalcModeAlways() )
+ pDoc->StartListeningArea( BCA_LISTEN_ALWAYS, pFormCell );
+ else
+ {
+ 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( pFormCell->aPos );
+ if ( rRef1.Valid() )
+ {
+ pDoc->StartListeningCell(
+ ScAddress( rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab ), pFormCell );
+ }
+ break;
+ case svDoubleRef:
+ t->CalcAbsIfRel( pFormCell->aPos );
+ if ( rRef1.Valid() && rRef2.Valid() )
+ {
+ if ( t->GetOpCode() == ocColRowNameAuto )
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ pDoc->StartListeningArea( ScRange (
+ 0,
+ rRef1.nRow,
+ rRef1.nTab,
+ MAXCOL,
+ rRef2.nRow,
+ rRef2.nTab ), pFormCell );
+ }
+ else
+ { // RowName
+ pDoc->StartListeningArea( ScRange (
+ rRef1.nCol,
+ 0,
+ rRef1.nTab,
+ rRef2.nCol,
+ MAXROW,
+ rRef2.nTab ), pFormCell );
+ }
+ }
+ else
+ {
+ pDoc->StartListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ rRef2.nCol,
+ rRef2.nRow,
+ rRef2.nTab ), pFormCell );
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ pFormCell->SetNeedsListening( FALSE);
+ }
+}
+
+// pArr gesetzt -> Referenzen von anderer Zelle nehmen
+// dann muss auch aPos uebergeben werden!
+
+void ScBaseCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
+ ScAddress aPos )
+{
+ if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo()
+ && !((ScFormulaCell*)this)->IsInChangeTrack()
+ )
+ {
+ pDoc->SetDetectiveDirty(TRUE); // es hat sich was geaendert...
+
+ ScFormulaCell* pFormCell = (ScFormulaCell*)this;
+ if( pFormCell->GetCode()->IsRecalcModeAlways() )
+ pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, pFormCell );
+ else
+ {
+ if (!pArr)
+ {
+ pArr = pFormCell->GetCode();
+ aPos = pFormCell->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( aPos );
+ if ( rRef1.Valid() )
+ {
+ pDoc->EndListeningCell(
+ ScAddress( rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab ), pFormCell );
+ }
+ break;
+ case svDoubleRef:
+ t->CalcAbsIfRel( aPos );
+ if ( rRef1.Valid() && rRef2.Valid() )
+ {
+ if ( t->GetOpCode() == ocColRowNameAuto )
+ { // automagically
+ if ( rRef1.IsColRel() )
+ { // ColName
+ pDoc->EndListeningArea( ScRange (
+ 0,
+ rRef1.nRow,
+ rRef1.nTab,
+ MAXCOL,
+ rRef2.nRow,
+ rRef2.nTab ), pFormCell );
+ }
+ else
+ { // RowName
+ pDoc->EndListeningArea( ScRange (
+ rRef1.nCol,
+ 0,
+ rRef1.nTab,
+ rRef2.nCol,
+ MAXROW,
+ rRef2.nTab ), pFormCell );
+ }
+ }
+ else
+ {
+ pDoc->EndListeningArea( ScRange (
+ rRef1.nCol,
+ rRef1.nRow,
+ rRef1.nTab,
+ rRef2.nCol,
+ rRef2.nRow,
+ rRef2.nTab ), pFormCell );
+ }
+ }
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ }
+ }
+}
+
+
+USHORT ScBaseCell::GetErrorCode() const
+{
+ switch ( eCellType )
+ {
+ case CELLTYPE_FORMULA :
+ return ((ScFormulaCell*)this)->GetErrCode();
+ default:
+ return 0;
+ }
+}
+
+
+BOOL ScBaseCell::HasEmptyData() const
+{
+ switch ( eCellType )
+ {
+ case CELLTYPE_NOTE :
+ return TRUE;
+ case CELLTYPE_FORMULA :
+ return ((ScFormulaCell*)this)->IsEmpty();
+ default:
+ return FALSE;
+ }
+}
+
+
+BOOL ScBaseCell::HasValueData() const
+{
+ switch ( eCellType )
+ {
+ case CELLTYPE_VALUE :
+ return TRUE;
+ case CELLTYPE_FORMULA :
+ return ((ScFormulaCell*)this)->IsValue();
+ default:
+ return FALSE;
+ }
+}
+
+
+BOOL ScBaseCell::HasStringData() const
+{
+ switch ( eCellType )
+ {
+ case CELLTYPE_STRING :
+ case CELLTYPE_EDIT :
+ return TRUE;
+ case CELLTYPE_FORMULA :
+ return !((ScFormulaCell*)this)->IsValue();
+ default:
+ return FALSE;
+ }
+}
+
+String ScBaseCell::GetStringData() const
+{
+ String aStr;
+ switch ( eCellType )
+ {
+ case CELLTYPE_STRING:
+ ((const ScStringCell*)this)->GetString( aStr );
+ break;
+ case CELLTYPE_EDIT:
+ ((const ScEditCell*)this)->GetString( aStr );
+ break;
+ case CELLTYPE_FORMULA:
+ ((ScFormulaCell*)this)->GetString( aStr ); // an der Formelzelle nicht-const
+ break;
+ }
+ return aStr;
+}
+
+// static
+BOOL ScBaseCell::CellEqual( const ScBaseCell* pCell1, const ScBaseCell* pCell2 )
+{
+ CellType eType1 = CELLTYPE_NONE;
+ CellType eType2 = CELLTYPE_NONE;
+ if ( pCell1 )
+ {
+ eType1 = pCell1->GetCellType();
+ if (eType1 == CELLTYPE_EDIT)
+ eType1 = CELLTYPE_STRING;
+ else if (eType1 == CELLTYPE_NOTE)
+ eType1 = CELLTYPE_NONE;
+ }
+ if ( pCell2 )
+ {
+ eType2 = pCell2->GetCellType();
+ if (eType2 == CELLTYPE_EDIT)
+ eType2 = CELLTYPE_STRING;
+ else if (eType2 == CELLTYPE_NOTE)
+ eType2 = CELLTYPE_NONE;
+ }
+ if ( eType1 != eType2 )
+ return FALSE;
+
+ switch ( eType1 ) // beide Typen gleich
+ {
+ case CELLTYPE_NONE: // beide leer
+ return TRUE;
+ case CELLTYPE_VALUE: // wirklich Value-Zellen
+ return ( ((const ScValueCell*)pCell1)->GetValue() ==
+ ((const ScValueCell*)pCell2)->GetValue() );
+ case CELLTYPE_STRING: // String oder Edit
+ {
+ String aText1;
+ if ( pCell1->GetCellType() == CELLTYPE_STRING )
+ ((const ScStringCell*)pCell1)->GetString(aText1);
+ else
+ ((const ScEditCell*)pCell1)->GetString(aText1);
+ String aText2;
+ if ( pCell2->GetCellType() == CELLTYPE_STRING )
+ ((const ScStringCell*)pCell2)->GetString(aText2);
+ else
+ ((const ScEditCell*)pCell2)->GetString(aText2);
+ return ( aText1 == aText2 );
+ }
+ case CELLTYPE_FORMULA:
+ {
+ //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!!
+ //! Vergleichsfunktion an der Formelzelle ???
+ //! Abfrage mit ScColumn::SwapRow zusammenfassen!
+
+ ScTokenArray* pCode1 = ((ScFormulaCell*)pCell1)->GetCode();
+ ScTokenArray* pCode2 = ((ScFormulaCell*)pCell2)->GetCode();
+
+ if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN
+ {
+ BOOL bEqual = TRUE;
+ USHORT nLen = pCode1->GetLen();
+ FormulaToken** ppToken1 = pCode1->GetArray();
+ FormulaToken** ppToken2 = pCode2->GetArray();
+ for (USHORT i=0; i<nLen; i++)
+ if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) )
+ {
+ bEqual = FALSE;
+ break;
+ }
+
+ if (bEqual)
+ return TRUE;
+ }
+
+ return FALSE; // unterschiedlich lang oder unterschiedliche Tokens
+ }
+ default:
+ DBG_ERROR("huch, was fuer Zellen???");
+ }
+ return FALSE;
+}
+
+// ============================================================================
+
+ScNoteCell::ScNoteCell( SvtBroadcaster* pBC ) :
+ ScBaseCell( CELLTYPE_NOTE )
+{
+ TakeBroadcaster( pBC );
+}
+
+ScNoteCell::ScNoteCell( ScPostIt* pNote, SvtBroadcaster* pBC ) :
+ ScBaseCell( CELLTYPE_NOTE )
+{
+ TakeNote( pNote );
+ TakeBroadcaster( pBC );
+}
+
+#ifdef DBG_UTIL
+ScNoteCell::~ScNoteCell()
+{
+ eCellType = CELLTYPE_DESTROYED;
+}
+#endif
+
+// ============================================================================
+
+ScValueCell::ScValueCell() :
+ ScBaseCell( CELLTYPE_VALUE ),
+ mfValue( 0.0 )
+{
+}
+
+ScValueCell::ScValueCell( double fValue ) :
+ ScBaseCell( CELLTYPE_VALUE ),
+ mfValue( fValue )
+{
+}
+
+#ifdef DBG_UTIL
+ScValueCell::~ScValueCell()
+{
+ eCellType = CELLTYPE_DESTROYED;
+}
+#endif
+
+// ============================================================================
+
+ScStringCell::ScStringCell() :
+ ScBaseCell( CELLTYPE_STRING )
+{
+}
+
+ScStringCell::ScStringCell( const String& rString ) :
+ ScBaseCell( CELLTYPE_STRING ),
+ maString( rString.intern() )
+{
+}
+
+#ifdef DBG_UTIL
+ScStringCell::~ScStringCell()
+{
+ eCellType = CELLTYPE_DESTROYED;
+}
+#endif
+
+// ============================================================================
+
+//
+// ScFormulaCell
+//
+
+ScFormulaCell::ScFormulaCell() :
+ ScBaseCell( CELLTYPE_FORMULA ),
+ eTempGrammar( FormulaGrammar::GRAM_DEFAULT),
+ pCode( NULL ),
+ pDocument( NULL ),
+ pPrevious(0),
+ pNext(0),
+ pPreviousTrack(0),
+ pNextTrack(0),
+ nFormatIndex(0),
+ nFormatType( NUMBERFORMAT_NUMBER ),
+ nSeenInIteration(0),
+ cMatrixFlag ( MM_NONE ),
+ bDirty( FALSE ),
+ bChanged( FALSE ),
+ bRunning( FALSE ),
+ bCompile( FALSE ),
+ bSubTotal( FALSE ),
+ bIsIterCell( FALSE ),
+ bInChangeTrack( FALSE ),
+ bTableOpDirty( FALSE ),
+ bNeedListening( FALSE ),
+ aPos(0,0,0)
+{
+}
+
+ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
+ const String& rFormula,
+ const FormulaGrammar::Grammar eGrammar,
+ BYTE 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 ), // -> wg. Benutzung im Fkt.AutoPiloten, war: 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
+}
+
+// Wird von den Importfiltern verwendet
+
+ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
+ const ScTokenArray* pArr,
+ const FormulaGrammar::Grammar eGrammar, BYTE 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 ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0
+ bChanged( FALSE ),
+ bRunning( FALSE ),
+ bCompile( FALSE ),
+ bSubTotal( FALSE ),
+ bIsIterCell( FALSE ),
+ bInChangeTrack( FALSE ),
+ bTableOpDirty( FALSE ),
+ bNeedListening( FALSE ),
+ aPos( rPos )
+{
+ // UPN-Array erzeugen
+ 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;
+ }
+}
+
+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();
+
+ if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
+ pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
+
+ // evtl. Fehler zuruecksetzen und neu kompilieren
+ // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben
+ // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten
+ 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();
+ if( !bCompile )
+ { // Name references with references and ColRowNames
+ pCode->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
+ {
+ if ( t->GetOpCode() == ocExternalRef )
+ {
+ // External name, cell, and area references.
+ bCompile = true;
+ }
+ else if ( t->GetType() == svIndex )
+ {
+ ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( 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 );
+}
+
+ScFormulaCell::~ScFormulaCell()
+{
+ pDocument->RemoveFromFormulaTree( this );
+ delete pCode;
+#ifdef DBG_UTIL
+ eCellType = CELLTYPE_DESTROYED;
+#endif
+}
+
+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? */
+ ScBaseCell* pCell;
+ ScSingleRefData& rRef = p->GetSingleRef();
+ rRef.CalcAbsIfRel( aPos );
+ if ( rRef.Valid() )
+ pCell = pDocument->GetCell( ScAddress( rRef.nCol,
+ rRef.nRow, rRef.nTab ) );
+ else
+ pCell = NULL;
+ if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA)
+ {
+ ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar);
+ return;
+ }
+ else
+ {
+ ScCompiler aComp( pDocument, aPos, *pCode);
+ aComp.SetGrammar(eGrammar);
+ aComp.CreateStringFromTokenArray( rBuffer );
+ }
+ }
+ else
+ {
+ DBG_ERROR("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( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const
+{
+ rtl::OUStringBuffer rBuffer( rFormula );
+ GetFormula( rBuffer, eGrammar );
+ rFormula = rBuffer;
+}
+
+void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+
+ 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 String& rFormula, BOOL bNoListening,
+ const FormulaGrammar::Grammar eGrammar )
+{
+ if ( pDocument->IsClipOrUndo() ) return;
+ BOOL bWasInFormulaTree = pDocument->IsInFormulaTree( this );
+ if ( bWasInFormulaTree )
+ pDocument->RemoveFromFormulaTree( this );
+ // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
+ 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().Len() && rFormula == aResult.GetHybridFormula() )
+ { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray
+ if ( rFormula.GetChar(0) == '=' )
+ pCode->AddBad( rFormula.GetBuffer() + 1 );
+ else
+ pCode->AddBad( rFormula.GetBuffer() );
+ }
+ bCompile = TRUE;
+ CompileTokenArray( bNoListening );
+ }
+ else
+ {
+ bChanged = TRUE;
+ SetTextWidth( TEXTWIDTH_DIRTY );
+ SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
+ }
+ if ( bWasInFormulaTree )
+ pDocument->PutInFormulaTree( this );
+}
+
+
+void ScFormulaCell::CompileTokenArray( BOOL bNoListening )
+{
+ // Not already compiled?
+ if( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
+ 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 );
+ }
+}
+
+
+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);
+ String aFormula, aFormulaNmsp;
+ aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
+ pDocument->DecXMLImportedFormulaCount( aFormula.Len() );
+ rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
+ // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
+ if ( pCode )
+ pCode->Clear();
+ ScTokenArray* pCodeOld = pCode;
+ pCode = aComp.CompileString( aFormula, aFormulaNmsp );
+ delete pCodeOld;
+ if( !pCode->GetCodeError() )
+ {
+ if ( !pCode->GetLen() )
+ {
+ if ( aFormula.GetChar(0) == '=' )
+ pCode->AddBad( aFormula.GetBuffer() + 1 );
+ else
+ pCode->AddBad( aFormula.GetBuffer() );
+ }
+ bSubTotal = aComp.CompileTokenArray();
+ if( !pCode->GetCodeError() )
+ {
+ nFormatType = aComp.GetNumFormatType();
+ nFormatIndex = 0;
+ bChanged = TRUE;
+ bCompile = FALSE;
+ StartListeningTo( pDocument );
+ }
+ }
+ else
+ {
+ bChanged = TRUE;
+ SetTextWidth( TEXTWIDTH_DIRTY );
+ SetScriptType( 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 );
+}
+
+
+void ScFormulaCell::CalcAfterLoad()
+{
+ BOOL bNewCompiled = FALSE;
+ // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis,
+ // aber kein TokenArray
+ if( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
+ {
+ Compile( aResult.GetHybridFormula(), TRUE, eTempGrammar);
+ aResult.SetToken( NULL);
+ bDirty = TRUE;
+ bNewCompiled = TRUE;
+ }
+ // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen
+ // wurde, da die RangeNames erst jetzt existieren.
+ 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;
+ }
+ // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503
+ // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib
+ // bei einem fabs(-NAN) abstuerzt (#32739#)
+ // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht
+ if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
+ {
+ DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?");
+ aResult.SetResultError( errIllegalFPOperation );
+ bDirty = TRUE;
+ }
+ // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix,
+ // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge
+ if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
+ GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
+ {
+ cMatrixFlag = MM_FORMULA;
+ SetMatColsRows( 1, 1);
+ }
+ // Muss die Zelle berechnet werden?
+ // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann
+ // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL
+ if( !bNewCompiled || !pCode->GetCodeError() )
+ {
+ StartListeningTo( pDocument );
+ if( !pCode->IsRecalcModeNormal() )
+ bDirty = TRUE;
+ }
+ if ( pCode->IsRecalcModeAlways() )
+ { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie
+ // auch bei jedem F9 berechnet werden.
+ bDirty = TRUE;
+ }
+ // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in
+ // SetDirtyAfterLoad.
+}
+
+
+bool ScFormulaCell::MarkUsedExternalReferences()
+{
+ return pCode && pDocument->MarkUsedExternalReferences( *pCode);
+}
+
+
+// FIXME: set to 0
+#define erDEBUGDOT 0
+// If set to 1, write output that's suitable for graphviz tools like dot.
+// Only node1 -> node2 entries are written, you'll have to manually surround
+// the file content with [strict] digraph name { ... }
+// The ``strict'' keyword might be necessary in case of multiple identical
+// paths like they occur in iterations, otherwise dot may consume too much
+// memory when generating the layout, or you'll get unreadable output. On the
+// other hand, information about recurring calculation is lost then.
+// Generates output only if variable nDebug is set in debugger, see below.
+// FIXME: currently doesn't cope with iterations and recursions. Code fragments
+// are a leftover from a previous debug session, meant as a pointer.
+#if erDEBUGDOT
+#include <cstdio>
+using ::std::fopen;
+using ::std::fprintf;
+#include <vector>
+static const char aDebugDotFile[] = "ttt_debug.dot";
+#endif
+
+void ScFormulaCell::Interpret()
+{
+
+#if erDEBUGDOT
+ static int nDebug = 0;
+ static const int erDEBUGDOTRUN = 3;
+ static FILE* pDebugFile = 0;
+ static sal_Int32 nDebugRootCount = 0;
+ static unsigned int nDebugPathCount = 0;
+ static ScAddress aDebugLastPos( ScAddress::INITIALIZE_INVALID);
+ static ScAddress aDebugThisPos( ScAddress::INITIALIZE_INVALID);
+ typedef ::std::vector< ByteString > DebugVector;
+ static DebugVector aDebugVec;
+ class DebugElement
+ {
+ public:
+ static void push( ScFormulaCell* pCell )
+ {
+ aDebugThisPos = pCell->aPos;
+ if (aDebugVec.empty())
+ {
+ ByteString aR( "root_");
+ aR += ByteString::CreateFromInt32( ++nDebugRootCount);
+ aDebugVec.push_back( aR);
+ }
+ String aStr;
+ pCell->aPos.Format( aStr, SCA_VALID | SCA_TAB_3D, pCell->GetDocument(),
+ pCell->GetDocument()->GetAddressConvention() );
+ ByteString aB( aStr, RTL_TEXTENCODING_UTF8);
+ aDebugVec.push_back( aB);
+ }
+ static void pop()
+ {
+ aDebugLastPos = aDebugThisPos;
+ if (!aDebugVec.empty())
+ {
+ aDebugVec.pop_back();
+ if (aDebugVec.size() == 1)
+ {
+ aDebugVec.pop_back();
+ aDebugLastPos = ScAddress( ScAddress::INITIALIZE_INVALID);
+ }
+ }
+ }
+ DebugElement( ScFormulaCell* p ) { push(p); }
+ ~DebugElement() { pop(); }
+ };
+ class DebugDot
+ {
+ public:
+ static void out( const char* pColor )
+ {
+ if (nDebug != erDEBUGDOTRUN)
+ return;
+ char pColorString[256];
+ sprintf( pColorString, (*pColor ?
+ ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor,
+ pColor);
+ size_t n = aDebugVec.size();
+ fprintf( pDebugFile,
+ "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n",
+ aDebugVec[n-2].GetBuffer(), aDebugVec[n-1].GetBuffer(),
+ ++nDebugPathCount, pColorString, n-1);
+ fflush( pDebugFile);
+ }
+ };
+ #define erDEBUGDOT_OUT( p ) (DebugDot::out(p))
+ #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p))
+ #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop())
+#else
+ #define erDEBUGDOT_OUT( p )
+ #define erDEBUGDOT_ELEMENT_PUSH( p )
+ #define erDEBUGDOT_ELEMENT_POP()
+#endif
+
+ if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
+ return; // no double/triple processing
+
+ //! HACK:
+ // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen
+ // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!!
+
+ if ( pDocument->IsInDdeLinkUpdate() )
+ return;
+
+#if erDEBUGDOT
+ // set nDebug=1 in debugger to init things
+ if (nDebug == 1)
+ {
+ ++nDebug;
+ pDebugFile = fopen( aDebugDotFile, "a");
+ if (!pDebugFile)
+ nDebug = 0;
+ else
+ nDebug = erDEBUGDOTRUN;
+ }
+ // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output
+ DebugElement aDebugElem( this);
+ // set nDebug=5 in debugger to close output
+ if (nDebug == 5)
+ {
+ nDebug = 0;
+ fclose( pDebugFile);
+ pDebugFile = 0;
+ }
+#endif
+
+ if (bRunning)
+ {
+
+#if erDEBUGDOT
+ if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
+ aDebugThisPos != aDebugLastPos)
+ erDEBUGDOT_OUT(aDebugThisPos == aDebugLastPos ? "orange" :
+ (pDocument->GetRecursionHelper().GetIteration() ? "blue" :
+ "red"));
+#endif
+
+ 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;
+ }
+ // #63038# 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 ;
+
+ erDEBUGDOT_OUT( pDocument->GetRecursionHelper().GetIteration() ? "magenta" : "");
+
+ ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
+ BOOL bOldRunning = bRunning;
+ if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
+ {
+ bRunning = TRUE;
+ rRecursionHelper.SetInRecursionReturn( true);
+ }
+ else
+ {
+ 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.
+ USHORT 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;
+ USHORT 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;
+ pIterCell->SetTextWidth( TEXTWIDTH_DIRTY);
+ pIterCell->SetScriptType( 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.GetStart());
+ !rRecursionHelper.IsInReturn() && aIter !=
+ rRecursionHelper.GetEnd(); ++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.GetStart(),
+ 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.
+ // 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().Len() )
+ {
+ pCode->SetCodeError( errNoCode );
+ // This is worth an assertion; if encountered in daily work
+ // documents we might need another solution. Or just confirm correctness.
+ DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but 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);
+ USHORT 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 because 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());
+ 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 ) // 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() );
+ }
+ else
+ {
+ ScFormulaResult aNewResult( p->GetResultToken());
+ 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 && 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 )
+ {
+ 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().Is() )
+ {
+ // 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());
+ }
+ if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
+ {
+ // Coded double error may occur via filter import.
+ USHORT nErr = GetDoubleErrorValue( aResult.GetDouble());
+ aResult.SetResultError( nErr);
+ bChanged = bContentChanged = true;
+ }
+ if( bChanged )
+ {
+ SetTextWidth( TEXTWIDTH_DIRTY );
+ SetScriptType( 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 Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten)
+
+ if ( pCode->IsRecalcModeForced() )
+ {
+ ULONG nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
+ aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
+ if ( nValidation )
+ {
+ const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
+ if ( pData && !pData->IsDataValid( this, aPos ) )
+ pData->DoCalcError( this );
+ }
+ }
+
+ // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren
+ ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
+ pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
+ }
+ else
+ {
+ // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen
+ DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" );
+ bDirty = FALSE;
+ bTableOpDirty = FALSE;
+ }
+}
+
+
+void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
+{
+ ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
+ if (pMat)
+ pMat->SetMatColsRows( nCols, nRows);
+ else if (nCols || nRows)
+ aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
+}
+
+
+void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
+{
+ const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
+ if (pMat)
+ pMat->GetMatColsRows( nCols, nRows);
+ else
+ {
+ nCols = 0;
+ nRows = 0;
+ }
+}
+
+
+ULONG ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, 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 __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
+{
+ if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
+ {
+ const ScHint* p = PTR_CAST( ScHint, &rHint );
+ 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;
+ bDirty = TRUE;
+ }
+ // #35962# 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?!?
+ // #87866# 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()
+{
+ if ( !IsInChangeTrack() )
+ {
+ if ( pDocument->GetHardRecalcState() )
+ bDirty = TRUE;
+ else
+ {
+ // Mehrfach-FormulaTracking in Load und in CompileAll
+ // nach CopyScenario und CopyBlockFromClip vermeiden.
+ // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=FALSE
+ // setzen, z.B. in CompileTokenArray
+ if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
+ {
+ bDirty = TRUE;
+ pDocument->AppendToFormulaTrack( this );
+ pDocument->TrackFormulas();
+ }
+ }
+
+ if (pDocument->IsStreamValid(aPos.Tab()))
+ pDocument->SetStreamValid(aPos.Tab(), FALSE);
+ }
+}
+
+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( USHORT 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( String& rURL, String& rCellText )
+{
+ String aCellString;
+
+ Color* pColor;
+
+ // Cell Text uses the Cell format while the URL uses
+ // the default format for the type.
+ ULONG nCellFormat = pDocument->GetNumberFormat( aPos );
+ SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
+
+ if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
+ nCellFormat = GetStandardFormat( *pFormatter,nCellFormat );
+
+ ULONG nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER);
+
+ if ( IsValue() )
+ {
+ double fValue = GetValue();
+ pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
+ }
+ else
+ {
+ GetString( aCellString );
+ pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
+ }
+ ScConstMatrixRef xMat( aResult.GetMatrix());
+ if (xMat)
+ {
+ ScMatValType nMatValType;
+ // determine if the matrix result is a string or value.
+ const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType);
+ if (pMatVal)
+ {
+ if (!ScMatrix::IsValueType( nMatValType))
+ rURL = pMatVal->GetString();
+ else
+ pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor );
+ }
+ }
+
+ if(!rURL.Len())
+ {
+ 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;
+}
+
+EditTextObject* ScFormulaCell::CreateURLObject()
+{
+ String aCellText;
+ String aURL;
+ GetURLResult( aURL, aCellText );
+
+ SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT);
+ EditEngine& rEE = pDocument->GetEditEngine();
+ rEE.SetText( EMPTY_STRING );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) );
+
+ return rEE.CreateTextObject();
+}
+
+// ============================================================================
+
+ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell )
+{
+ pCode = pCell->GetCode();
+ pCode->Reset();
+ aPos = pCell->aPos;
+}
+
+BOOL lcl_ScDetectiveRefIter_SkipRef( ScToken* p )
+{
+ ScSingleRefData& rRef1 = p->GetSingleRef();
+ if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted()
+ || !rRef1.Valid() )
+ return TRUE;
+ if ( p->GetType() == svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
+ if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted()
+ || !rRef2.Valid() )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL ScDetectiveRefIter::GetNextRef( ScRange& rRange )
+{
+ BOOL bRet = FALSE;
+
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if (p)
+ p->CalcAbsIfRel( aPos );
+
+ while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) )
+ {
+ p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if (p)
+ p->CalcAbsIfRel( aPos );
+ }
+
+ if( p )
+ {
+ SingleDoubleRefProvider aProv( *p );
+ rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab );
+ rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab );
+ bRet = TRUE;
+ }
+
+ return bRet;
+}
+
+// ============================================================================