1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cell.cxx,v $
10 * $Revision: 1.44.38.6 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 // INCLUDE ---------------------------------------------------------------
36 #include <svtools/zforlist.hxx>
38 #include "scitems.hxx"
41 #include "compiler.hxx"
42 #include "interpre.hxx"
43 #include "document.hxx"
44 #include "scmatrix.hxx"
45 #include "dociter.hxx"
46 #include "docoptio.hxx"
47 #include "rechead.hxx"
48 #include "rangenam.hxx"
50 #include "ddelink.hxx"
51 #include "validat.hxx"
52 #include "progress.hxx"
53 #include "editutil.hxx"
54 #include "recursionhelper.hxx"
56 #include "externalrefmgr.hxx"
57 #include "macromgr.hxx"
58 #include <svx/editobj.hxx>
59 #include <svtools/intitem.hxx>
60 #include <svx/flditem.hxx>
61 #include <svtools/broadcast.hxx>
63 using namespace formula
;
64 // More or less arbitrary, of course all recursions must fit into available
65 // stack space (which is what on all systems we don't know yet?). Choosing a
66 // lower value may be better than trying a much higher value that also isn't
67 // sufficient but temporarily leads to high memory consumption. On the other
68 // hand, if the value fits all recursions, execution is quicker as no resumes
69 // are necessary. Could be made a configurable option.
70 // Allow for a year's calendar (366).
71 const USHORT MAXRECURSION
= 400;
73 // STATIC DATA -----------------------------------------------------------
76 // MemPools auf 4k Boundaries - 64 Bytes ausrichten
77 const USHORT nMemPoolValueCell
= (0x8000 - 64) / sizeof(ScValueCell
);
78 const USHORT nMemPoolFormulaCell
= (0x8000 - 64) / sizeof(ScFormulaCell
);
79 const USHORT nMemPoolStringCell
= (0x4000 - 64) / sizeof(ScStringCell
);
80 const USHORT nMemPoolNoteCell
= (0x1000 - 64) / sizeof(ScNoteCell
);
81 IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell
, nMemPoolValueCell
, nMemPoolValueCell
)
82 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell
, nMemPoolFormulaCell
, nMemPoolFormulaCell
)
83 IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell
, nMemPoolStringCell
, nMemPoolStringCell
)
84 IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell
, nMemPoolNoteCell
, nMemPoolNoteCell
)
87 // ============================================================================
89 ScBaseCell::ScBaseCell( CellType eNewType
) :
92 nTextWidth( TEXTWIDTH_DIRTY
),
93 eCellType( sal::static_int_cast
<BYTE
>(eNewType
) ),
94 nScriptType( SC_SCRIPTTYPE_UNKNOWN
)
98 ScBaseCell::ScBaseCell( const ScBaseCell
& rCell
) :
101 nTextWidth( rCell
.nTextWidth
),
102 eCellType( rCell
.eCellType
),
103 nScriptType( SC_SCRIPTTYPE_UNKNOWN
)
107 ScBaseCell::~ScBaseCell()
110 delete mpBroadcaster
;
111 DBG_ASSERT( eCellType
== CELLTYPE_DESTROYED
, "BaseCell Destructor" );
116 ScBaseCell
* lclCloneCell( const ScBaseCell
& rSrcCell
, ScDocument
& rDestDoc
, const ScAddress
& rDestPos
, int nCloneFlags
)
118 switch( rSrcCell
.GetCellType() )
121 return new ScValueCell( static_cast< const ScValueCell
& >( rSrcCell
) );
122 case CELLTYPE_STRING
:
123 return new ScStringCell( static_cast< const ScStringCell
& >( rSrcCell
) );
125 return new ScEditCell( static_cast< const ScEditCell
& >( rSrcCell
), rDestDoc
);
126 case CELLTYPE_FORMULA
:
127 return new ScFormulaCell( static_cast< const ScFormulaCell
& >( rSrcCell
), rDestDoc
, rDestPos
, nCloneFlags
);
129 return new ScNoteCell
;
132 DBG_ERROR( "lclCloneCell - unknown cell type" );
138 ScBaseCell
* ScBaseCell::CloneWithoutNote( ScDocument
& rDestDoc
, int nCloneFlags
) const
140 // notes will not be cloned -> cell address only needed for formula cells
142 if( eCellType
== CELLTYPE_FORMULA
)
143 aDestPos
= static_cast< const ScFormulaCell
* >( this )->aPos
;
144 return lclCloneCell( *this, rDestDoc
, aDestPos
, nCloneFlags
);
147 ScBaseCell
* ScBaseCell::CloneWithoutNote( ScDocument
& rDestDoc
, const ScAddress
& rDestPos
, int nCloneFlags
) const
149 return lclCloneCell( *this, rDestDoc
, rDestPos
, nCloneFlags
);
152 ScBaseCell
* ScBaseCell::CloneWithNote( const ScAddress
& rOwnPos
, ScDocument
& rDestDoc
, const ScAddress
& rDestPos
, int nCloneFlags
) const
154 ScBaseCell
* pNewCell
= lclCloneCell( *this, rDestDoc
, rDestPos
, nCloneFlags
);
158 pNewCell
= new ScNoteCell
;
159 bool bCloneCaption
= (nCloneFlags
& SC_CLONECELL_NOCAPTION
) == 0;
160 pNewCell
->TakeNote( mpNote
->Clone( rOwnPos
, rDestDoc
, rDestPos
, bCloneCaption
) );
165 void ScBaseCell::Delete()
171 delete (ScValueCell
*) this;
173 case CELLTYPE_STRING
:
174 delete (ScStringCell
*) this;
177 delete (ScEditCell
*) this;
179 case CELLTYPE_FORMULA
:
180 delete (ScFormulaCell
*) this;
183 delete (ScNoteCell
*) this;
186 DBG_ERROR("Unbekannter Zellentyp");
191 bool ScBaseCell::IsBlank( bool bIgnoreNotes
) const
193 return (eCellType
== CELLTYPE_NOTE
) && (bIgnoreNotes
|| !mpNote
);
196 void ScBaseCell::TakeNote( ScPostIt
* pNote
)
202 ScPostIt
* ScBaseCell::ReleaseNote()
204 ScPostIt
* pNote
= mpNote
;
209 void ScBaseCell::DeleteNote()
214 void ScBaseCell::TakeBroadcaster( SvtBroadcaster
* pBroadcaster
)
216 delete mpBroadcaster
;
217 mpBroadcaster
= pBroadcaster
;
220 SvtBroadcaster
* ScBaseCell::ReleaseBroadcaster()
222 SvtBroadcaster
* pBroadcaster
= mpBroadcaster
;
227 void ScBaseCell::DeleteBroadcaster()
229 DELETEZ( mpBroadcaster
);
232 ScBaseCell
* ScBaseCell::CreateTextCell( const String
& rString
, ScDocument
* pDoc
)
234 if ( rString
.Search('\n') != STRING_NOTFOUND
|| rString
.Search(CHAR_CR
) != STRING_NOTFOUND
)
235 return new ScEditCell( rString
, pDoc
);
237 return new ScStringCell( rString
);
240 void ScBaseCell::StartListeningTo( ScDocument
* pDoc
)
242 if ( eCellType
== CELLTYPE_FORMULA
&& !pDoc
->IsClipOrUndo()
243 && !pDoc
->GetNoListening()
244 && !((ScFormulaCell
*)this)->IsInChangeTrack()
247 pDoc
->SetDetectiveDirty(TRUE
); // es hat sich was geaendert...
249 ScFormulaCell
* pFormCell
= (ScFormulaCell
*)this;
250 ScTokenArray
* pArr
= pFormCell
->GetCode();
251 if( pArr
->IsRecalcModeAlways() )
252 pDoc
->StartListeningArea( BCA_LISTEN_ALWAYS
, pFormCell
);
257 while ( ( t
= static_cast<ScToken
*>(pArr
->GetNextReferenceRPN()) ) != NULL
)
259 StackVar eType
= t
->GetType();
260 ScSingleRefData
& rRef1
= t
->GetSingleRef();
261 ScSingleRefData
& rRef2
= (eType
== svDoubleRef
?
262 t
->GetDoubleRef().Ref2
: rRef1
);
266 rRef1
.CalcAbsIfRel( pFormCell
->aPos
);
269 pDoc
->StartListeningCell(
270 ScAddress( rRef1
.nCol
,
272 rRef1
.nTab
), pFormCell
);
276 t
->CalcAbsIfRel( pFormCell
->aPos
);
277 if ( rRef1
.Valid() && rRef2
.Valid() )
279 if ( t
->GetOpCode() == ocColRowNameAuto
)
281 if ( rRef1
.IsColRel() )
283 pDoc
->StartListeningArea( ScRange (
289 rRef2
.nTab
), pFormCell
);
293 pDoc
->StartListeningArea( ScRange (
299 rRef2
.nTab
), pFormCell
);
304 pDoc
->StartListeningArea( ScRange (
310 rRef2
.nTab
), pFormCell
);
319 pFormCell
->SetNeedsListening( FALSE
);
323 // pArr gesetzt -> Referenzen von anderer Zelle nehmen
324 // dann muss auch aPos uebergeben werden!
326 void ScBaseCell::EndListeningTo( ScDocument
* pDoc
, ScTokenArray
* pArr
,
329 if ( eCellType
== CELLTYPE_FORMULA
&& !pDoc
->IsClipOrUndo()
330 && !((ScFormulaCell
*)this)->IsInChangeTrack()
333 pDoc
->SetDetectiveDirty(TRUE
); // es hat sich was geaendert...
335 ScFormulaCell
* pFormCell
= (ScFormulaCell
*)this;
336 if( pFormCell
->GetCode()->IsRecalcModeAlways() )
337 pDoc
->EndListeningArea( BCA_LISTEN_ALWAYS
, pFormCell
);
342 pArr
= pFormCell
->GetCode();
343 aPos
= pFormCell
->aPos
;
347 while ( ( t
= static_cast<ScToken
*>(pArr
->GetNextReferenceRPN()) ) != NULL
)
349 StackVar eType
= t
->GetType();
350 ScSingleRefData
& rRef1
= t
->GetSingleRef();
351 ScSingleRefData
& rRef2
= (eType
== svDoubleRef
?
352 t
->GetDoubleRef().Ref2
: rRef1
);
356 rRef1
.CalcAbsIfRel( aPos
);
359 pDoc
->EndListeningCell(
360 ScAddress( rRef1
.nCol
,
362 rRef1
.nTab
), pFormCell
);
366 t
->CalcAbsIfRel( aPos
);
367 if ( rRef1
.Valid() && rRef2
.Valid() )
369 if ( t
->GetOpCode() == ocColRowNameAuto
)
371 if ( rRef1
.IsColRel() )
373 pDoc
->EndListeningArea( ScRange (
379 rRef2
.nTab
), pFormCell
);
383 pDoc
->EndListeningArea( ScRange (
389 rRef2
.nTab
), pFormCell
);
394 pDoc
->EndListeningArea( ScRange (
400 rRef2
.nTab
), pFormCell
);
413 USHORT
ScBaseCell::GetErrorCode() const
417 case CELLTYPE_FORMULA
:
418 return ((ScFormulaCell
*)this)->GetErrCode();
425 BOOL
ScBaseCell::HasEmptyData() const
431 case CELLTYPE_FORMULA
:
432 return ((ScFormulaCell
*)this)->IsEmpty();
439 BOOL
ScBaseCell::HasValueData() const
443 case CELLTYPE_VALUE
:
445 case CELLTYPE_FORMULA
:
446 return ((ScFormulaCell
*)this)->IsValue();
453 BOOL
ScBaseCell::HasStringData() const
457 case CELLTYPE_STRING
:
460 case CELLTYPE_FORMULA
:
461 return !((ScFormulaCell
*)this)->IsValue();
467 String
ScBaseCell::GetStringData() const
472 case CELLTYPE_STRING
:
473 ((const ScStringCell
*)this)->GetString( aStr
);
476 ((const ScEditCell
*)this)->GetString( aStr
);
478 case CELLTYPE_FORMULA
:
479 ((ScFormulaCell
*)this)->GetString( aStr
); // an der Formelzelle nicht-const
486 BOOL
ScBaseCell::CellEqual( const ScBaseCell
* pCell1
, const ScBaseCell
* pCell2
)
488 CellType eType1
= CELLTYPE_NONE
;
489 CellType eType2
= CELLTYPE_NONE
;
492 eType1
= pCell1
->GetCellType();
493 if (eType1
== CELLTYPE_EDIT
)
494 eType1
= CELLTYPE_STRING
;
495 else if (eType1
== CELLTYPE_NOTE
)
496 eType1
= CELLTYPE_NONE
;
500 eType2
= pCell2
->GetCellType();
501 if (eType2
== CELLTYPE_EDIT
)
502 eType2
= CELLTYPE_STRING
;
503 else if (eType2
== CELLTYPE_NOTE
)
504 eType2
= CELLTYPE_NONE
;
506 if ( eType1
!= eType2
)
509 switch ( eType1
) // beide Typen gleich
511 case CELLTYPE_NONE
: // beide leer
513 case CELLTYPE_VALUE
: // wirklich Value-Zellen
514 return ( ((const ScValueCell
*)pCell1
)->GetValue() ==
515 ((const ScValueCell
*)pCell2
)->GetValue() );
516 case CELLTYPE_STRING
: // String oder Edit
519 if ( pCell1
->GetCellType() == CELLTYPE_STRING
)
520 ((const ScStringCell
*)pCell1
)->GetString(aText1
);
522 ((const ScEditCell
*)pCell1
)->GetString(aText1
);
524 if ( pCell2
->GetCellType() == CELLTYPE_STRING
)
525 ((const ScStringCell
*)pCell2
)->GetString(aText2
);
527 ((const ScEditCell
*)pCell2
)->GetString(aText2
);
528 return ( aText1
== aText2
);
530 case CELLTYPE_FORMULA
:
532 //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!!
533 //! Vergleichsfunktion an der Formelzelle ???
534 //! Abfrage mit ScColumn::SwapRow zusammenfassen!
536 ScTokenArray
* pCode1
= ((ScFormulaCell
*)pCell1
)->GetCode();
537 ScTokenArray
* pCode2
= ((ScFormulaCell
*)pCell2
)->GetCode();
539 if (pCode1
->GetLen() == pCode2
->GetLen()) // nicht-UPN
542 USHORT nLen
= pCode1
->GetLen();
543 FormulaToken
** ppToken1
= pCode1
->GetArray();
544 FormulaToken
** ppToken2
= pCode2
->GetArray();
545 for (USHORT i
=0; i
<nLen
; i
++)
546 if ( !ppToken1
[i
]->TextEqual(*(ppToken2
[i
])) )
556 return FALSE
; // unterschiedlich lang oder unterschiedliche Tokens
559 DBG_ERROR("huch, was fuer Zellen???");
564 // ============================================================================
566 ScNoteCell::ScNoteCell( SvtBroadcaster
* pBC
) :
567 ScBaseCell( CELLTYPE_NOTE
)
569 TakeBroadcaster( pBC
);
572 ScNoteCell::ScNoteCell( ScPostIt
* pNote
, SvtBroadcaster
* pBC
) :
573 ScBaseCell( CELLTYPE_NOTE
)
576 TakeBroadcaster( pBC
);
580 ScNoteCell::~ScNoteCell()
582 eCellType
= CELLTYPE_DESTROYED
;
586 // ============================================================================
588 ScValueCell::ScValueCell() :
589 ScBaseCell( CELLTYPE_VALUE
),
594 ScValueCell::ScValueCell( double fValue
) :
595 ScBaseCell( CELLTYPE_VALUE
),
601 ScValueCell::~ScValueCell()
603 eCellType
= CELLTYPE_DESTROYED
;
607 // ============================================================================
609 ScStringCell::ScStringCell() :
610 ScBaseCell( CELLTYPE_STRING
)
614 ScStringCell::ScStringCell( const String
& rString
) :
615 ScBaseCell( CELLTYPE_STRING
),
616 maString( rString
.intern() )
621 ScStringCell::~ScStringCell()
623 eCellType
= CELLTYPE_DESTROYED
;
627 // ============================================================================
633 ScFormulaCell::ScFormulaCell() :
634 ScBaseCell( CELLTYPE_FORMULA
),
635 eTempGrammar( FormulaGrammar::GRAM_DEFAULT
),
643 nFormatType( NUMBERFORMAT_NUMBER
),
645 cMatrixFlag ( MM_NONE
),
651 bIsIterCell( FALSE
),
652 bInChangeTrack( FALSE
),
653 bTableOpDirty( FALSE
),
654 bNeedListening( FALSE
),
659 ScFormulaCell::ScFormulaCell( ScDocument
* pDoc
, const ScAddress
& rPos
,
660 const String
& rFormula
,
661 const FormulaGrammar::Grammar eGrammar
,
663 ScBaseCell( CELLTYPE_FORMULA
),
664 eTempGrammar( eGrammar
),
672 nFormatType( NUMBERFORMAT_NUMBER
),
674 cMatrixFlag ( cMatInd
),
675 bDirty( TRUE
), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0
680 bIsIterCell( FALSE
),
681 bInChangeTrack( FALSE
),
682 bTableOpDirty( FALSE
),
683 bNeedListening( FALSE
),
686 Compile( rFormula
, TRUE
, eGrammar
); // bNoListening, Insert does that
689 // Wird von den Importfiltern verwendet
691 ScFormulaCell::ScFormulaCell( ScDocument
* pDoc
, const ScAddress
& rPos
,
692 const ScTokenArray
* pArr
,
693 const FormulaGrammar::Grammar eGrammar
, BYTE cInd
) :
694 ScBaseCell( CELLTYPE_FORMULA
),
695 eTempGrammar( eGrammar
),
696 pCode( pArr
? new ScTokenArray( *pArr
) : new ScTokenArray
),
703 nFormatType( NUMBERFORMAT_NUMBER
),
705 cMatrixFlag ( cInd
),
706 bDirty( NULL
!= pArr
), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0
711 bIsIterCell( FALSE
),
712 bInChangeTrack( FALSE
),
713 bTableOpDirty( FALSE
),
714 bNeedListening( FALSE
),
717 // UPN-Array erzeugen
718 if( pCode
->GetLen() && !pCode
->GetCodeError() && !pCode
->GetCodeLen() )
720 ScCompiler
aComp( pDocument
, aPos
, *pCode
);
721 aComp
.SetGrammar(eTempGrammar
);
722 bSubTotal
= aComp
.CompileTokenArray();
723 nFormatType
= aComp
.GetNumFormatType();
728 if ( pCode
->GetNextOpCodeRPN( ocSubTotal
) )
733 ScFormulaCell::ScFormulaCell( const ScFormulaCell
& rCell
, ScDocument
& rDoc
, const ScAddress
& rPos
, int nCloneFlags
) :
736 aResult( rCell
.aResult
),
737 eTempGrammar( rCell
.eTempGrammar
),
743 nFormatIndex( &rDoc
== rCell
.pDocument
? rCell
.nFormatIndex
: 0 ),
744 nFormatType( rCell
.nFormatType
),
746 cMatrixFlag ( rCell
.cMatrixFlag
),
747 bDirty( rCell
.bDirty
),
748 bChanged( rCell
.bChanged
),
750 bCompile( rCell
.bCompile
),
751 bSubTotal( rCell
.bSubTotal
),
752 bIsIterCell( FALSE
),
753 bInChangeTrack( FALSE
),
754 bTableOpDirty( FALSE
),
755 bNeedListening( FALSE
),
758 pCode
= rCell
.pCode
->Clone();
760 if ( nCloneFlags
& SC_CLONECELL_ADJUST3DREL
)
761 pCode
->ReadjustRelative3DReferences( rCell
.aPos
, aPos
);
763 // evtl. Fehler zuruecksetzen und neu kompilieren
764 // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben
765 // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten
766 if ( pCode
->GetCodeError() && !pDocument
->IsClipboard() && pCode
->GetLen() )
768 pCode
->SetCodeError( 0 );
771 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
772 BOOL bCompileLater
= FALSE
;
773 BOOL bClipMode
= rCell
.pDocument
->IsClipboard();
775 { // Name references with references and ColRowNames
778 while ( ( t
= static_cast<ScToken
*>(pCode
->GetNextReferenceOrName()) ) != NULL
&& !bCompile
)
780 if ( t
->GetOpCode() == ocExternalRef
)
782 // External name, cell, and area references.
785 else if ( t
->GetType() == svIndex
)
787 ScRangeData
* pRangeData
= rDoc
.GetRangeName()->FindIndex( t
->GetIndex() );
790 if( pRangeData
->HasReferences() )
794 bCompile
= TRUE
; // invalid reference!
796 else if ( t
->GetOpCode() == ocColRowName
)
798 bCompile
= TRUE
; // new lookup needed
799 bCompileLater
= bClipMode
;
805 if ( !bCompileLater
&& bClipMode
)
807 // Merging ranges needs the actual positions after UpdateReference.
808 // ColRowNames need new lookup after positions are adjusted.
809 bCompileLater
= pCode
->HasOpCode( ocRange
) || pCode
->HasOpCode( ocColRowName
);
811 if ( !bCompileLater
)
813 // bNoListening, not at all if in Clipboard/Undo,
814 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
815 CompileTokenArray( TRUE
);
819 if( nCloneFlags
& SC_CLONECELL_STARTLISTENING
)
820 StartListeningTo( &rDoc
);
823 ScFormulaCell::~ScFormulaCell()
825 pDocument
->RemoveFromFormulaTree( this );
826 if (pCode
->HasOpCode(ocMacro
))
827 pDocument
->GetMacroManager()->RemoveDependentCell(this);
831 eCellType
= CELLTYPE_DESTROYED
;
835 void ScFormulaCell::GetFormula( rtl::OUStringBuffer
& rBuffer
,
836 const FormulaGrammar::Grammar eGrammar
) const
838 if( pCode
->GetCodeError() && !pCode
->GetLen() )
840 rBuffer
= rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode
->GetCodeError()));
843 else if( cMatrixFlag
== MM_REFERENCE
)
845 // Reference to another cell that contains a matrix formula.
847 ScToken
* p
= static_cast<ScToken
*>(pCode
->GetNextReferenceRPN());
850 /* FIXME: original GetFormula() code obtained
851 * pCell only if (!this->IsInChangeTrack()),
852 * GetEnglishFormula() omitted that test.
853 * Can we live without in all cases? */
855 ScSingleRefData
& rRef
= p
->GetSingleRef();
856 rRef
.CalcAbsIfRel( aPos
);
858 pCell
= pDocument
->GetCell( ScAddress( rRef
.nCol
,
859 rRef
.nRow
, rRef
.nTab
) );
862 if (pCell
&& pCell
->GetCellType() == CELLTYPE_FORMULA
)
864 ((ScFormulaCell
*)pCell
)->GetFormula( rBuffer
, eGrammar
);
869 ScCompiler
aComp( pDocument
, aPos
, *pCode
);
870 aComp
.SetGrammar(eGrammar
);
871 aComp
.CreateStringFromTokenArray( rBuffer
);
876 DBG_ERROR("ScFormulaCell::GetFormula: not a matrix");
881 ScCompiler
aComp( pDocument
, aPos
, *pCode
);
882 aComp
.SetGrammar(eGrammar
);
883 aComp
.CreateStringFromTokenArray( rBuffer
);
887 rBuffer
.insert( 0, &ch
, 1 );
890 sal_Unicode
ch2('{');
891 rBuffer
.insert( 0, &ch2
, 1);
892 rBuffer
.append( sal_Unicode('}'));
896 void ScFormulaCell::GetFormula( String
& rFormula
, const FormulaGrammar::Grammar eGrammar
) const
898 rtl::OUStringBuffer
rBuffer( rFormula
);
899 GetFormula( rBuffer
, eGrammar
);
903 void ScFormulaCell::GetResultDimensions( SCSIZE
& rCols
, SCSIZE
& rRows
)
905 if (IsDirtyOrInTableOpDirty() && pDocument
->GetAutoCalc())
908 const ScMatrix
* pMat
= NULL
;
909 if (!pCode
->GetCodeError() && aResult
.GetType() == svMatrixCell
&&
910 ((pMat
= static_cast<const ScToken
*>(aResult
.GetToken().get())->GetMatrix()) != 0))
911 pMat
->GetDimensions( rCols
, rRows
);
919 void ScFormulaCell::Compile( const String
& rFormula
, BOOL bNoListening
,
920 const FormulaGrammar::Grammar eGrammar
)
922 if ( pDocument
->IsClipOrUndo() ) return;
923 BOOL bWasInFormulaTree
= pDocument
->IsInFormulaTree( this );
924 if ( bWasInFormulaTree
)
925 pDocument
->RemoveFromFormulaTree( this );
926 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
929 ScTokenArray
* pCodeOld
= pCode
;
930 ScCompiler
aComp( pDocument
, aPos
);
931 aComp
.SetGrammar(eGrammar
);
932 pCode
= aComp
.CompileString( rFormula
);
935 if( !pCode
->GetCodeError() )
937 if ( !pCode
->GetLen() && aResult
.GetHybridFormula().Len() && rFormula
== aResult
.GetHybridFormula() )
938 { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray
939 if ( rFormula
.GetChar(0) == '=' )
940 pCode
->AddBad( rFormula
.GetBuffer() + 1 );
942 pCode
->AddBad( rFormula
.GetBuffer() );
945 CompileTokenArray( bNoListening
);
950 SetTextWidth( TEXTWIDTH_DIRTY
);
951 SetScriptType( SC_SCRIPTTYPE_UNKNOWN
);
953 if ( bWasInFormulaTree
)
954 pDocument
->PutInFormulaTree( this );
958 void ScFormulaCell::CompileTokenArray( BOOL bNoListening
)
960 // Not already compiled?
961 if( !pCode
->GetLen() && aResult
.GetHybridFormula().Len() )
962 Compile( aResult
.GetHybridFormula(), bNoListening
, eTempGrammar
);
963 else if( bCompile
&& !pDocument
->IsClipOrUndo() && !pCode
->GetCodeError() )
965 // RPN length may get changed
966 BOOL bWasInFormulaTree
= pDocument
->IsInFormulaTree( this );
967 if ( bWasInFormulaTree
)
968 pDocument
->RemoveFromFormulaTree( this );
970 // Loading from within filter? No listening yet!
971 if( pDocument
->IsInsertingFromOtherDoc() )
974 if( !bNoListening
&& pCode
->GetCodeLen() )
975 EndListeningTo( pDocument
);
976 ScCompiler
aComp(pDocument
, aPos
, *pCode
);
977 aComp
.SetGrammar(pDocument
->GetGrammar());
978 bSubTotal
= aComp
.CompileTokenArray();
979 if( !pCode
->GetCodeError() )
981 nFormatType
= aComp
.GetNumFormatType();
984 aResult
.SetToken( NULL
);
987 StartListeningTo( pDocument
);
989 if ( bWasInFormulaTree
)
990 pDocument
->PutInFormulaTree( this );
995 void ScFormulaCell::CompileXML( ScProgress
& rProgress
)
997 if ( cMatrixFlag
== MM_REFERENCE
)
998 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
999 // just establish listeners
1000 StartListeningTo( pDocument
);
1004 ScCompiler
aComp( pDocument
, aPos
, *pCode
);
1005 aComp
.SetGrammar(eTempGrammar
);
1006 String aFormula
, aFormulaNmsp
;
1007 aComp
.CreateStringFromXMLTokenArray( aFormula
, aFormulaNmsp
);
1008 pDocument
->DecXMLImportedFormulaCount( aFormula
.Len() );
1009 rProgress
.SetStateCountDownOnPercent( pDocument
->GetXMLImportedFormulaCount() );
1010 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
1013 ScTokenArray
* pCodeOld
= pCode
;
1014 pCode
= aComp
.CompileString( aFormula
, aFormulaNmsp
);
1016 if( !pCode
->GetCodeError() )
1018 if ( !pCode
->GetLen() )
1020 if ( aFormula
.GetChar(0) == '=' )
1021 pCode
->AddBad( aFormula
.GetBuffer() + 1 );
1023 pCode
->AddBad( aFormula
.GetBuffer() );
1025 bSubTotal
= aComp
.CompileTokenArray();
1026 if( !pCode
->GetCodeError() )
1028 nFormatType
= aComp
.GetNumFormatType();
1032 StartListeningTo( pDocument
);
1038 SetTextWidth( TEXTWIDTH_DIRTY
);
1039 SetScriptType( SC_SCRIPTTYPE_UNKNOWN
);
1042 // Same as in Load: after loading, it must be known if ocMacro is in any formula
1043 // (for macro warning, CompileXML is called at the end of loading XML file)
1044 if ( !pDocument
->GetHasMacroFunc() && pCode
->HasOpCodeRPN( ocMacro
) )
1045 pDocument
->SetHasMacroFunc( TRUE
);
1049 void ScFormulaCell::CalcAfterLoad()
1051 BOOL bNewCompiled
= FALSE
;
1052 // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis,
1053 // aber kein TokenArray
1054 if( !pCode
->GetLen() && aResult
.GetHybridFormula().Len() )
1056 Compile( aResult
.GetHybridFormula(), TRUE
, eTempGrammar
);
1057 aResult
.SetToken( NULL
);
1059 bNewCompiled
= TRUE
;
1061 // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen
1062 // wurde, da die RangeNames erst jetzt existieren.
1063 if( pCode
->GetLen() && !pCode
->GetCodeLen() && !pCode
->GetCodeError() )
1065 ScCompiler
aComp(pDocument
, aPos
, *pCode
);
1066 aComp
.SetGrammar(pDocument
->GetGrammar());
1067 bSubTotal
= aComp
.CompileTokenArray();
1068 nFormatType
= aComp
.GetNumFormatType();
1072 bNewCompiled
= TRUE
;
1074 // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503
1075 // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib
1076 // bei einem fabs(-NAN) abstuerzt (#32739#)
1077 // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht
1078 if ( aResult
.IsValue() && !::rtl::math::isFinite( aResult
.GetDouble() ) )
1080 DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?");
1081 aResult
.SetResultError( errIllegalFPOperation
);
1084 // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix,
1085 // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge
1086 if ( pDocument
->GetSrcVersion() < SC_MATRIX_DOUBLEREF
&&
1087 GetMatrixFlag() == MM_NONE
&& pCode
->HasMatrixDoubleRefOps() )
1089 cMatrixFlag
= MM_FORMULA
;
1090 SetMatColsRows( 1, 1);
1092 // Muss die Zelle berechnet werden?
1093 // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann
1094 // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL
1095 if( !bNewCompiled
|| !pCode
->GetCodeError() )
1097 StartListeningTo( pDocument
);
1098 if( !pCode
->IsRecalcModeNormal() )
1101 if ( pCode
->IsRecalcModeAlways() )
1102 { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie
1103 // auch bei jedem F9 berechnet werden.
1106 // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in
1107 // SetDirtyAfterLoad.
1111 bool ScFormulaCell::MarkUsedExternalReferences()
1113 return pCode
&& pDocument
->MarkUsedExternalReferences( *pCode
);
1118 #define erDEBUGDOT 0
1119 // If set to 1, write output that's suitable for graphviz tools like dot.
1120 // Only node1 -> node2 entries are written, you'll have to manually surround
1121 // the file content with [strict] digraph name { ... }
1122 // The ``strict'' keyword might be necessary in case of multiple identical
1123 // paths like they occur in iterations, otherwise dot may consume too much
1124 // memory when generating the layout, or you'll get unreadable output. On the
1125 // other hand, information about recurring calculation is lost then.
1126 // Generates output only if variable nDebug is set in debugger, see below.
1127 // FIXME: currently doesn't cope with iterations and recursions. Code fragments
1128 // are a leftover from a previous debug session, meant as a pointer.
1132 using ::std::fprintf
;
1134 static const char aDebugDotFile
[] = "ttt_debug.dot";
1137 void ScFormulaCell::Interpret()
1141 static int nDebug
= 0;
1142 static const int erDEBUGDOTRUN
= 3;
1143 static FILE* pDebugFile
= 0;
1144 static sal_Int32 nDebugRootCount
= 0;
1145 static unsigned int nDebugPathCount
= 0;
1146 static ScAddress
aDebugLastPos( ScAddress::INITIALIZE_INVALID
);
1147 static ScAddress
aDebugThisPos( ScAddress::INITIALIZE_INVALID
);
1148 typedef ::std::vector
< ByteString
> DebugVector
;
1149 static DebugVector aDebugVec
;
1153 static void push( ScFormulaCell
* pCell
)
1155 aDebugThisPos
= pCell
->aPos
;
1156 if (aDebugVec
.empty())
1158 ByteString
aR( "root_");
1159 aR
+= ByteString::CreateFromInt32( ++nDebugRootCount
);
1160 aDebugVec
.push_back( aR
);
1163 pCell
->aPos
.Format( aStr
, SCA_VALID
| SCA_TAB_3D
, pCell
->GetDocument(),
1164 pCell
->GetDocument()->GetAddressConvention() );
1165 ByteString
aB( aStr
, RTL_TEXTENCODING_UTF8
);
1166 aDebugVec
.push_back( aB
);
1170 aDebugLastPos
= aDebugThisPos
;
1171 if (!aDebugVec
.empty())
1173 aDebugVec
.pop_back();
1174 if (aDebugVec
.size() == 1)
1176 aDebugVec
.pop_back();
1177 aDebugLastPos
= ScAddress( ScAddress::INITIALIZE_INVALID
);
1181 DebugElement( ScFormulaCell
* p
) { push(p
); }
1182 ~DebugElement() { pop(); }
1187 static void out( const char* pColor
)
1189 if (nDebug
!= erDEBUGDOTRUN
)
1191 char pColorString
[256];
1192 sprintf( pColorString
, (*pColor
?
1193 ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor
,
1195 size_t n
= aDebugVec
.size();
1196 fprintf( pDebugFile
,
1197 "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n",
1198 aDebugVec
[n
-2].GetBuffer(), aDebugVec
[n
-1].GetBuffer(),
1199 ++nDebugPathCount
, pColorString
, n
-1);
1200 fflush( pDebugFile
);
1203 #define erDEBUGDOT_OUT( p ) (DebugDot::out(p))
1204 #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p))
1205 #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop())
1207 #define erDEBUGDOT_OUT( p )
1208 #define erDEBUGDOT_ELEMENT_PUSH( p )
1209 #define erDEBUGDOT_ELEMENT_POP()
1212 if (!IsDirtyOrInTableOpDirty() || pDocument
->GetRecursionHelper().IsInReturn())
1213 return; // no double/triple processing
1216 // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen
1217 // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!!
1219 if ( pDocument
->IsInDdeLinkUpdate() )
1223 // set nDebug=1 in debugger to init things
1227 pDebugFile
= fopen( aDebugDotFile
, "a");
1231 nDebug
= erDEBUGDOTRUN
;
1233 // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output
1234 DebugElement
aDebugElem( this);
1235 // set nDebug=5 in debugger to close output
1239 fclose( pDebugFile
);
1248 if (!pDocument
->GetRecursionHelper().IsDoingIteration() ||
1249 aDebugThisPos
!= aDebugLastPos
)
1250 erDEBUGDOT_OUT(aDebugThisPos
== aDebugLastPos
? "orange" :
1251 (pDocument
->GetRecursionHelper().GetIteration() ? "blue" :
1255 if (!pDocument
->GetDocOptions().IsIter())
1257 aResult
.SetResultError( errCircularReference
);
1261 if (aResult
.GetResultError() == errCircularReference
)
1262 aResult
.SetResultError( 0 );
1264 // Start or add to iteration list.
1265 if (!pDocument
->GetRecursionHelper().IsDoingIteration() ||
1266 !pDocument
->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell
)
1267 pDocument
->GetRecursionHelper().SetInIterationReturn( true);
1271 // #63038# no multiple interprets for GetErrCode, IsValue, GetValue and
1272 // different entry point recursions. Would also lead to premature
1273 // convergence in iterations.
1274 if (pDocument
->GetRecursionHelper().GetIteration() && nSeenInIteration
==
1275 pDocument
->GetRecursionHelper().GetIteration())
1278 erDEBUGDOT_OUT( pDocument
->GetRecursionHelper().GetIteration() ? "magenta" : "");
1280 ScRecursionHelper
& rRecursionHelper
= pDocument
->GetRecursionHelper();
1281 BOOL bOldRunning
= bRunning
;
1282 if (rRecursionHelper
.GetRecursionCount() > MAXRECURSION
)
1285 rRecursionHelper
.SetInRecursionReturn( true);
1289 InterpretTail( SCITP_NORMAL
);
1292 // While leaving a recursion or iteration stack, insert its cells to the
1293 // recursion list in reverse order.
1294 if (rRecursionHelper
.IsInReturn())
1296 if (rRecursionHelper
.GetRecursionCount() > 0 ||
1297 !rRecursionHelper
.IsDoingRecursion())
1298 rRecursionHelper
.Insert( this, bOldRunning
, aResult
);
1299 bool bIterationFromRecursion
= false;
1300 bool bResumeIteration
= false;
1303 if ((rRecursionHelper
.IsInIterationReturn() &&
1304 rRecursionHelper
.GetRecursionCount() == 0 &&
1305 !rRecursionHelper
.IsDoingIteration()) ||
1306 bIterationFromRecursion
|| bResumeIteration
)
1308 ScFormulaCell
* pIterCell
= this; // scope for debug convenience
1309 bool & rDone
= rRecursionHelper
.GetConvergingReference();
1311 if (!bIterationFromRecursion
&& bResumeIteration
)
1313 bResumeIteration
= false;
1314 // Resuming iteration expands the range.
1315 ScFormulaRecursionList::const_iterator
aOldStart(
1316 rRecursionHelper
.GetLastIterationStart());
1317 rRecursionHelper
.ResumeIteration();
1318 // Mark new cells being in iteration.
1319 for (ScFormulaRecursionList::const_iterator
aIter(
1320 rRecursionHelper
.GetIterationStart()); aIter
!=
1323 pIterCell
= (*aIter
).pCell
;
1324 pIterCell
->bIsIterCell
= TRUE
;
1326 // Mark older cells dirty again, in case they converted
1327 // without accounting for all remaining cells in the circle
1328 // that weren't touched so far, e.g. conditional. Restore
1330 USHORT nIteration
= rRecursionHelper
.GetIteration();
1331 for (ScFormulaRecursionList::const_iterator
aIter(
1332 aOldStart
); aIter
!=
1333 rRecursionHelper
.GetIterationEnd(); ++aIter
)
1335 pIterCell
= (*aIter
).pCell
;
1336 if (pIterCell
->nSeenInIteration
== nIteration
)
1338 if (!pIterCell
->bDirty
|| aIter
== aOldStart
)
1340 pIterCell
->aResult
= (*aIter
).aPreviousResult
;
1342 --pIterCell
->nSeenInIteration
;
1344 pIterCell
->bDirty
= TRUE
;
1349 bResumeIteration
= false;
1350 // Close circle once.
1351 rRecursionHelper
.GetList().back().pCell
->InterpretTail(
1352 SCITP_CLOSE_ITERATION_CIRCLE
);
1353 // Start at 1, init things.
1354 rRecursionHelper
.StartIteration();
1355 // Mark all cells being in iteration.
1356 for (ScFormulaRecursionList::const_iterator
aIter(
1357 rRecursionHelper
.GetIterationStart()); aIter
!=
1358 rRecursionHelper
.GetIterationEnd(); ++aIter
)
1360 pIterCell
= (*aIter
).pCell
;
1361 pIterCell
->bIsIterCell
= TRUE
;
1364 bIterationFromRecursion
= false;
1365 USHORT nIterMax
= pDocument
->GetDocOptions().GetIterCount();
1366 for ( ; rRecursionHelper
.GetIteration() <= nIterMax
&& !rDone
;
1367 rRecursionHelper
.IncIteration())
1370 for ( ScFormulaRecursionList::iterator
aIter(
1371 rRecursionHelper
.GetIterationStart()); aIter
!=
1372 rRecursionHelper
.GetIterationEnd() &&
1373 !rRecursionHelper
.IsInReturn(); ++aIter
)
1375 pIterCell
= (*aIter
).pCell
;
1376 if (pIterCell
->IsDirtyOrInTableOpDirty() &&
1377 rRecursionHelper
.GetIteration() !=
1378 pIterCell
->GetSeenInIteration())
1380 (*aIter
).aPreviousResult
= pIterCell
->aResult
;
1381 pIterCell
->InterpretTail( SCITP_FROM_ITERATION
);
1383 rDone
= rDone
&& !pIterCell
->IsDirtyOrInTableOpDirty();
1385 if (rRecursionHelper
.IsInReturn())
1387 bResumeIteration
= true;
1389 // Don't increment iteration.
1392 if (!bResumeIteration
)
1396 for (ScFormulaRecursionList::const_iterator
aIter(
1397 rRecursionHelper
.GetIterationStart());
1398 aIter
!= rRecursionHelper
.GetIterationEnd();
1401 pIterCell
= (*aIter
).pCell
;
1402 pIterCell
->bIsIterCell
= FALSE
;
1403 pIterCell
->nSeenInIteration
= 0;
1404 pIterCell
->bRunning
= (*aIter
).bOldRunning
;
1409 for (ScFormulaRecursionList::const_iterator
aIter(
1410 rRecursionHelper
.GetIterationStart());
1411 aIter
!= rRecursionHelper
.GetIterationEnd();
1414 pIterCell
= (*aIter
).pCell
;
1415 pIterCell
->bIsIterCell
= FALSE
;
1416 pIterCell
->nSeenInIteration
= 0;
1417 pIterCell
->bRunning
= (*aIter
).bOldRunning
;
1418 // If one cell didn't converge, all cells of this
1419 // circular dependency don't, no matter whether
1420 // single cells did.
1421 pIterCell
->bDirty
= FALSE
;
1422 pIterCell
->bTableOpDirty
= FALSE
;
1423 pIterCell
->aResult
.SetResultError( errNoConvergence
);
1424 pIterCell
->bChanged
= TRUE
;
1425 pIterCell
->SetTextWidth( TEXTWIDTH_DIRTY
);
1426 pIterCell
->SetScriptType( SC_SCRIPTTYPE_UNKNOWN
);
1429 // End this iteration and remove entries.
1430 rRecursionHelper
.EndIteration();
1431 bResumeIteration
= rRecursionHelper
.IsDoingIteration();
1434 if (rRecursionHelper
.IsInRecursionReturn() &&
1435 rRecursionHelper
.GetRecursionCount() == 0 &&
1436 !rRecursionHelper
.IsDoingRecursion())
1438 bIterationFromRecursion
= false;
1439 // Iterate over cells known so far, start with the last cell
1440 // encountered, inserting new cells if another recursion limit
1441 // is reached. Repeat until solved.
1442 rRecursionHelper
.SetDoingRecursion( true);
1445 rRecursionHelper
.SetInRecursionReturn( false);
1446 for (ScFormulaRecursionList::const_iterator
aIter(
1447 rRecursionHelper
.GetStart());
1448 !rRecursionHelper
.IsInReturn() && aIter
!=
1449 rRecursionHelper
.GetEnd(); ++aIter
)
1451 ScFormulaCell
* pCell
= (*aIter
).pCell
;
1452 if (pCell
->IsDirtyOrInTableOpDirty())
1454 pCell
->InterpretTail( SCITP_NORMAL
);
1455 if (!pCell
->IsDirtyOrInTableOpDirty() && !pCell
->IsIterCell())
1456 pCell
->bRunning
= (*aIter
).bOldRunning
;
1459 } while (rRecursionHelper
.IsInRecursionReturn());
1460 rRecursionHelper
.SetDoingRecursion( false);
1461 if (rRecursionHelper
.IsInIterationReturn())
1463 if (!bResumeIteration
)
1464 bIterationFromRecursion
= true;
1466 else if (bResumeIteration
||
1467 rRecursionHelper
.IsDoingIteration())
1468 rRecursionHelper
.GetList().erase(
1469 rRecursionHelper
.GetStart(),
1470 rRecursionHelper
.GetLastIterationStart());
1472 rRecursionHelper
.Clear();
1474 } while (bIterationFromRecursion
|| bResumeIteration
);
1477 // Fire worksheet calculate event
1478 pDocument
->FireCalculateEvent( aPos
.Tab() );
1481 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam
)
1483 class RecursionCounter
1485 ScRecursionHelper
& rRec
;
1486 bool bStackedInIteration
;
1488 RecursionCounter( ScRecursionHelper
& r
, ScFormulaCell
* p
) : rRec(r
)
1490 bStackedInIteration
= rRec
.IsDoingIteration();
1491 if (bStackedInIteration
)
1492 rRec
.GetRecursionInIterationStack().push( p
);
1493 rRec
.IncRecursionCount();
1497 rRec
.DecRecursionCount();
1498 if (bStackedInIteration
)
1499 rRec
.GetRecursionInIterationStack().pop();
1501 } aRecursionCounter( pDocument
->GetRecursionHelper(), this);
1502 nSeenInIteration
= pDocument
->GetRecursionHelper().GetIteration();
1503 if( !pCode
->GetCodeLen() && !pCode
->GetCodeError() )
1505 // #i11719# no UPN and no error and no token code but result string present
1506 // => interpretation of this cell during name-compilation and unknown names
1507 // => can't exchange underlying code array in CompileTokenArray() /
1508 // Compile() because interpreter's token iterator would crash.
1509 // This should only be a temporary condition and, since we set an
1510 // error, if ran into it again we'd bump into the dirty-clearing
1511 // condition further down.
1512 if ( !pCode
->GetLen() && aResult
.GetHybridFormula().Len() )
1514 pCode
->SetCodeError( errNoCode
);
1515 // This is worth an assertion; if encountered in daily work
1516 // documents we might need another solution. Or just confirm correctness.
1517 DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but string" );
1520 CompileTokenArray();
1523 if( pCode
->GetCodeLen() && pDocument
)
1528 ScInterpreter
* pInt
;
1530 StackCleaner( ScDocument
* pD
, ScInterpreter
* pI
)
1531 : pDoc(pD
), pInt(pI
)
1536 pDoc
->DecInterpretLevel();
1539 pDocument
->IncInterpretLevel();
1540 ScInterpreter
* p
= new ScInterpreter( this, pDocument
, aPos
, *pCode
);
1541 StackCleaner
aStackCleaner( pDocument
, p
);
1542 USHORT nOldErrCode
= aResult
.GetResultError();
1543 if ( nSeenInIteration
== 0 )
1544 { // Only the first time
1545 // With bChanged=FALSE, if a newly compiled cell has a result of
1546 // 0.0, no change is detected and the cell will not be repainted.
1547 // bChanged = FALSE;
1548 aResult
.SetResultError( 0 );
1551 switch ( aResult
.GetResultError() )
1553 case errCircularReference
: // will be determined again if so
1554 aResult
.SetResultError( 0 );
1558 BOOL bOldRunning
= bRunning
;
1561 if (pDocument
->GetRecursionHelper().IsInReturn() && eTailParam
!= SCITP_CLOSE_ITERATION_CIRCLE
)
1563 if (nSeenInIteration
> 0)
1564 --nSeenInIteration
; // retry when iteration is resumed
1567 bRunning
= bOldRunning
;
1569 // #i102616# For single-sheet saving consider only content changes, not format type,
1570 // because format type isn't set on loading (might be changed later)
1571 BOOL bContentChanged
= FALSE
;
1573 // Do not create a HyperLink() cell if the formula results in an error.
1574 if( p
->GetError() && pCode
->IsHyperLink())
1575 pCode
->SetHyperLink(FALSE
);
1577 if( p
->GetError() && p
->GetError() != errCircularReference
)
1580 bTableOpDirty
= FALSE
;
1583 if (eTailParam
== SCITP_FROM_ITERATION
&& IsDirtyOrInTableOpDirty())
1585 bool bIsValue
= aResult
.IsValue(); // the previous type
1587 if ((bIsValue
&& p
->GetResultType() == svDouble
&& fabs(
1588 p
->GetNumResult() - aResult
.GetDouble()) <=
1589 pDocument
->GetDocOptions().GetIterEps()) ||
1590 (!bIsValue
&& p
->GetResultType() == svString
&&
1591 p
->GetStringResult() == aResult
.GetString()))
1593 // A convergence in the first iteration doesn't necessarily
1594 // mean that it's done, it may be because not all related cells
1595 // of a circle changed their values yet. If the set really
1596 // converges it will do so also during the next iteration. This
1597 // fixes situations like of #i44115#. If this wasn't wanted an
1598 // initial "uncalculated" value would be needed for all cells
1599 // of a circular dependency => graph needed before calculation.
1600 if (nSeenInIteration
> 1 ||
1601 pDocument
->GetDocOptions().GetIterCount() == 1)
1604 bTableOpDirty
= FALSE
;
1610 if( p
->GetError() != nOldErrCode
)
1613 // bContentChanged only has to be set if the file content would be changed
1614 if ( aResult
.GetCellResultType() != svUnknown
)
1615 bContentChanged
= TRUE
;
1617 // Different number format?
1618 if( nFormatType
!= p
->GetRetFormatType() )
1620 nFormatType
= p
->GetRetFormatType();
1623 if( nFormatIndex
!= p
->GetRetFormatIndex() )
1625 nFormatIndex
= p
->GetRetFormatIndex();
1629 // In case of changes just obtain the result, no temporary and
1630 // comparison needed anymore.
1633 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
1634 // Also handle special cases of initial results after loading.
1636 if ( !bContentChanged
&& pDocument
->IsStreamValid(aPos
.Tab()) )
1638 ScFormulaResult
aNewResult( p
->GetResultToken());
1639 StackVar eOld
= aResult
.GetCellResultType();
1640 StackVar eNew
= aNewResult
.GetCellResultType();
1641 if ( eOld
== svUnknown
&& ( eNew
== svError
|| ( eNew
== svDouble
&& aNewResult
.GetDouble() == 0.0 ) ) )
1643 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
1648 if ( eOld
== svHybridCell
) // string result from SetFormulaResultString?
1649 eOld
= svString
; // ScHybridCellToken has a valid GetString method
1651 bContentChanged
= (eOld
!= eNew
||
1652 (eNew
== svDouble
&& aResult
.GetDouble() != aNewResult
.GetDouble()) ||
1653 (eNew
== svString
&& aResult
.GetString() != aNewResult
.GetString()));
1657 aResult
.SetToken( p
->GetResultToken() );
1661 ScFormulaResult
aNewResult( p
->GetResultToken());
1662 StackVar eOld
= aResult
.GetCellResultType();
1663 StackVar eNew
= aNewResult
.GetCellResultType();
1664 bChanged
= (eOld
!= eNew
||
1665 (eNew
== svDouble
&& aResult
.GetDouble() != aNewResult
.GetDouble()) ||
1666 (eNew
== svString
&& aResult
.GetString() != aNewResult
.GetString()));
1668 // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged)
1669 if ( bChanged
&& !bContentChanged
&& pDocument
->IsStreamValid(aPos
.Tab()) )
1671 if ( ( eOld
== svUnknown
&& ( eNew
== svError
|| ( eNew
== svDouble
&& aNewResult
.GetDouble() == 0.0 ) ) ) ||
1672 ( eOld
== svHybridCell
&& eNew
== svString
&& aResult
.GetString() == aNewResult
.GetString() ) )
1674 // no change, see above
1677 bContentChanged
= TRUE
;
1680 aResult
.Assign( aNewResult
);
1683 // Precision as shown?
1684 if ( aResult
.IsValue() && !p
->GetError()
1685 && pDocument
->GetDocOptions().IsCalcAsShown()
1686 && nFormatType
!= NUMBERFORMAT_DATE
1687 && nFormatType
!= NUMBERFORMAT_TIME
1688 && nFormatType
!= NUMBERFORMAT_DATETIME
)
1690 ULONG nFormat
= pDocument
->GetNumberFormat( aPos
);
1691 if ( nFormatIndex
&& (nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0 )
1692 nFormat
= nFormatIndex
;
1693 if ( (nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0 )
1694 nFormat
= ScGlobal::GetStandardFormat(
1695 *pDocument
->GetFormatTable(), nFormat
, nFormatType
);
1696 aResult
.SetDouble( pDocument
->RoundValueAsShown(
1697 aResult
.GetDouble(), nFormat
));
1699 if (eTailParam
== SCITP_NORMAL
)
1702 bTableOpDirty
= FALSE
;
1704 if( aResult
.GetMatrix().Is() )
1706 // If the formula wasn't entered as a matrix formula, live on with
1707 // the upper left corner and let reference counting delete the matrix.
1708 if( cMatrixFlag
!= MM_FORMULA
&& !pCode
->IsHyperLink() )
1709 aResult
.SetToken( aResult
.GetCellResultToken());
1711 if ( aResult
.IsValue() && !::rtl::math::isFinite( aResult
.GetDouble() ) )
1713 // Coded double error may occur via filter import.
1714 USHORT nErr
= GetDoubleErrorValue( aResult
.GetDouble());
1715 aResult
.SetResultError( nErr
);
1716 bChanged
= bContentChanged
= true;
1720 SetTextWidth( TEXTWIDTH_DIRTY
);
1721 SetScriptType( SC_SCRIPTTYPE_UNKNOWN
);
1723 if (bContentChanged
&& pDocument
->IsStreamValid(aPos
.Tab()))
1725 // pass bIgnoreLock=TRUE, because even if called from pending row height update,
1726 // a changed result must still reset the stream flag
1727 pDocument
->SetStreamValid(aPos
.Tab(), FALSE
, TRUE
);
1729 if ( !pCode
->IsRecalcModeAlways() )
1730 pDocument
->RemoveFromFormulaTree( this );
1732 // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten)
1734 if ( pCode
->IsRecalcModeForced() )
1736 ULONG nValidation
= ((const SfxUInt32Item
*) pDocument
->GetAttr(
1737 aPos
.Col(), aPos
.Row(), aPos
.Tab(), ATTR_VALIDDATA
))->GetValue();
1740 const ScValidationData
* pData
= pDocument
->GetValidationEntry( nValidation
);
1741 if ( pData
&& !pData
->IsDataValid( this, aPos
) )
1742 pData
->DoCalcError( this );
1746 // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren
1747 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
1748 pDocument
->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE
);
1750 switch (p
->GetVolatileType())
1752 case ScInterpreter::VOLATILE
:
1753 // Volatile via built-in volatile functions. No actions needed.
1755 case ScInterpreter::VOLATILE_MACRO
:
1756 // The formula contains a volatile macro.
1757 pCode
->SetRecalcModeAlways();
1758 pDocument
->PutInFormulaTree(this);
1759 StartListeningTo(pDocument
);
1761 case ScInterpreter::NOT_VOLATILE
:
1762 if (pCode
->IsRecalcModeAlways())
1764 // The formula was previously volatile, but no more.
1765 EndListeningTo(pDocument
);
1766 pCode
->SetRecalcModeNormal();
1770 // non-volatile formula. End listening to the area in case
1771 // it's listening due to macro module change.
1772 pDocument
->EndListeningArea(BCA_LISTEN_ALWAYS
, this);
1774 pDocument
->RemoveFromFormulaTree(this);
1782 // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen
1783 DBG_ASSERT( pCode
->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" );
1785 bTableOpDirty
= FALSE
;
1790 void ScFormulaCell::SetMatColsRows( SCCOL nCols
, SCROW nRows
)
1792 ScMatrixFormulaCellToken
* pMat
= aResult
.GetMatrixFormulaCellTokenNonConst();
1794 pMat
->SetMatColsRows( nCols
, nRows
);
1795 else if (nCols
|| nRows
)
1796 aResult
.SetToken( new ScMatrixFormulaCellToken( nCols
, nRows
));
1800 void ScFormulaCell::GetMatColsRows( SCCOL
& nCols
, SCROW
& nRows
) const
1802 const ScMatrixFormulaCellToken
* pMat
= aResult
.GetMatrixFormulaCellToken();
1804 pMat
->GetMatColsRows( nCols
, nRows
);
1813 ULONG
ScFormulaCell::GetStandardFormat( SvNumberFormatter
& rFormatter
, ULONG nFormat
) const
1815 if ( nFormatIndex
&& (nFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0 )
1816 return nFormatIndex
;
1817 //! not ScFormulaCell::IsValue(), that could reinterpret the formula again.
1818 if ( aResult
.IsValue() )
1819 return ScGlobal::GetStandardFormat( aResult
.GetDouble(), rFormatter
, nFormat
, nFormatType
);
1821 return ScGlobal::GetStandardFormat( rFormatter
, nFormat
, nFormatType
);
1825 void __EXPORT
ScFormulaCell::Notify( SvtBroadcaster
&, const SfxHint
& rHint
)
1827 if ( !pDocument
->IsInDtorClear() && !pDocument
->GetHardRecalcState() )
1829 const ScHint
* p
= PTR_CAST( ScHint
, &rHint
);
1830 ULONG nHint
= (p
? p
->GetId() : 0);
1831 if (nHint
& (SC_HINT_DATACHANGED
| SC_HINT_DYING
| SC_HINT_TABLEOPDIRTY
))
1833 BOOL bForceTrack
= FALSE
;
1834 if ( nHint
& SC_HINT_TABLEOPDIRTY
)
1836 bForceTrack
= !bTableOpDirty
;
1837 if ( !bTableOpDirty
)
1839 pDocument
->AddTableOpFormulaCell( this );
1840 bTableOpDirty
= TRUE
;
1845 bForceTrack
= !bDirty
;
1848 // #35962# Don't remove from FormulaTree to put in FormulaTrack to
1849 // put in FormulaTree again and again, only if necessary.
1850 // Any other means except RECALCMODE_ALWAYS by which a cell could
1851 // be in FormulaTree if it would notify other cells through
1852 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
1853 // #87866# Yes. The new TableOpDirty made it necessary to have a
1854 // forced mode where formulas may still be in FormulaTree from
1855 // TableOpDirty but have to notify dependents for normal dirty.
1856 if ( (bForceTrack
|| !pDocument
->IsInFormulaTree( this )
1857 || pCode
->IsRecalcModeAlways())
1858 && !pDocument
->IsInFormulaTrack( this ) )
1859 pDocument
->AppendToFormulaTrack( this );
1864 void ScFormulaCell::SetDirty()
1866 if ( !IsInChangeTrack() )
1868 if ( pDocument
->GetHardRecalcState() )
1872 // Mehrfach-FormulaTracking in Load und in CompileAll
1873 // nach CopyScenario und CopyBlockFromClip vermeiden.
1874 // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=FALSE
1875 // setzen, z.B. in CompileTokenArray
1876 if ( !bDirty
|| !pDocument
->IsInFormulaTree( this ) )
1879 pDocument
->AppendToFormulaTrack( this );
1880 pDocument
->TrackFormulas();
1884 if (pDocument
->IsStreamValid(aPos
.Tab()))
1885 pDocument
->SetStreamValid(aPos
.Tab(), FALSE
);
1889 void ScFormulaCell::SetDirtyVar()
1892 // mark the sheet of this cell to be calculated
1893 pDocument
->AddCalculateTable( aPos
.Tab() );
1896 void ScFormulaCell::SetDirtyAfterLoad()
1898 if ( bDirty
&& !pDocument
->GetHardRecalcState() )
1899 pDocument
->PutInFormulaTree( this );
1902 void ScFormulaCell::SetTableOpDirty()
1904 if ( !IsInChangeTrack() )
1906 if ( pDocument
->GetHardRecalcState() )
1907 bTableOpDirty
= TRUE
;
1910 if ( !bTableOpDirty
|| !pDocument
->IsInFormulaTree( this ) )
1912 if ( !bTableOpDirty
)
1914 pDocument
->AddTableOpFormulaCell( this );
1915 bTableOpDirty
= TRUE
;
1917 pDocument
->AppendToFormulaTrack( this );
1918 pDocument
->TrackFormulas( SC_HINT_TABLEOPDIRTY
);
1925 BOOL
ScFormulaCell::IsDirtyOrInTableOpDirty() const
1927 return bDirty
|| (bTableOpDirty
&& pDocument
->IsInInterpreterTableOp());
1931 void ScFormulaCell::SetErrCode( USHORT n
)
1933 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
1934 * used whether it is solely for transport of a simple result error and get
1935 * rid of that abuse. */
1936 pCode
->SetCodeError( n
);
1937 // Hard set errors are transported as result type value per convention,
1938 // e.g. via clipboard. ScFormulaResult::IsValue() and
1939 // ScFormulaResult::GetDouble() handle that.
1940 aResult
.SetResultError( n
);
1943 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits
)
1945 if ( (nBits
& RECALCMODE_EMASK
) != RECALCMODE_NORMAL
)
1947 if ( nBits
& RECALCMODE_ONLOAD_ONCE
)
1948 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
1949 nBits
= (nBits
& ~RECALCMODE_EMASK
) | RECALCMODE_NORMAL
;
1951 pCode
->AddRecalcMode( nBits
);
1954 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
1955 void ScFormulaCell::GetURLResult( String
& rURL
, String
& rCellText
)
1961 // Cell Text uses the Cell format while the URL uses
1962 // the default format for the type.
1963 ULONG nCellFormat
= pDocument
->GetNumberFormat( aPos
);
1964 SvNumberFormatter
* pFormatter
= pDocument
->GetFormatTable();
1966 if ( (nCellFormat
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0 )
1967 nCellFormat
= GetStandardFormat( *pFormatter
,nCellFormat
);
1969 ULONG nURLFormat
= ScGlobal::GetStandardFormat( *pFormatter
,nCellFormat
, NUMBERFORMAT_NUMBER
);
1973 double fValue
= GetValue();
1974 pFormatter
->GetOutputString( fValue
, nCellFormat
, rCellText
, &pColor
);
1978 GetString( aCellString
);
1979 pFormatter
->GetOutputString( aCellString
, nCellFormat
, rCellText
, &pColor
);
1981 ScConstMatrixRef
xMat( aResult
.GetMatrix());
1984 ScMatValType nMatValType
;
1985 // determine if the matrix result is a string or value.
1986 const ScMatrixValue
* pMatVal
= xMat
->Get(0, 1, nMatValType
);
1989 if (!ScMatrix::IsValueType( nMatValType
))
1990 rURL
= pMatVal
->GetString();
1992 pFormatter
->GetOutputString( pMatVal
->fVal
, nURLFormat
, rURL
, &pColor
);
1999 pFormatter
->GetOutputString( GetValue(), nURLFormat
, rURL
, &pColor
);
2001 pFormatter
->GetOutputString( aCellString
, nURLFormat
, rURL
, &pColor
);
2005 bool ScFormulaCell::IsMultilineResult()
2008 return aResult
.IsMultiline();
2012 EditTextObject
* ScFormulaCell::CreateURLObject()
2016 GetURLResult( aURL
, aCellText
);
2018 SvxURLField
aUrlField( aURL
, aCellText
, SVXURLFORMAT_APPDEFAULT
);
2019 EditEngine
& rEE
= pDocument
->GetEditEngine();
2020 rEE
.SetText( EMPTY_STRING
);
2021 rEE
.QuickInsertField( SvxFieldItem( aUrlField
, EE_FEATURE_FIELD
), ESelection( 0xFFFF, 0xFFFF ) );
2023 return rEE
.CreateTextObject();
2026 // ============================================================================
2028 ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell
* pCell
)
2030 pCode
= pCell
->GetCode();
2035 BOOL
lcl_ScDetectiveRefIter_SkipRef( ScToken
* p
)
2037 ScSingleRefData
& rRef1
= p
->GetSingleRef();
2038 if ( rRef1
.IsColDeleted() || rRef1
.IsRowDeleted() || rRef1
.IsTabDeleted()
2041 if ( p
->GetType() == svDoubleRef
|| p
->GetType() == svExternalDoubleRef
)
2043 ScSingleRefData
& rRef2
= p
->GetDoubleRef().Ref2
;
2044 if ( rRef2
.IsColDeleted() || rRef2
.IsRowDeleted() || rRef2
.IsTabDeleted()
2051 BOOL
ScDetectiveRefIter::GetNextRef( ScRange
& rRange
)
2054 ScToken
* p
= GetNextRefToken();
2057 SingleDoubleRefProvider
aProv( *p
);
2058 rRange
.aStart
.Set( aProv
.Ref1
.nCol
, aProv
.Ref1
.nRow
, aProv
.Ref1
.nTab
);
2059 rRange
.aEnd
.Set( aProv
.Ref2
.nCol
, aProv
.Ref2
.nRow
, aProv
.Ref2
.nTab
);
2066 ScToken
* ScDetectiveRefIter::GetNextRefToken()
2068 ScToken
* p
= static_cast<ScToken
*>(pCode
->GetNextReferenceRPN());
2070 p
->CalcAbsIfRel( aPos
);
2072 while ( p
&& lcl_ScDetectiveRefIter_SkipRef( p
) )
2074 p
= static_cast<ScToken
*>(pCode
->GetNextReferenceRPN());
2076 p
->CalcAbsIfRel( aPos
);
2081 // ============================================================================