/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"

// INCLUDE ---------------------------------------------------------------

#include <svl/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 <editeng/editobj.hxx>
#include <svl/intitem.hxx>
#include <editeng/flditem.hxx>
#include <svl/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 sal_uInt16 MAXRECURSION = 400;

// STATIC DATA -----------------------------------------------------------

#ifdef USE_MEMPOOL
// MemPools auf 4k Boundaries  - 64 Bytes ausrichten
const sal_uInt16 nMemPoolValueCell = (0x8000 - 64) / sizeof(ScValueCell);
const sal_uInt16 nMemPoolFormulaCell = (0x8000 - 64) / sizeof(ScFormulaCell);
const sal_uInt16 nMemPoolStringCell = (0x4000 - 64) / sizeof(ScStringCell);
const sal_uInt16 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<sal_uInt8>(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(sal_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 (
                                        rRef1.nCol,
                                        rRef1.nRow,
                                        rRef1.nTab,
                                        rRef2.nCol,
                                        MAXROW,
                                        rRef2.nTab ), pFormCell );
                                }
                                else
                                {	// RowName
                                    pDoc->StartListeningArea( ScRange (
                                        rRef1.nCol,
                                        rRef1.nRow,
                                        rRef1.nTab,
                                        MAXCOL,
                                        rRef2.nRow,
                                        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( sal_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(sal_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 (
                                        rRef1.nCol,
                                        rRef1.nRow,
                                        rRef1.nTab,
                                        rRef2.nCol,
                                        MAXROW,
                                        rRef2.nTab ), pFormCell );
                                }
                                else
                                {	// RowName
                                    pDoc->EndListeningArea( ScRange (
                                        rRef1.nCol,
                                        rRef1.nRow,
                                        rRef1.nTab,
                                        MAXCOL,
                                        rRef2.nRow,
                                        rRef2.nTab ), pFormCell );
                                }
                            }
                            else
                            {
                                pDoc->EndListeningArea( ScRange (
                                    rRef1.nCol,
                                    rRef1.nRow,
                                    rRef1.nTab,
                                    rRef2.nCol,
                                    rRef2.nRow,
                                    rRef2.nTab ), pFormCell );
                            }
                        }
                    break;
                    default:
                        ;   // nothing
                }
			}
		}
	}
}


sal_uInt16 ScBaseCell::GetErrorCode() const
{
	switch ( eCellType )
	{
		case CELLTYPE_FORMULA :
			return ((ScFormulaCell*)this)->GetErrCode();
		default:
			return 0;
	}
}


sal_Bool ScBaseCell::HasEmptyData() const
{
	switch ( eCellType )
	{
		case CELLTYPE_NOTE :
			return sal_True;
		case CELLTYPE_FORMULA :
			return ((ScFormulaCell*)this)->IsEmpty();
		default:
			return sal_False;
	}
}


sal_Bool ScBaseCell::HasValueData() const
{
	switch ( eCellType )
	{
		case CELLTYPE_VALUE :
			return sal_True;
		case CELLTYPE_FORMULA :
			return ((ScFormulaCell*)this)->IsValue();
		default:
			return sal_False;
	}
}


