update dev300-m57
[ooovba.git] / sc / source / core / data / cell.cxx
blob06c82deb10d992e9a80ce17c411b0b3daac36de5
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"
39 #include "attrib.hxx"
40 #include "cell.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"
49 #include "brdcst.hxx"
50 #include "ddelink.hxx"
51 #include "validat.hxx"
52 #include "progress.hxx"
53 #include "editutil.hxx"
54 #include "recursionhelper.hxx"
55 #include "postit.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 -----------------------------------------------------------
75 #ifdef USE_MEMPOOL
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 const USHORT nMemPoolAsianStringCell = (0x4000 - 64) / sizeof(ScAsianStringCell);
82 const USHORT nMemPoolAsianEditCell = (0x4000 - 64) / sizeof(ScAsianEditCell);
83 IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell, nMemPoolValueCell, nMemPoolValueCell )
84 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell, nMemPoolFormulaCell, nMemPoolFormulaCell )
85 IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell, nMemPoolStringCell, nMemPoolStringCell )
86 IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell, nMemPoolNoteCell, nMemPoolNoteCell )
87 IMPL_FIXEDMEMPOOL_NEWDEL( ScAsianStringCell, nMemPoolAsianStringCell, nMemPoolAsianStringCell )
88 IMPL_FIXEDMEMPOOL_NEWDEL( ScAsianEditCell, nMemPoolAsianEditCell, nMemPoolAsianEditCell )
89 #endif
91 // ============================================================================
93 ScBaseCell::ScBaseCell( CellType eNewType ) :
94 mpNote( 0 ),
95 mpBroadcaster( 0 ),
96 nTextWidth( TEXTWIDTH_DIRTY ),
97 eCellType( sal::static_int_cast<BYTE>(eNewType) ),
98 nScriptType( SC_SCRIPTTYPE_UNKNOWN )
102 ScBaseCell::ScBaseCell( const ScBaseCell& rCell ) :
103 mpNote( 0 ),
104 mpBroadcaster( 0 ),
105 nTextWidth( rCell.nTextWidth ),
106 eCellType( rCell.eCellType ),
107 nScriptType( SC_SCRIPTTYPE_UNKNOWN )
111 ScBaseCell::~ScBaseCell()
113 delete mpNote;
114 delete mpBroadcaster;
115 DBG_ASSERT( eCellType == CELLTYPE_DESTROYED, "BaseCell Destructor" );
118 namespace {
120 ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags )
122 switch( rSrcCell.GetCellType() )
124 case CELLTYPE_VALUE:
125 return new ScValueCell( static_cast< const ScValueCell& >( rSrcCell ) );
126 case CELLTYPE_STRING:
127 return new ScStringCell( static_cast< const ScStringCell& >( rSrcCell ) );
128 case CELLTYPE_EDIT:
129 return new ScEditCell( static_cast< const ScEditCell& >( rSrcCell ), rDestDoc );
130 case CELLTYPE_FORMULA:
131 return new ScFormulaCell( static_cast< const ScFormulaCell& >( rSrcCell ), rDestDoc, rDestPos, nCloneFlags );
132 case CELLTYPE_NOTE:
133 return new ScNoteCell;
134 default:;
136 DBG_ERROR( "lclCloneCell - unknown cell type" );
137 return 0;
140 } // namespace
142 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, int nCloneFlags ) const
144 // notes will not be cloned -> cell address only needed for formula cells
145 ScAddress aDestPos;
146 if( eCellType == CELLTYPE_FORMULA )
147 aDestPos = static_cast< const ScFormulaCell* >( this )->aPos;
148 return lclCloneCell( *this, rDestDoc, aDestPos, nCloneFlags );
151 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const
153 return lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags );
156 ScBaseCell* ScBaseCell::CloneWithNote( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const
158 ScBaseCell* pNewCell = lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags );
159 if( mpNote )
161 if( !pNewCell )
162 pNewCell = new ScNoteCell;
163 bool bCloneCaption = (nCloneFlags & SC_CLONECELL_NOCAPTION) == 0;
164 pNewCell->TakeNote( mpNote->Clone( rOwnPos, rDestDoc, rDestPos, bCloneCaption ) );
166 return pNewCell;
169 void ScBaseCell::Delete()
171 DeleteNote();
172 switch (eCellType)
174 case CELLTYPE_VALUE:
175 delete (ScValueCell*) this;
176 break;
177 case CELLTYPE_STRING:
178 delete (ScStringCell*) this;
179 break;
180 case CELLTYPE_EDIT:
181 delete (ScEditCell*) this;
182 break;
183 case CELLTYPE_FORMULA:
184 delete (ScFormulaCell*) this;
185 break;
186 case CELLTYPE_NOTE:
187 delete (ScNoteCell*) this;
188 break;
189 default:
190 DBG_ERROR("Unbekannter Zellentyp");
191 break;
195 bool ScBaseCell::IsBlank( bool bIgnoreNotes ) const
197 return (eCellType == CELLTYPE_NOTE) && (bIgnoreNotes || !mpNote);
200 void ScBaseCell::TakeNote( ScPostIt* pNote )
202 delete mpNote;
203 mpNote = pNote;
206 ScPostIt* ScBaseCell::ReleaseNote()
208 ScPostIt* pNote = mpNote;
209 mpNote = 0;
210 return pNote;
213 void ScBaseCell::DeleteNote()
215 DELETEZ( mpNote );
218 void ScBaseCell::TakeBroadcaster( SvtBroadcaster* pBroadcaster )
220 delete mpBroadcaster;
221 mpBroadcaster = pBroadcaster;
224 SvtBroadcaster* ScBaseCell::ReleaseBroadcaster()
226 SvtBroadcaster* pBroadcaster = mpBroadcaster;
227 mpBroadcaster = 0;
228 return pBroadcaster;
231 void ScBaseCell::DeleteBroadcaster()
233 DELETEZ( mpBroadcaster );
236 ScBaseCell* ScBaseCell::CreateTextCell( const String& rString, ScDocument* pDoc )
238 if ( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND )
239 return new ScEditCell( rString, pDoc );
240 else
241 return new ScStringCell( rString );
244 void ScBaseCell::StartListeningTo( ScDocument* pDoc )
246 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo()
247 && !pDoc->GetNoListening()
248 && !((ScFormulaCell*)this)->IsInChangeTrack()
251 pDoc->SetDetectiveDirty(TRUE); // es hat sich was geaendert...
253 ScFormulaCell* pFormCell = (ScFormulaCell*)this;
254 ScTokenArray* pArr = pFormCell->GetCode();
255 if( pArr->IsRecalcModeAlways() )
256 pDoc->StartListeningArea( BCA_LISTEN_ALWAYS, pFormCell );
257 else
259 pArr->Reset();
260 ScToken* t;
261 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
263 StackVar eType = t->GetType();
264 ScSingleRefData& rRef1 = t->GetSingleRef();
265 ScSingleRefData& rRef2 = (eType == svDoubleRef ?
266 t->GetDoubleRef().Ref2 : rRef1);
267 switch( eType )
269 case svSingleRef:
270 rRef1.CalcAbsIfRel( pFormCell->aPos );
271 if ( rRef1.Valid() )
273 pDoc->StartListeningCell(
274 ScAddress( rRef1.nCol,
275 rRef1.nRow,
276 rRef1.nTab ), pFormCell );
278 break;
279 case svDoubleRef:
280 t->CalcAbsIfRel( pFormCell->aPos );
281 if ( rRef1.Valid() && rRef2.Valid() )
283 if ( t->GetOpCode() == ocColRowNameAuto )
284 { // automagically
285 if ( rRef1.IsColRel() )
286 { // ColName
287 pDoc->StartListeningArea( ScRange (
289 rRef1.nRow,
290 rRef1.nTab,
291 MAXCOL,
292 rRef2.nRow,
293 rRef2.nTab ), pFormCell );
295 else
296 { // RowName
297 pDoc->StartListeningArea( ScRange (
298 rRef1.nCol,
300 rRef1.nTab,
301 rRef2.nCol,
302 MAXROW,
303 rRef2.nTab ), pFormCell );
306 else
308 pDoc->StartListeningArea( ScRange (
309 rRef1.nCol,
310 rRef1.nRow,
311 rRef1.nTab,
312 rRef2.nCol,
313 rRef2.nRow,
314 rRef2.nTab ), pFormCell );
317 break;
318 default:
319 ; // nothing
323 pFormCell->SetNeedsListening( FALSE);
327 // pArr gesetzt -> Referenzen von anderer Zelle nehmen
328 // dann muss auch aPos uebergeben werden!
330 void ScBaseCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
331 ScAddress aPos )
333 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo()
334 && !((ScFormulaCell*)this)->IsInChangeTrack()
337 pDoc->SetDetectiveDirty(TRUE); // es hat sich was geaendert...
339 ScFormulaCell* pFormCell = (ScFormulaCell*)this;
340 if( pFormCell->GetCode()->IsRecalcModeAlways() )
341 pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, pFormCell );
342 else
344 if (!pArr)
346 pArr = pFormCell->GetCode();
347 aPos = pFormCell->aPos;
349 pArr->Reset();
350 ScToken* t;
351 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
353 StackVar eType = t->GetType();
354 ScSingleRefData& rRef1 = t->GetSingleRef();
355 ScSingleRefData& rRef2 = (eType == svDoubleRef ?
356 t->GetDoubleRef().Ref2 : rRef1);
357 switch( eType )
359 case svSingleRef:
360 rRef1.CalcAbsIfRel( aPos );
361 if ( rRef1.Valid() )
363 pDoc->EndListeningCell(
364 ScAddress( rRef1.nCol,
365 rRef1.nRow,
366 rRef1.nTab ), pFormCell );
368 break;
369 case svDoubleRef:
370 t->CalcAbsIfRel( aPos );
371 if ( rRef1.Valid() && rRef2.Valid() )
373 if ( t->GetOpCode() == ocColRowNameAuto )
374 { // automagically
375 if ( rRef1.IsColRel() )
376 { // ColName
377 pDoc->EndListeningArea( ScRange (
379 rRef1.nRow,
380 rRef1.nTab,
381 MAXCOL,
382 rRef2.nRow,
383 rRef2.nTab ), pFormCell );
385 else
386 { // RowName
387 pDoc->EndListeningArea( ScRange (
388 rRef1.nCol,
390 rRef1.nTab,
391 rRef2.nCol,
392 MAXROW,
393 rRef2.nTab ), pFormCell );
396 else
398 pDoc->EndListeningArea( ScRange (
399 rRef1.nCol,
400 rRef1.nRow,
401 rRef1.nTab,
402 rRef2.nCol,
403 rRef2.nRow,
404 rRef2.nTab ), pFormCell );
407 break;
408 default:
409 ; // nothing
417 USHORT ScBaseCell::GetErrorCode() const
419 switch ( eCellType )
421 case CELLTYPE_FORMULA :
422 return ((ScFormulaCell*)this)->GetErrCode();
423 default:
424 return 0;
429 BOOL ScBaseCell::HasEmptyData() const
431 switch ( eCellType )
433 case CELLTYPE_NOTE :
434 return TRUE;
435 case CELLTYPE_FORMULA :
436 return ((ScFormulaCell*)this)->IsEmpty();
437 default:
438 return FALSE;
443 BOOL ScBaseCell::HasValueData() const
445 switch ( eCellType )
447 case CELLTYPE_VALUE :
448 return TRUE;
449 case CELLTYPE_FORMULA :
450 return ((ScFormulaCell*)this)->IsValue();
451 default:
452 return FALSE;
457 BOOL ScBaseCell::HasStringData() const
459 switch ( eCellType )
461 case CELLTYPE_STRING :
462 case CELLTYPE_EDIT :
463 return TRUE;
464 case CELLTYPE_FORMULA :
465 return !((ScFormulaCell*)this)->IsValue();
466 default:
467 return FALSE;
471 String ScBaseCell::GetStringData() const
473 String aStr;
474 switch ( eCellType )
476 case CELLTYPE_STRING:
477 ((const ScStringCell*)this)->GetString( aStr );
478 break;
479 case CELLTYPE_EDIT:
480 ((const ScEditCell*)this)->GetString( aStr );
481 break;
482 case CELLTYPE_FORMULA:
483 ((ScFormulaCell*)this)->GetString( aStr ); // an der Formelzelle nicht-const
484 break;
486 return aStr;
489 // static
490 BOOL ScBaseCell::CellEqual( const ScBaseCell* pCell1, const ScBaseCell* pCell2 )
492 CellType eType1 = CELLTYPE_NONE;
493 CellType eType2 = CELLTYPE_NONE;
494 if ( pCell1 )
496 eType1 = pCell1->GetCellType();
497 if (eType1 == CELLTYPE_EDIT)
498 eType1 = CELLTYPE_STRING;
499 else if (eType1 == CELLTYPE_NOTE)
500 eType1 = CELLTYPE_NONE;
502 if ( pCell2 )
504 eType2 = pCell2->GetCellType();
505 if (eType2 == CELLTYPE_EDIT)
506 eType2 = CELLTYPE_STRING;
507 else if (eType2 == CELLTYPE_NOTE)
508 eType2 = CELLTYPE_NONE;
510 if ( eType1 != eType2 )
511 return FALSE;
513 switch ( eType1 ) // beide Typen gleich
515 case CELLTYPE_NONE: // beide leer
516 return TRUE;
517 case CELLTYPE_VALUE: // wirklich Value-Zellen
518 return ( ((const ScValueCell*)pCell1)->GetValue() ==
519 ((const ScValueCell*)pCell2)->GetValue() );
520 case CELLTYPE_STRING: // String oder Edit
522 String aText1, aText2;
523 const ScStringCell* pStrCell1 = static_cast<const ScStringCell*>(pCell1);
524 const ScStringCell* pStrCell2 = static_cast<const ScStringCell*>(pCell2);
525 pStrCell1->GetString( aText1 );
526 pStrCell2->GetString( aText2 );
527 if (aText1 != aText2)
528 return false;
530 ScPhonetic aPhonetic1, aPhonetic2;
531 if (pStrCell1->HasPhonetic())
533 const ScAsianStringCell* pPhoCell1 =
534 static_cast<const ScAsianStringCell*>(pStrCell1);
535 pPhoCell1->GetPhonetic(aPhonetic1);
537 if (pStrCell2->HasPhonetic())
539 const ScAsianStringCell* pPhoCell2 =
540 static_cast<const ScAsianStringCell*>(pStrCell2);
541 pPhoCell2->GetPhonetic(aPhonetic2);
544 return aPhonetic1 == aPhonetic2;
546 case CELLTYPE_FORMULA:
548 //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!!
549 //! Vergleichsfunktion an der Formelzelle ???
550 //! Abfrage mit ScColumn::SwapRow zusammenfassen!
552 ScTokenArray* pCode1 = ((ScFormulaCell*)pCell1)->GetCode();
553 ScTokenArray* pCode2 = ((ScFormulaCell*)pCell2)->GetCode();
555 if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN
557 BOOL bEqual = TRUE;
558 USHORT nLen = pCode1->GetLen();
559 FormulaToken** ppToken1 = pCode1->GetArray();
560 FormulaToken** ppToken2 = pCode2->GetArray();
561 for (USHORT i=0; i<nLen; i++)
562 if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) )
564 bEqual = FALSE;
565 break;
568 if (bEqual)
569 return TRUE;
572 return FALSE; // unterschiedlich lang oder unterschiedliche Tokens
574 default:
575 DBG_ERROR("huch, was fuer Zellen???");
577 return FALSE;
580 // ============================================================================
582 ScNoteCell::ScNoteCell( SvtBroadcaster* pBC ) :
583 ScBaseCell( CELLTYPE_NOTE )
585 TakeBroadcaster( pBC );
588 ScNoteCell::ScNoteCell( ScPostIt* pNote, SvtBroadcaster* pBC ) :
589 ScBaseCell( CELLTYPE_NOTE )
591 TakeNote( pNote );
592 TakeBroadcaster( pBC );
595 #ifdef DBG_UTIL
596 ScNoteCell::~ScNoteCell()
598 eCellType = CELLTYPE_DESTROYED;
600 #endif
602 // ============================================================================
604 ScValueCell::ScValueCell() :
605 ScBaseCell( CELLTYPE_VALUE ),
606 mfValue( 0.0 )
610 ScValueCell::ScValueCell( double fValue ) :
611 ScBaseCell( CELLTYPE_VALUE ),
612 mfValue( fValue )
616 #ifdef DBG_UTIL
617 ScValueCell::~ScValueCell()
619 eCellType = CELLTYPE_DESTROYED;
621 #endif
623 // ============================================================================
625 ScStringCell::ScStringCell() :
626 ScBaseCell( CELLTYPE_STRING )
630 ScStringCell::ScStringCell( const String& rString ) :
631 ScBaseCell( CELLTYPE_STRING ),
632 maString( rString.intern() )
636 #ifdef DBG_UTIL
637 ScStringCell::~ScStringCell()
639 eCellType = CELLTYPE_DESTROYED;
641 #endif
643 // ============================================================================
646 // ScFormulaCell
649 ScFormulaCell::ScFormulaCell() :
650 ScBaseCell( CELLTYPE_FORMULA ),
651 eTempGrammar( FormulaGrammar::GRAM_DEFAULT),
652 pCode( NULL ),
653 pDocument( NULL ),
654 pPrevious(0),
655 pNext(0),
656 pPreviousTrack(0),
657 pNextTrack(0),
658 nFormatIndex(0),
659 nFormatType( NUMBERFORMAT_NUMBER ),
660 nSeenInIteration(0),
661 cMatrixFlag ( MM_NONE ),
662 bDirty( FALSE ),
663 bChanged( FALSE ),
664 bRunning( FALSE ),
665 bCompile( FALSE ),
666 bSubTotal( FALSE ),
667 bIsIterCell( FALSE ),
668 bInChangeTrack( FALSE ),
669 bTableOpDirty( FALSE ),
670 bNeedListening( FALSE ),
671 aPos(0,0,0)
675 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
676 const String& rFormula,
677 const FormulaGrammar::Grammar eGrammar,
678 BYTE cMatInd ) :
679 ScBaseCell( CELLTYPE_FORMULA ),
680 eTempGrammar( eGrammar),
681 pCode( NULL ),
682 pDocument( pDoc ),
683 pPrevious(0),
684 pNext(0),
685 pPreviousTrack(0),
686 pNextTrack(0),
687 nFormatIndex(0),
688 nFormatType( NUMBERFORMAT_NUMBER ),
689 nSeenInIteration(0),
690 cMatrixFlag ( cMatInd ),
691 bDirty( TRUE ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0
692 bChanged( FALSE ),
693 bRunning( FALSE ),
694 bCompile( FALSE ),
695 bSubTotal( FALSE ),
696 bIsIterCell( FALSE ),
697 bInChangeTrack( FALSE ),
698 bTableOpDirty( FALSE ),
699 bNeedListening( FALSE ),
700 aPos( rPos )
702 Compile( rFormula, TRUE, eGrammar ); // bNoListening, Insert does that
705 // Wird von den Importfiltern verwendet
707 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
708 const ScTokenArray* pArr,
709 const FormulaGrammar::Grammar eGrammar, BYTE cInd ) :
710 ScBaseCell( CELLTYPE_FORMULA ),
711 eTempGrammar( eGrammar),
712 pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ),
713 pDocument( pDoc ),
714 pPrevious(0),
715 pNext(0),
716 pPreviousTrack(0),
717 pNextTrack(0),
718 nFormatIndex(0),
719 nFormatType( NUMBERFORMAT_NUMBER ),
720 nSeenInIteration(0),
721 cMatrixFlag ( cInd ),
722 bDirty( NULL != pArr ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0
723 bChanged( FALSE ),
724 bRunning( FALSE ),
725 bCompile( FALSE ),
726 bSubTotal( FALSE ),
727 bIsIterCell( FALSE ),
728 bInChangeTrack( FALSE ),
729 bTableOpDirty( FALSE ),
730 bNeedListening( FALSE ),
731 aPos( rPos )
733 // UPN-Array erzeugen
734 if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
736 ScCompiler aComp( pDocument, aPos, *pCode);
737 aComp.SetGrammar(eTempGrammar);
738 bSubTotal = aComp.CompileTokenArray();
739 nFormatType = aComp.GetNumFormatType();
741 else
743 pCode->Reset();
744 if ( pCode->GetNextOpCodeRPN( ocSubTotal ) )
745 bSubTotal = TRUE;
749 ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
750 ScBaseCell( rCell ),
751 SvtListener(),
752 aResult( rCell.aResult ),
753 eTempGrammar( rCell.eTempGrammar),
754 pDocument( &rDoc ),
755 pPrevious(0),
756 pNext(0),
757 pPreviousTrack(0),
758 pNextTrack(0),
759 nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ),
760 nFormatType( rCell.nFormatType ),
761 nSeenInIteration(0),
762 cMatrixFlag ( rCell.cMatrixFlag ),
763 bDirty( rCell.bDirty ),
764 bChanged( rCell.bChanged ),
765 bRunning( FALSE ),
766 bCompile( rCell.bCompile ),
767 bSubTotal( rCell.bSubTotal ),
768 bIsIterCell( FALSE ),
769 bInChangeTrack( FALSE ),
770 bTableOpDirty( FALSE ),
771 bNeedListening( FALSE ),
772 aPos( rPos )
774 pCode = rCell.pCode->Clone();
776 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
777 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
779 // evtl. Fehler zuruecksetzen und neu kompilieren
780 // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben
781 // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten
782 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
784 pCode->SetCodeError( 0 );
785 bCompile = TRUE;
787 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
788 BOOL bCompileLater = FALSE;
789 BOOL bClipMode = rCell.pDocument->IsClipboard();
790 if( !bCompile )
791 { // Name references with references and ColRowNames
792 pCode->Reset();
793 ScToken* t;
794 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
796 if ( t->GetOpCode() == ocExternalRef )
798 // External name, cell, and area references.
799 bCompile = true;
801 else if ( t->GetType() == svIndex )
803 ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( t->GetIndex() );
804 if( pRangeData )
806 if( pRangeData->HasReferences() )
807 bCompile = TRUE;
809 else
810 bCompile = TRUE; // invalid reference!
812 else if ( t->GetOpCode() == ocColRowName )
814 bCompile = TRUE; // new lookup needed
815 bCompileLater = bClipMode;
819 if( bCompile )
821 if ( !bCompileLater && bClipMode )
823 // Merging ranges needs the actual positions after UpdateReference.
824 // ColRowNames need new lookup after positions are adjusted.
825 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName);
827 if ( !bCompileLater )
829 // bNoListening, not at all if in Clipboard/Undo,
830 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
831 CompileTokenArray( TRUE );
835 if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
836 StartListeningTo( &rDoc );
839 ScFormulaCell::~ScFormulaCell()
841 pDocument->RemoveFromFormulaTree( this );
842 if (pCode->HasOpCode(ocMacro))
843 pDocument->GetMacroManager()->RemoveDependentCell(this);
845 delete pCode;
846 #ifdef DBG_UTIL
847 eCellType = CELLTYPE_DESTROYED;
848 #endif
851 void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer,
852 const FormulaGrammar::Grammar eGrammar ) const
854 if( pCode->GetCodeError() && !pCode->GetLen() )
856 rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
857 return;
859 else if( cMatrixFlag == MM_REFERENCE )
861 // Reference to another cell that contains a matrix formula.
862 pCode->Reset();
863 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
864 if( p )
866 /* FIXME: original GetFormula() code obtained
867 * pCell only if (!this->IsInChangeTrack()),
868 * GetEnglishFormula() omitted that test.
869 * Can we live without in all cases? */
870 ScBaseCell* pCell;
871 ScSingleRefData& rRef = p->GetSingleRef();
872 rRef.CalcAbsIfRel( aPos );
873 if ( rRef.Valid() )
874 pCell = pDocument->GetCell( ScAddress( rRef.nCol,
875 rRef.nRow, rRef.nTab ) );
876 else
877 pCell = NULL;
878 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA)
880 ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar);
881 return;
883 else
885 ScCompiler aComp( pDocument, aPos, *pCode);
886 aComp.SetGrammar(eGrammar);
887 aComp.CreateStringFromTokenArray( rBuffer );
890 else
892 DBG_ERROR("ScFormulaCell::GetFormula: not a matrix");
895 else
897 ScCompiler aComp( pDocument, aPos, *pCode);
898 aComp.SetGrammar(eGrammar);
899 aComp.CreateStringFromTokenArray( rBuffer );
902 sal_Unicode ch('=');
903 rBuffer.insert( 0, &ch, 1 );
904 if( cMatrixFlag )
906 sal_Unicode ch2('{');
907 rBuffer.insert( 0, &ch2, 1);
908 rBuffer.append( sal_Unicode('}'));
912 void ScFormulaCell::GetFormula( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const
914 rtl::OUStringBuffer rBuffer( rFormula );
915 GetFormula( rBuffer, eGrammar );
916 rFormula = rBuffer;
919 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
921 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
922 Interpret();
924 const ScMatrix* pMat = NULL;
925 if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell &&
926 ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0))
927 pMat->GetDimensions( rCols, rRows );
928 else
930 rCols = 0;
931 rRows = 0;
935 void ScFormulaCell::Compile( const String& rFormula, BOOL bNoListening,
936 const FormulaGrammar::Grammar eGrammar )
938 if ( pDocument->IsClipOrUndo() ) return;
939 BOOL bWasInFormulaTree = pDocument->IsInFormulaTree( this );
940 if ( bWasInFormulaTree )
941 pDocument->RemoveFromFormulaTree( this );
942 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
943 if ( pCode )
944 pCode->Clear();
945 ScTokenArray* pCodeOld = pCode;
946 ScCompiler aComp( pDocument, aPos);
947 aComp.SetGrammar(eGrammar);
948 pCode = aComp.CompileString( rFormula );
949 if ( pCodeOld )
950 delete pCodeOld;
951 if( !pCode->GetCodeError() )
953 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() && rFormula == aResult.GetHybridFormula() )
954 { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray
955 if ( rFormula.GetChar(0) == '=' )
956 pCode->AddBad( rFormula.GetBuffer() + 1 );
957 else
958 pCode->AddBad( rFormula.GetBuffer() );
960 bCompile = TRUE;
961 CompileTokenArray( bNoListening );
963 else
965 bChanged = TRUE;
966 SetTextWidth( TEXTWIDTH_DIRTY );
967 SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
969 if ( bWasInFormulaTree )
970 pDocument->PutInFormulaTree( this );
974 void ScFormulaCell::CompileTokenArray( BOOL bNoListening )
976 // Not already compiled?
977 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
978 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
979 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
981 // RPN length may get changed
982 BOOL bWasInFormulaTree = pDocument->IsInFormulaTree( this );
983 if ( bWasInFormulaTree )
984 pDocument->RemoveFromFormulaTree( this );
986 // Loading from within filter? No listening yet!
987 if( pDocument->IsInsertingFromOtherDoc() )
988 bNoListening = TRUE;
990 if( !bNoListening && pCode->GetCodeLen() )
991 EndListeningTo( pDocument );
992 ScCompiler aComp(pDocument, aPos, *pCode);
993 aComp.SetGrammar(pDocument->GetGrammar());
994 bSubTotal = aComp.CompileTokenArray();
995 if( !pCode->GetCodeError() )
997 nFormatType = aComp.GetNumFormatType();
998 nFormatIndex = 0;
999 bChanged = TRUE;
1000 aResult.SetToken( NULL);
1001 bCompile = FALSE;
1002 if ( !bNoListening )
1003 StartListeningTo( pDocument );
1005 if ( bWasInFormulaTree )
1006 pDocument->PutInFormulaTree( this );
1011 void ScFormulaCell::CompileXML( ScProgress& rProgress )
1013 if ( cMatrixFlag == MM_REFERENCE )
1014 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1015 // just establish listeners
1016 StartListeningTo( pDocument );
1017 return ;
1020 ScCompiler aComp( pDocument, aPos, *pCode);
1021 aComp.SetGrammar(eTempGrammar);
1022 String aFormula, aFormulaNmsp;
1023 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1024 pDocument->DecXMLImportedFormulaCount( aFormula.Len() );
1025 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
1026 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
1027 if ( pCode )
1028 pCode->Clear();
1029 ScTokenArray* pCodeOld = pCode;
1030 pCode = aComp.CompileString( aFormula, aFormulaNmsp );
1031 delete pCodeOld;
1032 if( !pCode->GetCodeError() )
1034 if ( !pCode->GetLen() )
1036 if ( aFormula.GetChar(0) == '=' )
1037 pCode->AddBad( aFormula.GetBuffer() + 1 );
1038 else
1039 pCode->AddBad( aFormula.GetBuffer() );
1041 bSubTotal = aComp.CompileTokenArray();
1042 if( !pCode->GetCodeError() )
1044 nFormatType = aComp.GetNumFormatType();
1045 nFormatIndex = 0;
1046 bChanged = TRUE;
1047 bCompile = FALSE;
1048 StartListeningTo( pDocument );
1051 else
1053 bChanged = TRUE;
1054 SetTextWidth( TEXTWIDTH_DIRTY );
1055 SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
1058 // Same as in Load: after loading, it must be known if ocMacro is in any formula
1059 // (for macro warning, CompileXML is called at the end of loading XML file)
1060 if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
1061 pDocument->SetHasMacroFunc( TRUE );
1065 void ScFormulaCell::CalcAfterLoad()
1067 BOOL bNewCompiled = FALSE;
1068 // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis,
1069 // aber kein TokenArray
1070 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1072 Compile( aResult.GetHybridFormula(), TRUE, eTempGrammar);
1073 aResult.SetToken( NULL);
1074 bDirty = TRUE;
1075 bNewCompiled = TRUE;
1077 // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen
1078 // wurde, da die RangeNames erst jetzt existieren.
1079 if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
1081 ScCompiler aComp(pDocument, aPos, *pCode);
1082 aComp.SetGrammar(pDocument->GetGrammar());
1083 bSubTotal = aComp.CompileTokenArray();
1084 nFormatType = aComp.GetNumFormatType();
1085 nFormatIndex = 0;
1086 bDirty = TRUE;
1087 bCompile = FALSE;
1088 bNewCompiled = TRUE;
1090 // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503
1091 // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib
1092 // bei einem fabs(-NAN) abstuerzt (#32739#)
1093 // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht
1094 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1096 DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?");
1097 aResult.SetResultError( errIllegalFPOperation );
1098 bDirty = TRUE;
1100 // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix,
1101 // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge
1102 if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
1103 GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
1105 cMatrixFlag = MM_FORMULA;
1106 SetMatColsRows( 1, 1);
1108 // Muss die Zelle berechnet werden?
1109 // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann
1110 // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL
1111 if( !bNewCompiled || !pCode->GetCodeError() )
1113 StartListeningTo( pDocument );
1114 if( !pCode->IsRecalcModeNormal() )
1115 bDirty = TRUE;
1117 if ( pCode->IsRecalcModeAlways() )
1118 { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie
1119 // auch bei jedem F9 berechnet werden.
1120 bDirty = TRUE;
1122 // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in
1123 // SetDirtyAfterLoad.
1127 bool ScFormulaCell::MarkUsedExternalReferences()
1129 return pCode && pDocument->MarkUsedExternalReferences( *pCode);
1133 // FIXME: set to 0
1134 #define erDEBUGDOT 0
1135 // If set to 1, write output that's suitable for graphviz tools like dot.
1136 // Only node1 -> node2 entries are written, you'll have to manually surround
1137 // the file content with [strict] digraph name { ... }
1138 // The ``strict'' keyword might be necessary in case of multiple identical
1139 // paths like they occur in iterations, otherwise dot may consume too much
1140 // memory when generating the layout, or you'll get unreadable output. On the
1141 // other hand, information about recurring calculation is lost then.
1142 // Generates output only if variable nDebug is set in debugger, see below.
1143 // FIXME: currently doesn't cope with iterations and recursions. Code fragments
1144 // are a leftover from a previous debug session, meant as a pointer.
1145 #if erDEBUGDOT
1146 #include <cstdio>
1147 using ::std::fopen;
1148 using ::std::fprintf;
1149 #include <vector>
1150 static const char aDebugDotFile[] = "ttt_debug.dot";
1151 #endif
1153 void ScFormulaCell::Interpret()
1156 #if erDEBUGDOT
1157 static int nDebug = 0;
1158 static const int erDEBUGDOTRUN = 3;
1159 static FILE* pDebugFile = 0;
1160 static sal_Int32 nDebugRootCount = 0;
1161 static unsigned int nDebugPathCount = 0;
1162 static ScAddress aDebugLastPos( ScAddress::INITIALIZE_INVALID);
1163 static ScAddress aDebugThisPos( ScAddress::INITIALIZE_INVALID);
1164 typedef ::std::vector< ByteString > DebugVector;
1165 static DebugVector aDebugVec;
1166 class DebugElement
1168 public:
1169 static void push( ScFormulaCell* pCell )
1171 aDebugThisPos = pCell->aPos;
1172 if (aDebugVec.empty())
1174 ByteString aR( "root_");
1175 aR += ByteString::CreateFromInt32( ++nDebugRootCount);
1176 aDebugVec.push_back( aR);
1178 String aStr;
1179 pCell->aPos.Format( aStr, SCA_VALID | SCA_TAB_3D, pCell->GetDocument(),
1180 pCell->GetDocument()->GetAddressConvention() );
1181 ByteString aB( aStr, RTL_TEXTENCODING_UTF8);
1182 aDebugVec.push_back( aB);
1184 static void pop()
1186 aDebugLastPos = aDebugThisPos;
1187 if (!aDebugVec.empty())
1189 aDebugVec.pop_back();
1190 if (aDebugVec.size() == 1)
1192 aDebugVec.pop_back();
1193 aDebugLastPos = ScAddress( ScAddress::INITIALIZE_INVALID);
1197 DebugElement( ScFormulaCell* p ) { push(p); }
1198 ~DebugElement() { pop(); }
1200 class DebugDot
1202 public:
1203 static void out( const char* pColor )
1205 if (nDebug != erDEBUGDOTRUN)
1206 return;
1207 char pColorString[256];
1208 sprintf( pColorString, (*pColor ?
1209 ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor,
1210 pColor);
1211 size_t n = aDebugVec.size();
1212 fprintf( pDebugFile,
1213 "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n",
1214 aDebugVec[n-2].GetBuffer(), aDebugVec[n-1].GetBuffer(),
1215 ++nDebugPathCount, pColorString, n-1);
1216 fflush( pDebugFile);
1219 #define erDEBUGDOT_OUT( p ) (DebugDot::out(p))
1220 #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p))
1221 #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop())
1222 #else
1223 #define erDEBUGDOT_OUT( p )
1224 #define erDEBUGDOT_ELEMENT_PUSH( p )
1225 #define erDEBUGDOT_ELEMENT_POP()
1226 #endif
1228 if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
1229 return; // no double/triple processing
1231 //! HACK:
1232 // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen
1233 // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!!
1235 if ( pDocument->IsInDdeLinkUpdate() )
1236 return;
1238 #if erDEBUGDOT
1239 // set nDebug=1 in debugger to init things
1240 if (nDebug == 1)
1242 ++nDebug;
1243 pDebugFile = fopen( aDebugDotFile, "a");
1244 if (!pDebugFile)
1245 nDebug = 0;
1246 else
1247 nDebug = erDEBUGDOTRUN;
1249 // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output
1250 DebugElement aDebugElem( this);
1251 // set nDebug=5 in debugger to close output
1252 if (nDebug == 5)
1254 nDebug = 0;
1255 fclose( pDebugFile);
1256 pDebugFile = 0;
1258 #endif
1260 if (bRunning)
1263 #if erDEBUGDOT
1264 if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
1265 aDebugThisPos != aDebugLastPos)
1266 erDEBUGDOT_OUT(aDebugThisPos == aDebugLastPos ? "orange" :
1267 (pDocument->GetRecursionHelper().GetIteration() ? "blue" :
1268 "red"));
1269 #endif
1271 if (!pDocument->GetDocOptions().IsIter())
1273 aResult.SetResultError( errCircularReference );
1274 return;
1277 if (aResult.GetResultError() == errCircularReference)
1278 aResult.SetResultError( 0 );
1280 // Start or add to iteration list.
1281 if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
1282 !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
1283 pDocument->GetRecursionHelper().SetInIterationReturn( true);
1285 return;
1287 // #63038# no multiple interprets for GetErrCode, IsValue, GetValue and
1288 // different entry point recursions. Would also lead to premature
1289 // convergence in iterations.
1290 if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
1291 pDocument->GetRecursionHelper().GetIteration())
1292 return ;
1294 erDEBUGDOT_OUT( pDocument->GetRecursionHelper().GetIteration() ? "magenta" : "");
1296 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
1297 BOOL bOldRunning = bRunning;
1298 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1300 bRunning = TRUE;
1301 rRecursionHelper.SetInRecursionReturn( true);
1303 else
1305 InterpretTail( SCITP_NORMAL);
1308 // While leaving a recursion or iteration stack, insert its cells to the
1309 // recursion list in reverse order.
1310 if (rRecursionHelper.IsInReturn())
1312 if (rRecursionHelper.GetRecursionCount() > 0 ||
1313 !rRecursionHelper.IsDoingRecursion())
1314 rRecursionHelper.Insert( this, bOldRunning, aResult);
1315 bool bIterationFromRecursion = false;
1316 bool bResumeIteration = false;
1319 if ((rRecursionHelper.IsInIterationReturn() &&
1320 rRecursionHelper.GetRecursionCount() == 0 &&
1321 !rRecursionHelper.IsDoingIteration()) ||
1322 bIterationFromRecursion || bResumeIteration)
1324 ScFormulaCell* pIterCell = this; // scope for debug convenience
1325 bool & rDone = rRecursionHelper.GetConvergingReference();
1326 rDone = false;
1327 if (!bIterationFromRecursion && bResumeIteration)
1329 bResumeIteration = false;
1330 // Resuming iteration expands the range.
1331 ScFormulaRecursionList::const_iterator aOldStart(
1332 rRecursionHelper.GetLastIterationStart());
1333 rRecursionHelper.ResumeIteration();
1334 // Mark new cells being in iteration.
1335 for (ScFormulaRecursionList::const_iterator aIter(
1336 rRecursionHelper.GetIterationStart()); aIter !=
1337 aOldStart; ++aIter)
1339 pIterCell = (*aIter).pCell;
1340 pIterCell->bIsIterCell = TRUE;
1342 // Mark older cells dirty again, in case they converted
1343 // without accounting for all remaining cells in the circle
1344 // that weren't touched so far, e.g. conditional. Restore
1345 // backuped result.
1346 USHORT nIteration = rRecursionHelper.GetIteration();
1347 for (ScFormulaRecursionList::const_iterator aIter(
1348 aOldStart); aIter !=
1349 rRecursionHelper.GetIterationEnd(); ++aIter)
1351 pIterCell = (*aIter).pCell;
1352 if (pIterCell->nSeenInIteration == nIteration)
1354 if (!pIterCell->bDirty || aIter == aOldStart)
1356 pIterCell->aResult = (*aIter).aPreviousResult;
1358 --pIterCell->nSeenInIteration;
1360 pIterCell->bDirty = TRUE;
1363 else
1365 bResumeIteration = false;
1366 // Close circle once.
1367 rRecursionHelper.GetList().back().pCell->InterpretTail(
1368 SCITP_CLOSE_ITERATION_CIRCLE);
1369 // Start at 1, init things.
1370 rRecursionHelper.StartIteration();
1371 // Mark all cells being in iteration.
1372 for (ScFormulaRecursionList::const_iterator aIter(
1373 rRecursionHelper.GetIterationStart()); aIter !=
1374 rRecursionHelper.GetIterationEnd(); ++aIter)
1376 pIterCell = (*aIter).pCell;
1377 pIterCell->bIsIterCell = TRUE;
1380 bIterationFromRecursion = false;
1381 USHORT nIterMax = pDocument->GetDocOptions().GetIterCount();
1382 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1383 rRecursionHelper.IncIteration())
1385 rDone = true;
1386 for ( ScFormulaRecursionList::iterator aIter(
1387 rRecursionHelper.GetIterationStart()); aIter !=
1388 rRecursionHelper.GetIterationEnd() &&
1389 !rRecursionHelper.IsInReturn(); ++aIter)
1391 pIterCell = (*aIter).pCell;
1392 if (pIterCell->IsDirtyOrInTableOpDirty() &&
1393 rRecursionHelper.GetIteration() !=
1394 pIterCell->GetSeenInIteration())
1396 (*aIter).aPreviousResult = pIterCell->aResult;
1397 pIterCell->InterpretTail( SCITP_FROM_ITERATION);
1399 rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
1401 if (rRecursionHelper.IsInReturn())
1403 bResumeIteration = true;
1404 break; // for
1405 // Don't increment iteration.
1408 if (!bResumeIteration)
1410 if (rDone)
1412 for (ScFormulaRecursionList::const_iterator aIter(
1413 rRecursionHelper.GetIterationStart());
1414 aIter != rRecursionHelper.GetIterationEnd();
1415 ++aIter)
1417 pIterCell = (*aIter).pCell;
1418 pIterCell->bIsIterCell = FALSE;
1419 pIterCell->nSeenInIteration = 0;
1420 pIterCell->bRunning = (*aIter).bOldRunning;
1423 else
1425 for (ScFormulaRecursionList::const_iterator aIter(
1426 rRecursionHelper.GetIterationStart());
1427 aIter != rRecursionHelper.GetIterationEnd();
1428 ++aIter)
1430 pIterCell = (*aIter).pCell;
1431 pIterCell->bIsIterCell = FALSE;
1432 pIterCell->nSeenInIteration = 0;
1433 pIterCell->bRunning = (*aIter).bOldRunning;
1434 // If one cell didn't converge, all cells of this
1435 // circular dependency don't, no matter whether
1436 // single cells did.
1437 pIterCell->bDirty = FALSE;
1438 pIterCell->bTableOpDirty = FALSE;
1439 pIterCell->aResult.SetResultError( errNoConvergence);
1440 pIterCell->bChanged = TRUE;
1441 pIterCell->SetTextWidth( TEXTWIDTH_DIRTY);
1442 pIterCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN);
1445 // End this iteration and remove entries.
1446 rRecursionHelper.EndIteration();
1447 bResumeIteration = rRecursionHelper.IsDoingIteration();
1450 if (rRecursionHelper.IsInRecursionReturn() &&
1451 rRecursionHelper.GetRecursionCount() == 0 &&
1452 !rRecursionHelper.IsDoingRecursion())
1454 bIterationFromRecursion = false;
1455 // Iterate over cells known so far, start with the last cell
1456 // encountered, inserting new cells if another recursion limit
1457 // is reached. Repeat until solved.
1458 rRecursionHelper.SetDoingRecursion( true);
1461 rRecursionHelper.SetInRecursionReturn( false);
1462 for (ScFormulaRecursionList::const_iterator aIter(
1463 rRecursionHelper.GetStart());
1464 !rRecursionHelper.IsInReturn() && aIter !=
1465 rRecursionHelper.GetEnd(); ++aIter)
1467 ScFormulaCell* pCell = (*aIter).pCell;
1468 if (pCell->IsDirtyOrInTableOpDirty())
1470 pCell->InterpretTail( SCITP_NORMAL);
1471 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1472 pCell->bRunning = (*aIter).bOldRunning;
1475 } while (rRecursionHelper.IsInRecursionReturn());
1476 rRecursionHelper.SetDoingRecursion( false);
1477 if (rRecursionHelper.IsInIterationReturn())
1479 if (!bResumeIteration)
1480 bIterationFromRecursion = true;
1482 else if (bResumeIteration ||
1483 rRecursionHelper.IsDoingIteration())
1484 rRecursionHelper.GetList().erase(
1485 rRecursionHelper.GetStart(),
1486 rRecursionHelper.GetLastIterationStart());
1487 else
1488 rRecursionHelper.Clear();
1490 } while (bIterationFromRecursion || bResumeIteration);
1493 // Fire worksheet calculate event
1494 pDocument->FireCalculateEvent( aPos.Tab() );
1497 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
1499 class RecursionCounter
1501 ScRecursionHelper& rRec;
1502 bool bStackedInIteration;
1503 public:
1504 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
1506 bStackedInIteration = rRec.IsDoingIteration();
1507 if (bStackedInIteration)
1508 rRec.GetRecursionInIterationStack().push( p);
1509 rRec.IncRecursionCount();
1511 ~RecursionCounter()
1513 rRec.DecRecursionCount();
1514 if (bStackedInIteration)
1515 rRec.GetRecursionInIterationStack().pop();
1517 } aRecursionCounter( pDocument->GetRecursionHelper(), this);
1518 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
1519 if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
1521 // #i11719# no UPN and no error and no token code but result string present
1522 // => interpretation of this cell during name-compilation and unknown names
1523 // => can't exchange underlying code array in CompileTokenArray() /
1524 // Compile() because interpreter's token iterator would crash.
1525 // This should only be a temporary condition and, since we set an
1526 // error, if ran into it again we'd bump into the dirty-clearing
1527 // condition further down.
1528 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1530 pCode->SetCodeError( errNoCode );
1531 // This is worth an assertion; if encountered in daily work
1532 // documents we might need another solution. Or just confirm correctness.
1533 DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but string" );
1534 return;
1536 CompileTokenArray();
1539 if( pCode->GetCodeLen() && pDocument )
1541 class StackCleaner
1543 ScDocument* pDoc;
1544 ScInterpreter* pInt;
1545 public:
1546 StackCleaner( ScDocument* pD, ScInterpreter* pI )
1547 : pDoc(pD), pInt(pI)
1549 ~StackCleaner()
1551 delete pInt;
1552 pDoc->DecInterpretLevel();
1555 pDocument->IncInterpretLevel();
1556 ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
1557 StackCleaner aStackCleaner( pDocument, p);
1558 USHORT nOldErrCode = aResult.GetResultError();
1559 if ( nSeenInIteration == 0 )
1560 { // Only the first time
1561 // With bChanged=FALSE, if a newly compiled cell has a result of
1562 // 0.0, no change is detected and the cell will not be repainted.
1563 // bChanged = FALSE;
1564 aResult.SetResultError( 0 );
1567 switch ( aResult.GetResultError() )
1569 case errCircularReference : // will be determined again if so
1570 aResult.SetResultError( 0 );
1571 break;
1574 BOOL bOldRunning = bRunning;
1575 bRunning = TRUE;
1576 p->Interpret();
1577 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
1579 if (nSeenInIteration > 0)
1580 --nSeenInIteration; // retry when iteration is resumed
1581 return;
1583 bRunning = bOldRunning;
1585 // Do not create a HyperLink() cell if the formula results in an error.
1586 if( p->GetError() && pCode->IsHyperLink())
1587 pCode->SetHyperLink(FALSE);
1589 if( p->GetError() && p->GetError() != errCircularReference)
1591 bDirty = FALSE;
1592 bTableOpDirty = FALSE;
1593 bChanged = TRUE;
1595 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1597 bool bIsValue = aResult.IsValue(); // the previous type
1598 // Did it converge?
1599 if ((bIsValue && p->GetResultType() == svDouble && fabs(
1600 p->GetNumResult() - aResult.GetDouble()) <=
1601 pDocument->GetDocOptions().GetIterEps()) ||
1602 (!bIsValue && p->GetResultType() == svString &&
1603 p->GetStringResult() == aResult.GetString()))
1605 // A convergence in the first iteration doesn't necessarily
1606 // mean that it's done, it may be because not all related cells
1607 // of a circle changed their values yet. If the set really
1608 // converges it will do so also during the next iteration. This
1609 // fixes situations like of #i44115#. If this wasn't wanted an
1610 // initial "uncalculated" value would be needed for all cells
1611 // of a circular dependency => graph needed before calculation.
1612 if (nSeenInIteration > 1 ||
1613 pDocument->GetDocOptions().GetIterCount() == 1)
1615 bDirty = FALSE;
1616 bTableOpDirty = FALSE;
1621 // New error code?
1622 if( p->GetError() != nOldErrCode )
1623 bChanged = TRUE;
1624 // Different number format?
1625 if( nFormatType != p->GetRetFormatType() )
1627 nFormatType = p->GetRetFormatType();
1628 bChanged = TRUE;
1630 if( nFormatIndex != p->GetRetFormatIndex() )
1632 nFormatIndex = p->GetRetFormatIndex();
1633 bChanged = TRUE;
1636 // In case of changes just obtain the result, no temporary and
1637 // comparison needed anymore.
1638 if (bChanged)
1639 aResult.SetToken( p->GetResultToken() );
1640 else
1642 ScFormulaResult aNewResult( p->GetResultToken());
1643 StackVar eOld = aResult.GetCellResultType();
1644 StackVar eNew = aNewResult.GetCellResultType();
1645 bChanged = (eOld != eNew ||
1646 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
1647 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1648 aResult.Assign( aNewResult);
1651 // Precision as shown?
1652 if ( aResult.IsValue() && !p->GetError()
1653 && pDocument->GetDocOptions().IsCalcAsShown()
1654 && nFormatType != NUMBERFORMAT_DATE
1655 && nFormatType != NUMBERFORMAT_TIME
1656 && nFormatType != NUMBERFORMAT_DATETIME )
1658 ULONG nFormat = pDocument->GetNumberFormat( aPos );
1659 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1660 nFormat = nFormatIndex;
1661 if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1662 nFormat = ScGlobal::GetStandardFormat(
1663 *pDocument->GetFormatTable(), nFormat, nFormatType );
1664 aResult.SetDouble( pDocument->RoundValueAsShown(
1665 aResult.GetDouble(), nFormat));
1667 if (eTailParam == SCITP_NORMAL)
1669 bDirty = FALSE;
1670 bTableOpDirty = FALSE;
1672 if( aResult.GetMatrix().Is() )
1674 // If the formula wasn't entered as a matrix formula, live on with
1675 // the upper left corner and let reference counting delete the matrix.
1676 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
1677 aResult.SetToken( aResult.GetCellResultToken());
1679 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1681 // Coded double error may occur via filter import.
1682 USHORT nErr = GetDoubleErrorValue( aResult.GetDouble());
1683 aResult.SetResultError( nErr);
1684 bChanged = true;
1686 if( bChanged )
1688 SetTextWidth( TEXTWIDTH_DIRTY );
1689 SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
1691 if ( !pCode->IsRecalcModeAlways() )
1692 pDocument->RemoveFromFormulaTree( this );
1694 // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten)
1696 if ( pCode->IsRecalcModeForced() )
1698 ULONG nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
1699 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
1700 if ( nValidation )
1702 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
1703 if ( pData && !pData->IsDataValid( this, aPos ) )
1704 pData->DoCalcError( this );
1708 // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren
1709 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
1710 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
1712 switch (p->GetVolatileType())
1714 case ScInterpreter::VOLATILE:
1715 // Volatile via built-in volatile functions. No actions needed.
1716 break;
1717 case ScInterpreter::VOLATILE_MACRO:
1718 // The formula contains a volatile macro.
1719 pCode->SetRecalcModeAlways();
1720 pDocument->PutInFormulaTree(this);
1721 StartListeningTo(pDocument);
1722 break;
1723 case ScInterpreter::NOT_VOLATILE:
1724 if (pCode->IsRecalcModeAlways())
1726 // The formula was previously volatile, but no more.
1727 EndListeningTo(pDocument);
1728 pCode->SetRecalcModeNormal();
1730 else
1732 // non-volatile formula. End listening to the area in case
1733 // it's listening due to macro module change.
1734 pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
1736 pDocument->RemoveFromFormulaTree(this);
1737 break;
1738 default:
1742 else
1744 // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen
1745 DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" );
1746 bDirty = FALSE;
1747 bTableOpDirty = FALSE;
1752 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
1754 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
1755 if (pMat)
1756 pMat->SetMatColsRows( nCols, nRows);
1757 else if (nCols || nRows)
1758 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
1762 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
1764 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
1765 if (pMat)
1766 pMat->GetMatColsRows( nCols, nRows);
1767 else
1769 nCols = 0;
1770 nRows = 0;
1775 ULONG ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, ULONG nFormat ) const
1777 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1778 return nFormatIndex;
1779 //! not ScFormulaCell::IsValue(), that could reinterpret the formula again.
1780 if ( aResult.IsValue() )
1781 return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType );
1782 else
1783 return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType );
1787 void __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
1789 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
1791 const ScHint* p = PTR_CAST( ScHint, &rHint );
1792 ULONG nHint = (p ? p->GetId() : 0);
1793 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY))
1795 BOOL bForceTrack = FALSE;
1796 if ( nHint & SC_HINT_TABLEOPDIRTY )
1798 bForceTrack = !bTableOpDirty;
1799 if ( !bTableOpDirty )
1801 pDocument->AddTableOpFormulaCell( this );
1802 bTableOpDirty = TRUE;
1805 else
1807 bForceTrack = !bDirty;
1808 SetDirtyVar();
1810 // #35962# Don't remove from FormulaTree to put in FormulaTrack to
1811 // put in FormulaTree again and again, only if necessary.
1812 // Any other means except RECALCMODE_ALWAYS by which a cell could
1813 // be in FormulaTree if it would notify other cells through
1814 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
1815 // #87866# Yes. The new TableOpDirty made it necessary to have a
1816 // forced mode where formulas may still be in FormulaTree from
1817 // TableOpDirty but have to notify dependents for normal dirty.
1818 if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
1819 || pCode->IsRecalcModeAlways())
1820 && !pDocument->IsInFormulaTrack( this ) )
1821 pDocument->AppendToFormulaTrack( this );
1826 void ScFormulaCell::SetDirty()
1828 if ( !IsInChangeTrack() )
1830 if ( pDocument->GetHardRecalcState() )
1831 SetDirtyVar();
1832 else
1834 // Mehrfach-FormulaTracking in Load und in CompileAll
1835 // nach CopyScenario und CopyBlockFromClip vermeiden.
1836 // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=FALSE
1837 // setzen, z.B. in CompileTokenArray
1838 if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
1840 SetDirtyVar();
1841 pDocument->AppendToFormulaTrack( this );
1842 pDocument->TrackFormulas();
1848 void ScFormulaCell::SetDirtyVar()
1850 bDirty = TRUE;
1851 // mark the sheet of this cell to be calculated
1852 pDocument->AddCalculateTable( aPos.Tab() );
1855 void ScFormulaCell::SetDirtyAfterLoad()
1857 bDirty = TRUE;
1858 if ( !pDocument->GetHardRecalcState() )
1859 pDocument->PutInFormulaTree( this );
1862 void ScFormulaCell::SetTableOpDirty()
1864 if ( !IsInChangeTrack() )
1866 if ( pDocument->GetHardRecalcState() )
1867 bTableOpDirty = TRUE;
1868 else
1870 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
1872 if ( !bTableOpDirty )
1874 pDocument->AddTableOpFormulaCell( this );
1875 bTableOpDirty = TRUE;
1877 pDocument->AppendToFormulaTrack( this );
1878 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
1885 BOOL ScFormulaCell::IsDirtyOrInTableOpDirty() const
1887 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
1891 void ScFormulaCell::SetErrCode( USHORT n )
1893 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
1894 * used whether it is solely for transport of a simple result error and get
1895 * rid of that abuse. */
1896 pCode->SetCodeError( n );
1897 // Hard set errors are transported as result type value per convention,
1898 // e.g. via clipboard. ScFormulaResult::IsValue() and
1899 // ScFormulaResult::GetDouble() handle that.
1900 aResult.SetResultError( n );
1903 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
1905 if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL )
1906 bDirty = TRUE;
1907 if ( nBits & RECALCMODE_ONLOAD_ONCE )
1908 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
1909 nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL;
1911 pCode->AddRecalcMode( nBits );
1914 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
1915 void ScFormulaCell::GetURLResult( String& rURL, String& rCellText )
1917 String aCellString;
1919 Color* pColor;
1921 // Cell Text uses the Cell format while the URL uses
1922 // the default format for the type.
1923 ULONG nCellFormat = pDocument->GetNumberFormat( aPos );
1924 SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
1926 if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1927 nCellFormat = GetStandardFormat( *pFormatter,nCellFormat );
1929 ULONG nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER);
1931 if ( IsValue() )
1933 double fValue = GetValue();
1934 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
1936 else
1938 GetString( aCellString );
1939 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
1941 ScConstMatrixRef xMat( aResult.GetMatrix());
1942 if (xMat)
1944 ScMatValType nMatValType;
1945 // determine if the matrix result is a string or value.
1946 const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType);
1947 if (pMatVal)
1949 if (!ScMatrix::IsValueType( nMatValType))
1950 rURL = pMatVal->GetString();
1951 else
1952 pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor );
1956 if(!rURL.Len())
1958 if(IsValue())
1959 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
1960 else
1961 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
1965 bool ScFormulaCell::IsMultilineResult()
1967 if (!IsValue())
1968 return aResult.IsMultiline();
1969 return false;
1972 EditTextObject* ScFormulaCell::CreateURLObject()
1974 String aCellText;
1975 String aURL;
1976 GetURLResult( aURL, aCellText );
1978 SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT);
1979 EditEngine& rEE = pDocument->GetEditEngine();
1980 rEE.SetText( EMPTY_STRING );
1981 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) );
1983 return rEE.CreateTextObject();
1986 // ============================================================================
1988 ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell )
1990 pCode = pCell->GetCode();
1991 pCode->Reset();
1992 aPos = pCell->aPos;
1995 BOOL lcl_ScDetectiveRefIter_SkipRef( ScToken* p )
1997 ScSingleRefData& rRef1 = p->GetSingleRef();
1998 if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted()
1999 || !rRef1.Valid() )
2000 return TRUE;
2001 if ( p->GetType() == svDoubleRef || p->GetType() == svExternalDoubleRef )
2003 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
2004 if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted()
2005 || !rRef2.Valid() )
2006 return TRUE;
2008 return FALSE;
2011 BOOL ScDetectiveRefIter::GetNextRef( ScRange& rRange )
2013 BOOL bRet = FALSE;
2014 ScToken* p = GetNextRefToken();
2015 if( p )
2017 SingleDoubleRefProvider aProv( *p );
2018 rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab );
2019 rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab );
2020 bRet = TRUE;
2023 return bRet;
2026 ScToken* ScDetectiveRefIter::GetNextRefToken()
2028 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2029 if (p)
2030 p->CalcAbsIfRel( aPos );
2032 while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) )
2034 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2035 if (p)
2036 p->CalcAbsIfRel( aPos );
2038 return p;
2041 // ============================================================================