sal_Bool ScBaseCell::HasStringData() const
{
	switch ( eCellType )
	{
		case CELLTYPE_STRING :
		case CELLTYPE_EDIT :
			return sal_True;
		case CELLTYPE_FORMULA :
			return !((ScFormulaCell*)this)->IsValue();
		default:
			return sal_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
sal_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 sal_False;

	switch ( eType1 )				// beide Typen gleich
	{
		case CELLTYPE_NONE:			// beide leer
			return sal_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
				{
					sal_Bool bEqual = sal_True;
					sal_uInt16 nLen = pCode1->GetLen();
					FormulaToken** ppToken1 = pCode1->GetArray();
					FormulaToken** ppToken2 = pCode2->GetArray();
					for (sal_uInt16 i=0; i<nLen; i++)
						if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) )
						{
							bEqual = sal_False;
							break;
						}

					if (bEqual)
						return sal_True;
				}

				return sal_False;		// unterschiedlich lang oder unterschiedliche Tokens
			}
		default:
			DBG_ERROR("huch, was fuer Zellen???");
	}
	return sal_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( sal_False ),
	bChanged( sal_False ),
	bRunning( sal_False ),
	bCompile( sal_False ),
	bSubTotal( sal_False ),
	bIsIterCell( sal_False ),
	bInChangeTrack( sal_False ),
	bTableOpDirty( sal_False ),
	bNeedListening( sal_False ),
	aPos(0,0,0)
{
}

ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
							  const String& 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( sal_True ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0
	bChanged( sal_False ),
	bRunning( sal_False ),
	bCompile( sal_False ),
	bSubTotal( sal_False ),
	bIsIterCell( sal_False ),
	bInChangeTrack( sal_False ),
	bTableOpDirty( sal_False ),
	bNeedListening( sal_False ),
	aPos( rPos )
{
    Compile( rFormula, sal_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, 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 ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0
	bChanged( sal_False ),
	bRunning( sal_False ),
	bCompile( sal_False ),
	bSubTotal( sal_False ),
	bIsIterCell( sal_False ),
	bInChangeTrack( sal_False ),
	bTableOpDirty( sal_False ),
	bNeedListening( sal_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 = sal_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( sal_False ),
	bCompile( rCell.bCompile ),
	bSubTotal( rCell.bSubTotal ),
	bIsIterCell( sal_False ),
	bInChangeTrack( sal_False ),
	bTableOpDirty( sal_False ),
	bNeedListening( sal_False ),
	aPos( rPos )
{
	pCode = (rCell.pCode) ? rCell.pCode->Clone() : NULL;

    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 = sal_True;
	}
    //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
    sal_Bool bCompileLater = sal_False;
    sal_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 = sal_True;
				}
				else
                    bCompile = sal_True;    // invalid reference!
			}
			else if ( t->GetOpCode() == ocColRowName )
			{
                bCompile = sal_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( sal_True );
        }
	}

    if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
        StartListeningTo( &rDoc );
}

ScFormulaCell::~ScFormulaCell()
{
	pDocument->RemoveFromFormulaTree( this );

    if (pDocument->HasExternalRefManager())
        pDocument->GetExternalRefManager()->removeRefCell(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, sal_Bool bNoListening,
                            const FormulaGrammar::Grammar eGrammar )
{
	//#118851#, the initialization code for pCode after it can not be gnored if it is still NULL
	if ( pCode && pDocument->IsClipOrUndo() ) return;
	sal_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 = sal_True;
		CompileTokenArray( bNoListening );
	}
	else
	{
		bChanged = sal_True;
		SetTextWidth( TEXTWIDTH_DIRTY );
		SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
	}
	if ( bWasInFormulaTree )
		pDocument->PutInFormulaTree( this );
}


void ScFormulaCell::CompileTokenArray( sal_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
		sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
		if ( bWasInFormulaTree )
			pDocument->RemoveFromFormulaTree( this );

		// Loading from within filter? No listening yet!
		if( pDocument->IsInsertingFromOtherDoc() )
			bNoListening = sal_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 = sal_True;
            aResult.SetToken( NULL);
			bCompile = sal_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 = sal_True;
			bCompile = sal_False;
			StartListeningTo( pDocument );
		}
	}
	else
	{
		bChanged = sal_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( sal_True );
}


void ScFormulaCell::CalcAfterLoad()
{
	sal_Bool bNewCompiled = sal_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(), sal_True, eTempGrammar);
		aResult.SetToken( NULL);
		bDirty = sal_True;
		bNewCompiled = sal_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 = sal_True;
		bCompile = sal_False;
		bNewCompiled = sal_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 = sal_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 = sal_True;
	}
	if ( pCode->IsRecalcModeAlways() )
	{	// zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie
		// auch bei jedem F9 berechnet werden.
		bDirty = sal_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();
    sal_Bool bOldRunning = bRunning;
    if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
    {
        bRunning = sal_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 = sal_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 = sal_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 = sal_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 = sal_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 = sal_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 = sal_False;
                            pIterCell->bTableOpDirty = sal_False;
                            pIterCell->aResult.SetResultError( errNoConvergence);
                            pIterCell->bChanged = sal_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);
        sal_uInt16 nOldErrCode = aResult.GetResultError();
        if ( nSeenInIteration == 0 )
        {   // Only the first time
            // With bChanged=sal_False, if a newly compiled cell has a result of
            // 0.0, no change is detected and the cell will not be repainted.
            // bChanged = sal_False;
            aResult.SetResultError( 0 );
        }

        switch ( aResult.GetResultError() )
        {
            case errCircularReference :     // will be determined again if so
                aResult.SetResultError( 0 );
            break;
        }

        sal_Bool bOldRunning = bRunning;
        bRunning = sal_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)
        sal_Bool bContentChanged = sal_False;

        // Do not create a HyperLink() cell if the formula results in an error.
        if( p->GetError() && pCode->IsHyperLink())
            pCode->SetHyperLink(sal_False);

        if( p->GetError() && p->GetError() != errCircularReference)
        {
            bDirty = sal_False;
            bTableOpDirty = sal_False;
            bChanged = sal_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 = sal_False;
                    bTableOpDirty = sal_False;
                }
            }
        }

        // New error code?
        if( p->GetError() != nOldErrCode )
        {
            bChanged = sal_True;
            // bContentChanged only has to be set if the file content would be changed
            if ( aResult.GetCellResultType() != svUnknown )
                bContentChanged = sal_True;
        }
        // Different number format?
        if( nFormatType != p->GetRetFormatType() )
        {
            nFormatType = p->GetRetFormatType();
            bChanged = sal_True;
        }
        if( nFormatIndex != p->GetRetFormatIndex() )
        {
            nFormatIndex = p->GetRetFormatIndex();
            bChanged = sal_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 = sal_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 = sal_False;
            bTableOpDirty = sal_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.
            sal_uInt16 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=sal_True, because even if called from pending row height update,
            // a changed result must still reset the stream flag
            pDocument->SetStreamValid(aPos.Tab(), sal_False, sal_True);
        }
        if ( !pCode->IsRecalcModeAlways() )
            pDocument->RemoveFromFormulaTree( this );

        //  FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten)

        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 );
                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 = sal_False;
        bTableOpDirty = sal_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;
    }
}


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 __EXPORT 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))
		{
            sal_Bool bForceTrack = sal_False;
			if ( nHint & SC_HINT_TABLEOPDIRTY )
            {
                bForceTrack = !bTableOpDirty;
                if ( !bTableOpDirty )
                {
                    pDocument->AddTableOpFormulaCell( this );
                    bTableOpDirty = sal_True;
                }
            }
			else
            {
                bForceTrack = !bDirty;
				bDirty = sal_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 = sal_True;
		else
		{
			// Mehrfach-FormulaTracking in Load und in CompileAll
			// nach CopyScenario und CopyBlockFromClip vermeiden.
			// Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=sal_False
			// setzen, z.B. in CompileTokenArray
			if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
			{
				bDirty = sal_True;
				pDocument->AppendToFormulaTrack( this );
				pDocument->TrackFormulas();
			}
		}

        if (pDocument->IsStreamValid(aPos.Tab()))
            pDocument->SetStreamValid(aPos.Tab(), sal_False);
	}
}

void ScFormulaCell::SetDirtyAfterLoad()
{
    bDirty = sal_True;
    if ( !pDocument->GetHardRecalcState() )
        pDocument->PutInFormulaTree( this );
}

void ScFormulaCell::SetTableOpDirty()
{
	if ( !IsInChangeTrack() )
	{
		if ( pDocument->GetHardRecalcState() )
			bTableOpDirty = sal_True;
		else
		{
			if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
			{
                if ( !bTableOpDirty )
                {
                    pDocument->AddTableOpFormulaCell( this );
                    bTableOpDirty = sal_True;
                }
				pDocument->AppendToFormulaTrack( this );
				pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
			}
		}
	}
}


sal_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 = sal_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.
    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
    {
        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;
}

sal_Bool lcl_ScDetectiveRefIter_SkipRef( ScToken* p )
{
	ScSingleRefData& rRef1 = p->GetSingleRef();
	if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted()
			|| !rRef1.Valid() )
		return sal_True;
	if ( p->GetType() == svDoubleRef )
	{
		ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
		if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted()
				|| !rRef2.Valid() )
			return sal_True;
	}
	return sal_False;
}

sal_Bool ScDetectiveRefIter::GetNextRef( ScRange& rRange )
{
	sal_Bool bRet = sal_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 = sal_True;
	}

	return bRet;
}

// ============================================================================
