1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_sc.hxx"
24
25 // INCLUDE ---------------------------------------------------------------
26
27 #include <svl/zforlist.hxx>
28
29 #include "scitems.hxx"
30 #include "attrib.hxx"
31 #include "cell.hxx"
32 #include "compiler.hxx"
33 #include "interpre.hxx"
34 #include "document.hxx"
35 #include "scmatrix.hxx"
36 #include "dociter.hxx"
37 #include "docoptio.hxx"
38 #include "rechead.hxx"
39 #include "rangenam.hxx"
40 #include "brdcst.hxx"
41 #include "ddelink.hxx"
42 #include "validat.hxx"
43 #include "progress.hxx"
44 #include "editutil.hxx"
45 #include "recursionhelper.hxx"
46 #include "postit.hxx"
47 #include "externalrefmgr.hxx"
48 #include <editeng/editobj.hxx>
49 #include <svl/intitem.hxx>
50 #include <editeng/flditem.hxx>
51 #include <svl/broadcast.hxx>
52
53 using namespace formula;
54 // More or less arbitrary, of course all recursions must fit into available
55 // stack space (which is what on all systems we don't know yet?). Choosing a
56 // lower value may be better than trying a much higher value that also isn't
57 // sufficient but temporarily leads to high memory consumption. On the other
58 // hand, if the value fits all recursions, execution is quicker as no resumes
59 // are necessary. Could be made a configurable option.
60 // Allow for a year's calendar (366).
61 const sal_uInt16 MAXRECURSION = 400;
62
63 // STATIC DATA -----------------------------------------------------------
64
65 #ifdef USE_MEMPOOL
66 // MemPools auf 4k Boundaries - 64 Bytes ausrichten
67 const sal_uInt16 nMemPoolValueCell = (0x8000 - 64) / sizeof(ScValueCell);
68 const sal_uInt16 nMemPoolFormulaCell = (0x8000 - 64) / sizeof(ScFormulaCell);
69 const sal_uInt16 nMemPoolStringCell = (0x4000 - 64) / sizeof(ScStringCell);
70 const sal_uInt16 nMemPoolNoteCell = (0x1000 - 64) / sizeof(ScNoteCell);
IMPL_FIXEDMEMPOOL_NEWDEL(ScValueCell,nMemPoolValueCell,nMemPoolValueCell)71 IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell, nMemPoolValueCell, nMemPoolValueCell )
72 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell, nMemPoolFormulaCell, nMemPoolFormulaCell )
73 IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell, nMemPoolStringCell, nMemPoolStringCell )
74 IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell, nMemPoolNoteCell, nMemPoolNoteCell )
75 #endif
76
77 // ============================================================================
78
79 ScBaseCell::ScBaseCell( CellType eNewType ) :
80 mpNote( 0 ),
81 mpBroadcaster( 0 ),
82 nTextWidth( TEXTWIDTH_DIRTY ),
83 eCellType( sal::static_int_cast<sal_uInt8>(eNewType) ),
84 nScriptType( SC_SCRIPTTYPE_UNKNOWN )
85 {
86 }
87
ScBaseCell(const ScBaseCell & rCell)88 ScBaseCell::ScBaseCell( const ScBaseCell& rCell ) :
89 mpNote( 0 ),
90 mpBroadcaster( 0 ),
91 nTextWidth( rCell.nTextWidth ),
92 eCellType( rCell.eCellType ),
93 nScriptType( SC_SCRIPTTYPE_UNKNOWN )
94 {
95 }
96
~ScBaseCell()97 ScBaseCell::~ScBaseCell()
98 {
99 delete mpNote;
100 delete mpBroadcaster;
101 DBG_ASSERT( eCellType == CELLTYPE_DESTROYED, "BaseCell Destructor" );
102 }
103
104 namespace {
105
lclCloneCell(const ScBaseCell & rSrcCell,ScDocument & rDestDoc,const ScAddress & rDestPos,int nCloneFlags)106 ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags )
107 {
108 switch( rSrcCell.GetCellType() )
109 {
110 case CELLTYPE_VALUE:
111 return new ScValueCell( static_cast< const ScValueCell& >( rSrcCell ) );
112 case CELLTYPE_STRING:
113 return new ScStringCell( static_cast< const ScStringCell& >( rSrcCell ) );
114 case CELLTYPE_EDIT:
115 return new ScEditCell( static_cast< const ScEditCell& >( rSrcCell ), rDestDoc );
116 case CELLTYPE_FORMULA:
117 return new ScFormulaCell( static_cast< const ScFormulaCell& >( rSrcCell ), rDestDoc, rDestPos, nCloneFlags );
118 case CELLTYPE_NOTE:
119 return new ScNoteCell;
120 default:;
121 }
122 DBG_ERROR( "lclCloneCell - unknown cell type" );
123 return 0;
124 }
125
126 } // namespace
127
CloneWithoutNote(ScDocument & rDestDoc,int nCloneFlags) const128 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, int nCloneFlags ) const
129 {
130 // notes will not be cloned -> cell address only needed for formula cells
131 ScAddress aDestPos;
132 if( eCellType == CELLTYPE_FORMULA )
133 aDestPos = static_cast< const ScFormulaCell* >( this )->aPos;
134 return lclCloneCell( *this, rDestDoc, aDestPos, nCloneFlags );
135 }
136
CloneWithoutNote(ScDocument & rDestDoc,const ScAddress & rDestPos,int nCloneFlags) const137 ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const
138 {
139 return lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags );
140 }
141
CloneWithNote(const ScAddress & rOwnPos,ScDocument & rDestDoc,const ScAddress & rDestPos,int nCloneFlags) const142 ScBaseCell* ScBaseCell::CloneWithNote( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const
143 {
144 ScBaseCell* pNewCell = lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags );
145 if( mpNote )
146 {
147 if( !pNewCell )
148 pNewCell = new ScNoteCell;
149 bool bCloneCaption = (nCloneFlags & SC_CLONECELL_NOCAPTION) == 0;
150 pNewCell->TakeNote( mpNote->Clone( rOwnPos, rDestDoc, rDestPos, bCloneCaption ) );
151 }
152 return pNewCell;
153 }
154
Delete()155 void ScBaseCell::Delete()
156 {
157 DeleteNote();
158 switch (eCellType)
159 {
160 case CELLTYPE_VALUE:
161 delete (ScValueCell*) this;
162 break;
163 case CELLTYPE_STRING:
164 delete (ScStringCell*) this;
165 break;
166 case CELLTYPE_EDIT:
167 delete (ScEditCell*) this;
168 break;
169 case CELLTYPE_FORMULA:
170 delete (ScFormulaCell*) this;
171 break;
172 case CELLTYPE_NOTE:
173 delete (ScNoteCell*) this;
174 break;
175 default:
176 DBG_ERROR("Unbekannter Zellentyp");
177 break;
178 }
179 }
180
IsBlank(bool bIgnoreNotes) const181 bool ScBaseCell::IsBlank( bool bIgnoreNotes ) const
182 {
183 return (eCellType == CELLTYPE_NOTE) && (bIgnoreNotes || !mpNote);
184 }
185
TakeNote(ScPostIt * pNote)186 void ScBaseCell::TakeNote( ScPostIt* pNote )
187 {
188 delete mpNote;
189 mpNote = pNote;
190 }
191
ReleaseNote()192 ScPostIt* ScBaseCell::ReleaseNote()
193 {
194 ScPostIt* pNote = mpNote;
195 mpNote = 0;
196 return pNote;
197 }
198
DeleteNote()199 void ScBaseCell::DeleteNote()
200 {
201 DELETEZ( mpNote );
202 }
203
TakeBroadcaster(SvtBroadcaster * pBroadcaster)204 void ScBaseCell::TakeBroadcaster( SvtBroadcaster* pBroadcaster )
205 {
206 delete mpBroadcaster;
207 mpBroadcaster = pBroadcaster;
208 }
209
ReleaseBroadcaster()210 SvtBroadcaster* ScBaseCell::ReleaseBroadcaster()
211 {
212 SvtBroadcaster* pBroadcaster = mpBroadcaster;
213 mpBroadcaster = 0;
214 return pBroadcaster;
215 }
216
DeleteBroadcaster()217 void ScBaseCell::DeleteBroadcaster()
218 {
219 DELETEZ( mpBroadcaster );
220 }
221
CreateTextCell(const String & rString,ScDocument * pDoc)222 ScBaseCell* ScBaseCell::CreateTextCell( const String& rString, ScDocument* pDoc )
223 {
224 if ( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND )
225 return new ScEditCell( rString, pDoc );
226 else
227 return new ScStringCell( rString );
228 }
229
StartListeningTo(ScDocument * pDoc)230 void ScBaseCell::StartListeningTo( ScDocument* pDoc )
231 {
232 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo()
233 && !pDoc->GetNoListening()
234 && !((ScFormulaCell*)this)->IsInChangeTrack()
235 )
236 {
237 pDoc->SetDetectiveDirty(sal_True); // es hat sich was geaendert...
238
239 ScFormulaCell* pFormCell = (ScFormulaCell*)this;
240 ScTokenArray* pArr = pFormCell->GetCode();
241 if( pArr->IsRecalcModeAlways() )
242 pDoc->StartListeningArea( BCA_LISTEN_ALWAYS, pFormCell );
243 else
244 {
245 pArr->Reset();
246 ScToken* t;
247 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
248 {
249 StackVar eType = t->GetType();
250 ScSingleRefData& rRef1 = t->GetSingleRef();
251 ScSingleRefData& rRef2 = (eType == svDoubleRef ?
252 t->GetDoubleRef().Ref2 : rRef1);
253 switch( eType )
254 {
255 case svSingleRef:
256 rRef1.CalcAbsIfRel( pFormCell->aPos );
257 if ( rRef1.Valid() )
258 {
259 pDoc->StartListeningCell(
260 ScAddress( rRef1.nCol,
261 rRef1.nRow,
262 rRef1.nTab ), pFormCell );
263 }
264 break;
265 case svDoubleRef:
266 t->CalcAbsIfRel( pFormCell->aPos );
267 if ( rRef1.Valid() && rRef2.Valid() )
268 {
269 if ( t->GetOpCode() == ocColRowNameAuto )
270 { // automagically
271 if ( rRef1.IsColRel() )
272 { // ColName
273 pDoc->StartListeningArea( ScRange (
274 rRef1.nCol,
275 rRef1.nRow,
276 rRef1.nTab,
277 rRef2.nCol,
278 MAXROW,
279 rRef2.nTab ), pFormCell );
280 }
281 else
282 { // RowName
283 pDoc->StartListeningArea( ScRange (
284 rRef1.nCol,
285 rRef1.nRow,
286 rRef1.nTab,
287 MAXCOL,
288 rRef2.nRow,
289 rRef2.nTab ), pFormCell );
290 }
291 }
292 else
293 {
294 pDoc->StartListeningArea( ScRange (
295 rRef1.nCol,
296 rRef1.nRow,
297 rRef1.nTab,
298 rRef2.nCol,
299 rRef2.nRow,
300 rRef2.nTab ), pFormCell );
301 }
302 }
303 break;
304 default:
305 ; // nothing
306 }
307 }
308 }
309 pFormCell->SetNeedsListening( sal_False);
310 }
311 }
312
313 // pArr gesetzt -> Referenzen von anderer Zelle nehmen
314 // dann muss auch aPos uebergeben werden!
315
EndListeningTo(ScDocument * pDoc,ScTokenArray * pArr,ScAddress aPos)316 void ScBaseCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
317 ScAddress aPos )
318 {
319 if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo()
320 && !((ScFormulaCell*)this)->IsInChangeTrack()
321 )
322 {
323 pDoc->SetDetectiveDirty(sal_True); // es hat sich was geaendert...
324
325 ScFormulaCell* pFormCell = (ScFormulaCell*)this;
326 if( pFormCell->GetCode()->IsRecalcModeAlways() )
327 pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, pFormCell );
328 else
329 {
330 if (!pArr)
331 {
332 pArr = pFormCell->GetCode();
333 aPos = pFormCell->aPos;
334 }
335 pArr->Reset();
336 ScToken* t;
337 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
338 {
339 StackVar eType = t->GetType();
340 ScSingleRefData& rRef1 = t->GetSingleRef();
341 ScSingleRefData& rRef2 = (eType == svDoubleRef ?
342 t->GetDoubleRef().Ref2 : rRef1);
343 switch( eType )
344 {
345 case svSingleRef:
346 rRef1.CalcAbsIfRel( aPos );
347 if ( rRef1.Valid() )
348 {
349 pDoc->EndListeningCell(
350 ScAddress( rRef1.nCol,
351 rRef1.nRow,
352 rRef1.nTab ), pFormCell );
353 }
354 break;
355 case svDoubleRef:
356 t->CalcAbsIfRel( aPos );
357 if ( rRef1.Valid() && rRef2.Valid() )
358 {
359 if ( t->GetOpCode() == ocColRowNameAuto )
360 { // automagically
361 if ( rRef1.IsColRel() )
362 { // ColName
363 pDoc->EndListeningArea( ScRange (
364 rRef1.nCol,
365 rRef1.nRow,
366 rRef1.nTab,
367 rRef2.nCol,
368 MAXROW,
369 rRef2.nTab ), pFormCell );
370 }
371 else
372 { // RowName
373 pDoc->EndListeningArea( ScRange (
374 rRef1.nCol,
375 rRef1.nRow,
376 rRef1.nTab,
377 MAXCOL,
378 rRef2.nRow,
379 rRef2.nTab ), pFormCell );
380 }
381 }
382 else
383 {
384 pDoc->EndListeningArea( ScRange (
385 rRef1.nCol,
386 rRef1.nRow,
387 rRef1.nTab,
388 rRef2.nCol,
389 rRef2.nRow,
390 rRef2.nTab ), pFormCell );
391 }
392 }
393 break;
394 default:
395 ; // nothing
396 }
397 }
398 }
399 }
400 }
401
402
GetErrorCode() const403 sal_uInt16 ScBaseCell::GetErrorCode() const
404 {
405 switch ( eCellType )
406 {
407 case CELLTYPE_FORMULA :
408 return ((ScFormulaCell*)this)->GetErrCode();
409 default:
410 return 0;
411 }
412 }
413
414
HasEmptyData() const415 sal_Bool ScBaseCell::HasEmptyData() const
416 {
417 switch ( eCellType )
418 {
419 case CELLTYPE_NOTE :
420 return sal_True;
421 case CELLTYPE_FORMULA :
422 return ((ScFormulaCell*)this)->IsEmpty();
423 default:
424 return sal_False;
425 }
426 }
427
428
HasValueData() const429 sal_Bool ScBaseCell::HasValueData() const
430 {
431 switch ( eCellType )
432 {
433 case CELLTYPE_VALUE :
434 return sal_True;
435 case CELLTYPE_FORMULA :
436 return ((ScFormulaCell*)this)->IsValue();
437 default:
438 return sal_False;
439 }
440 }
441
442
HasStringData() const443 sal_Bool ScBaseCell::HasStringData() const
444 {
445 switch ( eCellType )
446 {
447 case CELLTYPE_STRING :
448 case CELLTYPE_EDIT :
449 return sal_True;
450 case CELLTYPE_FORMULA :
451 return !((ScFormulaCell*)this)->IsValue();
452 default:
453 return sal_False;
454 }
455 }
456
GetStringData() const457 String ScBaseCell::GetStringData() const
458 {
459 String aStr;
460 switch ( eCellType )
461 {
462 case CELLTYPE_STRING:
463 ((const ScStringCell*)this)->GetString( aStr );
464 break;
465 case CELLTYPE_EDIT:
466 ((const ScEditCell*)this)->GetString( aStr );
467 break;
468 case CELLTYPE_FORMULA:
469 ((ScFormulaCell*)this)->GetString( aStr ); // an der Formelzelle nicht-const
470 break;
471 }
472 return aStr;
473 }
474
475 // static
CellEqual(const ScBaseCell * pCell1,const ScBaseCell * pCell2)476 sal_Bool ScBaseCell::CellEqual( const ScBaseCell* pCell1, const ScBaseCell* pCell2 )
477 {
478 CellType eType1 = CELLTYPE_NONE;
479 CellType eType2 = CELLTYPE_NONE;
480 if ( pCell1 )
481 {
482 eType1 = pCell1->GetCellType();
483 if (eType1 == CELLTYPE_EDIT)
484 eType1 = CELLTYPE_STRING;
485 else if (eType1 == CELLTYPE_NOTE)
486 eType1 = CELLTYPE_NONE;
487 }
488 if ( pCell2 )
489 {
490 eType2 = pCell2->GetCellType();
491 if (eType2 == CELLTYPE_EDIT)
492 eType2 = CELLTYPE_STRING;
493 else if (eType2 == CELLTYPE_NOTE)
494 eType2 = CELLTYPE_NONE;
495 }
496 if ( eType1 != eType2 )
497 return sal_False;
498
499 switch ( eType1 ) // beide Typen gleich
500 {
501 case CELLTYPE_NONE: // beide leer
502 return sal_True;
503 case CELLTYPE_VALUE: // wirklich Value-Zellen
504 return ( ((const ScValueCell*)pCell1)->GetValue() ==
505 ((const ScValueCell*)pCell2)->GetValue() );
506 case CELLTYPE_STRING: // String oder Edit
507 {
508 String aText1;
509 if ( pCell1->GetCellType() == CELLTYPE_STRING )
510 ((const ScStringCell*)pCell1)->GetString(aText1);
511 else
512 ((const ScEditCell*)pCell1)->GetString(aText1);
513 String aText2;
514 if ( pCell2->GetCellType() == CELLTYPE_STRING )
515 ((const ScStringCell*)pCell2)->GetString(aText2);
516 else
517 ((const ScEditCell*)pCell2)->GetString(aText2);
518 return ( aText1 == aText2 );
519 }
520 case CELLTYPE_FORMULA:
521 {
522 //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!!
523 //! Vergleichsfunktion an der Formelzelle ???
524 //! Abfrage mit ScColumn::SwapRow zusammenfassen!
525
526 ScTokenArray* pCode1 = ((ScFormulaCell*)pCell1)->GetCode();
527 ScTokenArray* pCode2 = ((ScFormulaCell*)pCell2)->GetCode();
528
529 if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN
530 {
531 sal_Bool bEqual = sal_True;
532 sal_uInt16 nLen = pCode1->GetLen();
533 FormulaToken** ppToken1 = pCode1->GetArray();
534 FormulaToken** ppToken2 = pCode2->GetArray();
535 for (sal_uInt16 i=0; i<nLen; i++)
536 if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) )
537 {
538 bEqual = sal_False;
539 break;
540 }
541
542 if (bEqual)
543 return sal_True;
544 }
545
546 return sal_False; // unterschiedlich lang oder unterschiedliche Tokens
547 }
548 default:
549 DBG_ERROR("huch, was fuer Zellen???");
550 }
551 return sal_False;
552 }
553
554 // ============================================================================
555
ScNoteCell(SvtBroadcaster * pBC)556 ScNoteCell::ScNoteCell( SvtBroadcaster* pBC ) :
557 ScBaseCell( CELLTYPE_NOTE )
558 {
559 TakeBroadcaster( pBC );
560 }
561
ScNoteCell(ScPostIt * pNote,SvtBroadcaster * pBC)562 ScNoteCell::ScNoteCell( ScPostIt* pNote, SvtBroadcaster* pBC ) :
563 ScBaseCell( CELLTYPE_NOTE )
564 {
565 TakeNote( pNote );
566 TakeBroadcaster( pBC );
567 }
568
569 #ifdef DBG_UTIL
~ScNoteCell()570 ScNoteCell::~ScNoteCell()
571 {
572 eCellType = CELLTYPE_DESTROYED;
573 }
574 #endif
575
576 // ============================================================================
577
ScValueCell()578 ScValueCell::ScValueCell() :
579 ScBaseCell( CELLTYPE_VALUE ),
580 mfValue( 0.0 )
581 {
582 }
583
ScValueCell(double fValue)584 ScValueCell::ScValueCell( double fValue ) :
585 ScBaseCell( CELLTYPE_VALUE ),
586 mfValue( fValue )
587 {
588 }
589
590 #ifdef DBG_UTIL
~ScValueCell()591 ScValueCell::~ScValueCell()
592 {
593 eCellType = CELLTYPE_DESTROYED;
594 }
595 #endif
596
597 // ============================================================================
598
ScStringCell()599 ScStringCell::ScStringCell() :
600 ScBaseCell( CELLTYPE_STRING )
601 {
602 }
603
ScStringCell(const String & rString)604 ScStringCell::ScStringCell( const String& rString ) :
605 ScBaseCell( CELLTYPE_STRING ),
606 maString( rString.intern() )
607 {
608 }
609
610 #ifdef DBG_UTIL
~ScStringCell()611 ScStringCell::~ScStringCell()
612 {
613 eCellType = CELLTYPE_DESTROYED;
614 }
615 #endif
616
617 // ============================================================================
618
619 //
620 // ScFormulaCell
621 //
622
ScFormulaCell()623 ScFormulaCell::ScFormulaCell() :
624 ScBaseCell( CELLTYPE_FORMULA ),
625 eTempGrammar( FormulaGrammar::GRAM_DEFAULT),
626 pCode( NULL ),
627 pDocument( NULL ),
628 pPrevious(0),
629 pNext(0),
630 pPreviousTrack(0),
631 pNextTrack(0),
632 nFormatIndex(0),
633 nFormatType( NUMBERFORMAT_NUMBER ),
634 nSeenInIteration(0),
635 cMatrixFlag ( MM_NONE ),
636 bDirty( sal_False ),
637 bChanged( sal_False ),
638 bRunning( sal_False ),
639 bCompile( sal_False ),
640 bSubTotal( sal_False ),
641 bIsIterCell( sal_False ),
642 bInChangeTrack( sal_False ),
643 bTableOpDirty( sal_False ),
644 bNeedListening( sal_False ),
645 pValidRefToken( NULL ),
646 aPos(0,0,0)
647 {
648 }
649
ScFormulaCell(ScDocument * pDoc,const ScAddress & rPos,const String & rFormula,const FormulaGrammar::Grammar eGrammar,sal_uInt8 cMatInd)650 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
651 const String& rFormula,
652 const FormulaGrammar::Grammar eGrammar,
653 sal_uInt8 cMatInd ) :
654 ScBaseCell( CELLTYPE_FORMULA ),
655 eTempGrammar( eGrammar),
656 pCode( NULL ),
657 pDocument( pDoc ),
658 pPrevious(0),
659 pNext(0),
660 pPreviousTrack(0),
661 pNextTrack(0),
662 nFormatIndex(0),
663 nFormatType( NUMBERFORMAT_NUMBER ),
664 nSeenInIteration(0),
665 cMatrixFlag ( cMatInd ),
666 bDirty( sal_True ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0
667 bChanged( sal_False ),
668 bRunning( sal_False ),
669 bCompile( sal_False ),
670 bSubTotal( sal_False ),
671 bIsIterCell( sal_False ),
672 bInChangeTrack( sal_False ),
673 bTableOpDirty( sal_False ),
674 bNeedListening( sal_False ),
675 pValidRefToken( NULL ),
676 aPos( rPos )
677 {
678 Compile( rFormula, sal_True, eGrammar ); // bNoListening, Insert does that
679 }
680
681 // Wird von den Importfiltern verwendet
682
ScFormulaCell(ScDocument * pDoc,const ScAddress & rPos,const ScTokenArray * pArr,const FormulaGrammar::Grammar eGrammar,sal_uInt8 cInd)683 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
684 const ScTokenArray* pArr,
685 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) :
686 ScBaseCell( CELLTYPE_FORMULA ),
687 eTempGrammar( eGrammar),
688 pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ),
689 pDocument( pDoc ),
690 pPrevious(0),
691 pNext(0),
692 pPreviousTrack(0),
693 pNextTrack(0),
694 nFormatIndex(0),
695 nFormatType( NUMBERFORMAT_NUMBER ),
696 nSeenInIteration(0),
697 cMatrixFlag ( cInd ),
698 bDirty( NULL != pArr ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0
699 bChanged( sal_False ),
700 bRunning( sal_False ),
701 bCompile( sal_False ),
702 bSubTotal( sal_False ),
703 bIsIterCell( sal_False ),
704 bInChangeTrack( sal_False ),
705 bTableOpDirty( sal_False ),
706 bNeedListening( sal_False ),
707 pValidRefToken( NULL ),
708 aPos( rPos )
709 {
710 // UPN-Array erzeugen
711 if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
712 {
713 ScCompiler aComp( pDocument, aPos, *pCode);
714 aComp.SetGrammar(eTempGrammar);
715 bSubTotal = aComp.CompileTokenArray();
716 nFormatType = aComp.GetNumFormatType();
717 }
718 else
719 {
720 pCode->Reset();
721 if ( pCode->GetNextOpCodeRPN( ocSubTotal ) )
722 bSubTotal = sal_True;
723 }
724 }
725
ScFormulaCell(const ScFormulaCell & rCell,ScDocument & rDoc,const ScAddress & rPos,int nCloneFlags)726 ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
727 ScBaseCell( rCell ),
728 SvtListener(),
729 aResult( rCell.aResult ),
730 eTempGrammar( rCell.eTempGrammar),
731 pDocument( &rDoc ),
732 pPrevious(0),
733 pNext(0),
734 pPreviousTrack(0),
735 pNextTrack(0),
736 nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ),
737 nFormatType( rCell.nFormatType ),
738 nSeenInIteration(0),
739 cMatrixFlag ( rCell.cMatrixFlag ),
740 bDirty( rCell.bDirty ),
741 bChanged( rCell.bChanged ),
742 bRunning( sal_False ),
743 bCompile( rCell.bCompile ),
744 bSubTotal( rCell.bSubTotal ),
745 bIsIterCell( sal_False ),
746 bInChangeTrack( sal_False ),
747 bTableOpDirty( sal_False ),
748 bNeedListening( sal_False ),
749 aPos( rPos )
750 {
751 if ( rCell.pValidRefToken )
752 pValidRefToken = static_cast<ScToken*>(rCell.pValidRefToken->Clone());
753 else
754 pValidRefToken = NULL;
755
756 pCode = (rCell.pCode) ? rCell.pCode->Clone() : NULL;
757
758 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
759 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
760
761 // evtl. Fehler zuruecksetzen und neu kompilieren
762 // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben
763 // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten
764 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
765 {
766 pCode->SetCodeError( 0 );
767 bCompile = sal_True;
768 }
769 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
770 sal_Bool bCompileLater = sal_False;
771 sal_Bool bClipMode = rCell.pDocument->IsClipboard();
772 if( !bCompile )
773 { // Name references with references and ColRowNames
774 pCode->Reset();
775 ScToken* t;
776 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
777 {
778 if ( t->GetOpCode() == ocExternalRef )
779 {
780 // External name, cell, and area references.
781 bCompile = true;
782 }
783 else if ( t->GetType() == svIndex )
784 {
785 ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( t->GetIndex() );
786 if( pRangeData )
787 {
788 if( pRangeData->HasReferences() )
789 bCompile = sal_True;
790 }
791 else
792 bCompile = sal_True; // invalid reference!
793 }
794 else if ( t->GetOpCode() == ocColRowName )
795 {
796 bCompile = sal_True; // new lookup needed
797 bCompileLater = bClipMode;
798 }
799 }
800 }
801 if( bCompile )
802 {
803 if ( !bCompileLater && bClipMode )
804 {
805 // Merging ranges needs the actual positions after UpdateReference.
806 // ColRowNames need new lookup after positions are adjusted.
807 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName);
808 }
809 if ( !bCompileLater )
810 {
811 // bNoListening, not at all if in Clipboard/Undo,
812 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
813 CompileTokenArray( sal_True );
814 }
815 }
816
817 if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
818 StartListeningTo( &rDoc );
819 }
820
~ScFormulaCell()821 ScFormulaCell::~ScFormulaCell()
822 {
823 pDocument->RemoveFromFormulaTree( this );
824
825 if (pDocument->HasExternalRefManager())
826 pDocument->GetExternalRefManager()->removeRefCell(this);
827
828 delete pCode;
829 #ifdef DBG_UTIL
830 eCellType = CELLTYPE_DESTROYED;
831 #endif
832 DELETEZ(pValidRefToken);
833 }
834
GetFormula(rtl::OUStringBuffer & rBuffer,const FormulaGrammar::Grammar eGrammar) const835 void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer,
836 const FormulaGrammar::Grammar eGrammar ) const
837 {
838 if( pCode->GetCodeError() && !pCode->GetLen() )
839 {
840 rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
841 return;
842 }
843 else if( cMatrixFlag == MM_REFERENCE )
844 {
845 // Reference to another cell that contains a matrix formula.
846 pCode->Reset();
847 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
848 if( p )
849 {
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? */
854 ScBaseCell* pCell;
855 ScSingleRefData& rRef = p->GetSingleRef();
856 rRef.CalcAbsIfRel( aPos );
857 if ( rRef.Valid() )
858 pCell = pDocument->GetCell( ScAddress( rRef.nCol,
859 rRef.nRow, rRef.nTab ) );
860 else
861 pCell = NULL;
862 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA)
863 {
864 ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar);
865 return;
866 }
867 else
868 {
869 ScCompiler aComp( pDocument, aPos, *pCode);
870 aComp.SetGrammar(eGrammar);
871 aComp.CreateStringFromTokenArray( rBuffer );
872 }
873 }
874 else
875 {
876 DBG_ERROR("ScFormulaCell::GetFormula: not a matrix");
877 }
878 }
879 else
880 {
881 ScCompiler aComp( pDocument, aPos, *pCode);
882 aComp.SetGrammar(eGrammar);
883 aComp.CreateStringFromTokenArray( rBuffer );
884 }
885
886 sal_Unicode ch('=');
887 rBuffer.insert( 0, &ch, 1 );
888 if( cMatrixFlag )
889 {
890 sal_Unicode ch2('{');
891 rBuffer.insert( 0, &ch2, 1);
892 rBuffer.append( sal_Unicode('}'));
893 }
894 }
895
GetFormula(String & rFormula,const FormulaGrammar::Grammar eGrammar) const896 void ScFormulaCell::GetFormula( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const
897 {
898 rtl::OUStringBuffer rBuffer( rFormula );
899 GetFormula( rBuffer, eGrammar );
900 rFormula = rBuffer;
901 }
902
GetResultDimensions(SCSIZE & rCols,SCSIZE & rRows)903 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
904 {
905 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
906 Interpret();
907
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 );
912 else
913 {
914 rCols = 0;
915 rRows = 0;
916 }
917 }
918
Compile(const String & rFormula,sal_Bool bNoListening,const FormulaGrammar::Grammar eGrammar)919 void ScFormulaCell::Compile( const String& rFormula, sal_Bool bNoListening,
920 const FormulaGrammar::Grammar eGrammar )
921 {
922 //#118851#, the initialization code for pCode after it can not be gnored if it is still NULL
923 if ( pCode && pDocument->IsClipOrUndo() ) return;
924 sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
925 if ( bWasInFormulaTree )
926 pDocument->RemoveFromFormulaTree( this );
927 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
928 if ( pCode )
929 pCode->Clear();
930 ScTokenArray* pCodeOld = pCode;
931 ScCompiler aComp( pDocument, aPos);
932 aComp.SetGrammar(eGrammar);
933 pCode = aComp.CompileString( rFormula );
934 if ( pCodeOld )
935 delete pCodeOld;
936 if( !pCode->GetCodeError() )
937 {
938 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() && rFormula == aResult.GetHybridFormula() )
939 { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray
940 if ( rFormula.GetChar(0) == '=' )
941 pCode->AddBad( rFormula.GetBuffer() + 1 );
942 else
943 pCode->AddBad( rFormula.GetBuffer() );
944 }
945 bCompile = sal_True;
946 CompileTokenArray( bNoListening );
947 }
948 else
949 {
950 bChanged = sal_True;
951 SetTextWidth( TEXTWIDTH_DIRTY );
952 SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
953 }
954 if ( bWasInFormulaTree )
955 pDocument->PutInFormulaTree( this );
956 }
957
958
CompileTokenArray(sal_Bool bNoListening)959 void ScFormulaCell::CompileTokenArray( sal_Bool bNoListening )
960 {
961 // Not already compiled?
962 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
963 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
964 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
965 {
966 // RPN length may get changed
967 sal_Bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
968 if ( bWasInFormulaTree )
969 pDocument->RemoveFromFormulaTree( this );
970
971 // Loading from within filter? No listening yet!
972 if( pDocument->IsInsertingFromOtherDoc() )
973 bNoListening = sal_True;
974
975 if( !bNoListening && pCode->GetCodeLen() )
976 EndListeningTo( pDocument );
977 ScCompiler aComp(pDocument, aPos, *pCode);
978 aComp.SetGrammar(pDocument->GetGrammar());
979 bSubTotal = aComp.CompileTokenArray();
980 if( !pCode->GetCodeError() )
981 {
982 nFormatType = aComp.GetNumFormatType();
983 nFormatIndex = 0;
984 bChanged = sal_True;
985 aResult.SetToken( NULL);
986 bCompile = sal_False;
987 if ( !bNoListening )
988 StartListeningTo( pDocument );
989 }
990 if ( bWasInFormulaTree )
991 pDocument->PutInFormulaTree( this );
992 }
993 }
994
995
CompileXML(ScProgress & rProgress)996 void ScFormulaCell::CompileXML( ScProgress& rProgress )
997 {
998 if ( cMatrixFlag == MM_REFERENCE )
999 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1000 // just establish listeners
1001 StartListeningTo( pDocument );
1002 return ;
1003 }
1004
1005 ScCompiler aComp( pDocument, aPos, *pCode);
1006 aComp.SetGrammar(eTempGrammar);
1007 String aFormula, aFormulaNmsp;
1008 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1009 pDocument->DecXMLImportedFormulaCount( aFormula.Len() );
1010 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
1011 // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein
1012 if ( pCode )
1013 pCode->Clear();
1014 ScTokenArray* pCodeOld = pCode;
1015 pCode = aComp.CompileString( aFormula, aFormulaNmsp );
1016 delete pCodeOld;
1017 if( !pCode->GetCodeError() )
1018 {
1019 if ( !pCode->GetLen() )
1020 {
1021 if ( aFormula.GetChar(0) == '=' )
1022 pCode->AddBad( aFormula.GetBuffer() + 1 );
1023 else
1024 pCode->AddBad( aFormula.GetBuffer() );
1025 }
1026 bSubTotal = aComp.CompileTokenArray();
1027 if( !pCode->GetCodeError() )
1028 {
1029 nFormatType = aComp.GetNumFormatType();
1030 nFormatIndex = 0;
1031 bChanged = sal_True;
1032 bCompile = sal_False;
1033 StartListeningTo( pDocument );
1034 }
1035 }
1036 else
1037 {
1038 bChanged = sal_True;
1039 SetTextWidth( TEXTWIDTH_DIRTY );
1040 SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
1041 }
1042
1043 // Same as in Load: after loading, it must be known if ocMacro is in any formula
1044 // (for macro warning, CompileXML is called at the end of loading XML file)
1045 if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
1046 pDocument->SetHasMacroFunc( sal_True );
1047 }
1048
1049
CalcAfterLoad()1050 void ScFormulaCell::CalcAfterLoad()
1051 {
1052 sal_Bool bNewCompiled = sal_False;
1053 // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis,
1054 // aber kein TokenArray
1055 if( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1056 {
1057 Compile( aResult.GetHybridFormula(), sal_True, eTempGrammar);
1058 aResult.SetToken( NULL);
1059 bDirty = sal_True;
1060 bNewCompiled = sal_True;
1061 }
1062 // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen
1063 // wurde, da die RangeNames erst jetzt existieren.
1064 if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
1065 {
1066 ScCompiler aComp(pDocument, aPos, *pCode);
1067 aComp.SetGrammar(pDocument->GetGrammar());
1068 bSubTotal = aComp.CompileTokenArray();
1069 nFormatType = aComp.GetNumFormatType();
1070 nFormatIndex = 0;
1071 bDirty = sal_True;
1072 bCompile = sal_False;
1073 bNewCompiled = sal_True;
1074 }
1075 // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503
1076 // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib
1077 // bei einem fabs(-NAN) abstuerzt (#32739#)
1078 // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht
1079 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1080 {
1081 DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?");
1082 aResult.SetResultError( errIllegalFPOperation );
1083 bDirty = sal_True;
1084 }
1085 // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix,
1086 // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge
1087 if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
1088 GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
1089 {
1090 cMatrixFlag = MM_FORMULA;
1091 SetMatColsRows( 1, 1);
1092 }
1093 // Muss die Zelle berechnet werden?
1094 // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann
1095 // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL
1096 if( !bNewCompiled || !pCode->GetCodeError() )
1097 {
1098 StartListeningTo( pDocument );
1099 if( !pCode->IsRecalcModeNormal() )
1100 bDirty = sal_True;
1101 }
1102 if ( pCode->IsRecalcModeAlways() )
1103 { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie
1104 // auch bei jedem F9 berechnet werden.
1105 bDirty = sal_True;
1106 }
1107 // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in
1108 // SetDirtyAfterLoad.
1109 }
1110
1111
MarkUsedExternalReferences()1112 bool ScFormulaCell::MarkUsedExternalReferences()
1113 {
1114 return pCode && pDocument->MarkUsedExternalReferences( *pCode);
1115 }
1116
1117
1118 // FIXME: set to 0
1119 #define erDEBUGDOT 0
1120 // If set to 1, write output that's suitable for graphviz tools like dot.
1121 // Only node1 -> node2 entries are written, you'll have to manually surround
1122 // the file content with [strict] digraph name { ... }
1123 // The ``strict'' keyword might be necessary in case of multiple identical
1124 // paths like they occur in iterations, otherwise dot may consume too much
1125 // memory when generating the layout, or you'll get unreadable output. On the
1126 // other hand, information about recurring calculation is lost then.
1127 // Generates output only if variable nDebug is set in debugger, see below.
1128 // FIXME: currently doesn't cope with iterations and recursions. Code fragments
1129 // are a leftover from a previous debug session, meant as a pointer.
1130 #if erDEBUGDOT
1131 #include <cstdio>
1132 using ::std::fopen;
1133 using ::std::fprintf;
1134 #include <vector>
1135 static const char aDebugDotFile[] = "ttt_debug.dot";
1136 #endif
1137
Interpret()1138 void ScFormulaCell::Interpret()
1139 {
1140
1141 #if erDEBUGDOT
1142 static int nDebug = 0;
1143 static const int erDEBUGDOTRUN = 3;
1144 static FILE* pDebugFile = 0;
1145 static sal_Int32 nDebugRootCount = 0;
1146 static unsigned int nDebugPathCount = 0;
1147 static ScAddress aDebugLastPos( ScAddress::INITIALIZE_INVALID);
1148 static ScAddress aDebugThisPos( ScAddress::INITIALIZE_INVALID);
1149 typedef ::std::vector< ByteString > DebugVector;
1150 static DebugVector aDebugVec;
1151 class DebugElement
1152 {
1153 public:
1154 static void push( ScFormulaCell* pCell )
1155 {
1156 aDebugThisPos = pCell->aPos;
1157 if (aDebugVec.empty())
1158 {
1159 ByteString aR( "root_");
1160 aR += ByteString::CreateFromInt32( ++nDebugRootCount);
1161 aDebugVec.push_back( aR);
1162 }
1163 String aStr;
1164 pCell->aPos.Format( aStr, SCA_VALID | SCA_TAB_3D, pCell->GetDocument(),
1165 pCell->GetDocument()->GetAddressConvention() );
1166 ByteString aB( aStr, RTL_TEXTENCODING_UTF8);
1167 aDebugVec.push_back( aB);
1168 }
1169 static void pop()
1170 {
1171 aDebugLastPos = aDebugThisPos;
1172 if (!aDebugVec.empty())
1173 {
1174 aDebugVec.pop_back();
1175 if (aDebugVec.size() == 1)
1176 {
1177 aDebugVec.pop_back();
1178 aDebugLastPos = ScAddress( ScAddress::INITIALIZE_INVALID);
1179 }
1180 }
1181 }
1182 DebugElement( ScFormulaCell* p ) { push(p); }
1183 ~DebugElement() { pop(); }
1184 };
1185 class DebugDot
1186 {
1187 public:
1188 static void out( const char* pColor )
1189 {
1190 if (nDebug != erDEBUGDOTRUN)
1191 return;
1192 char pColorString[256];
1193 sprintf( pColorString, (*pColor ?
1194 ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor,
1195 pColor);
1196 size_t n = aDebugVec.size();
1197 fprintf( pDebugFile,
1198 "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n",
1199 aDebugVec[n-2].GetBuffer(), aDebugVec[n-1].GetBuffer(),
1200 ++nDebugPathCount, pColorString, n-1);
1201 fflush( pDebugFile);
1202 }
1203 };
1204 #define erDEBUGDOT_OUT( p ) (DebugDot::out(p))
1205 #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p))
1206 #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop())
1207 #else
1208 #define erDEBUGDOT_OUT( p )
1209 #define erDEBUGDOT_ELEMENT_PUSH( p )
1210 #define erDEBUGDOT_ELEMENT_POP()
1211 #endif
1212
1213 if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
1214 return; // no double/triple processing
1215
1216 //! HACK:
1217 // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen
1218 // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!!
1219
1220 if ( pDocument->IsInDdeLinkUpdate() )
1221 return;
1222
1223 #if erDEBUGDOT
1224 // set nDebug=1 in debugger to init things
1225 if (nDebug == 1)
1226 {
1227 ++nDebug;
1228 pDebugFile = fopen( aDebugDotFile, "a");
1229 if (!pDebugFile)
1230 nDebug = 0;
1231 else
1232 nDebug = erDEBUGDOTRUN;
1233 }
1234 // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output
1235 DebugElement aDebugElem( this);
1236 // set nDebug=5 in debugger to close output
1237 if (nDebug == 5)
1238 {
1239 nDebug = 0;
1240 fclose( pDebugFile);
1241 pDebugFile = 0;
1242 }
1243 #endif
1244
1245 if (bRunning)
1246 {
1247
1248 #if erDEBUGDOT
1249 if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
1250 aDebugThisPos != aDebugLastPos)
1251 erDEBUGDOT_OUT(aDebugThisPos == aDebugLastPos ? "orange" :
1252 (pDocument->GetRecursionHelper().GetIteration() ? "blue" :
1253 "red"));
1254 #endif
1255
1256 if (!pDocument->GetDocOptions().IsIter())
1257 {
1258 aResult.SetResultError( errCircularReference );
1259 return;
1260 }
1261
1262 if (aResult.GetResultError() == errCircularReference)
1263 aResult.SetResultError( 0 );
1264
1265 // Start or add to iteration list.
1266 if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
1267 !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
1268 pDocument->GetRecursionHelper().SetInIterationReturn( true);
1269
1270 return;
1271 }
1272 // #63038# no multiple interprets for GetErrCode, IsValue, GetValue and
1273 // different entry point recursions. Would also lead to premature
1274 // convergence in iterations.
1275 if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
1276 pDocument->GetRecursionHelper().GetIteration())
1277 return ;
1278
1279 erDEBUGDOT_OUT( pDocument->GetRecursionHelper().GetIteration() ? "magenta" : "");
1280
1281 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
1282 sal_Bool bOldRunning = bRunning;
1283 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1284 {
1285 bRunning = sal_True;
1286 rRecursionHelper.SetInRecursionReturn( true);
1287 }
1288 else
1289 {
1290 InterpretTail( SCITP_NORMAL);
1291 }
1292
1293 // While leaving a recursion or iteration stack, insert its cells to the
1294 // recursion list in reverse order.
1295 if (rRecursionHelper.IsInReturn())
1296 {
1297 if (rRecursionHelper.GetRecursionCount() > 0 ||
1298 !rRecursionHelper.IsDoingRecursion())
1299 rRecursionHelper.Insert( this, bOldRunning, aResult);
1300 bool bIterationFromRecursion = false;
1301 bool bResumeIteration = false;
1302 do
1303 {
1304 if ((rRecursionHelper.IsInIterationReturn() &&
1305 rRecursionHelper.GetRecursionCount() == 0 &&
1306 !rRecursionHelper.IsDoingIteration()) ||
1307 bIterationFromRecursion || bResumeIteration)
1308 {
1309 ScFormulaCell* pIterCell = this; // scope for debug convenience
1310 bool & rDone = rRecursionHelper.GetConvergingReference();
1311 rDone = false;
1312 if (!bIterationFromRecursion && bResumeIteration)
1313 {
1314 bResumeIteration = false;
1315 // Resuming iteration expands the range.
1316 ScFormulaRecursionList::const_iterator aOldStart(
1317 rRecursionHelper.GetLastIterationStart());
1318 rRecursionHelper.ResumeIteration();
1319 // Mark new cells being in iteration.
1320 for (ScFormulaRecursionList::const_iterator aIter(
1321 rRecursionHelper.GetIterationStart()); aIter !=
1322 aOldStart; ++aIter)
1323 {
1324 pIterCell = (*aIter).pCell;
1325 pIterCell->bIsIterCell = sal_True;
1326 }
1327 // Mark older cells dirty again, in case they converted
1328 // without accounting for all remaining cells in the circle
1329 // that weren't touched so far, e.g. conditional. Restore
1330 // backuped result.
1331 sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1332 for (ScFormulaRecursionList::const_iterator aIter(
1333 aOldStart); aIter !=
1334 rRecursionHelper.GetIterationEnd(); ++aIter)
1335 {
1336 pIterCell = (*aIter).pCell;
1337 if (pIterCell->nSeenInIteration == nIteration)
1338 {
1339 if (!pIterCell->bDirty || aIter == aOldStart)
1340 {
1341 pIterCell->aResult = (*aIter).aPreviousResult;
1342 }
1343 --pIterCell->nSeenInIteration;
1344 }
1345 pIterCell->bDirty = sal_True;
1346 }
1347 }
1348 else
1349 {
1350 bResumeIteration = false;
1351 // Close circle once.
1352 rRecursionHelper.GetList().back().pCell->InterpretTail(
1353 SCITP_CLOSE_ITERATION_CIRCLE);
1354 // Start at 1, init things.
1355 rRecursionHelper.StartIteration();
1356 // Mark all cells being in iteration.
1357 for (ScFormulaRecursionList::const_iterator aIter(
1358 rRecursionHelper.GetIterationStart()); aIter !=
1359 rRecursionHelper.GetIterationEnd(); ++aIter)
1360 {
1361 pIterCell = (*aIter).pCell;
1362 pIterCell->bIsIterCell = sal_True;
1363 }
1364 }
1365 bIterationFromRecursion = false;
1366 sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
1367 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1368 rRecursionHelper.IncIteration())
1369 {
1370 rDone = true;
1371 for ( ScFormulaRecursionList::iterator aIter(
1372 rRecursionHelper.GetIterationStart()); aIter !=
1373 rRecursionHelper.GetIterationEnd() &&
1374 !rRecursionHelper.IsInReturn(); ++aIter)
1375 {
1376 pIterCell = (*aIter).pCell;
1377 if (pIterCell->IsDirtyOrInTableOpDirty() &&
1378 rRecursionHelper.GetIteration() !=
1379 pIterCell->GetSeenInIteration())
1380 {
1381 (*aIter).aPreviousResult = pIterCell->aResult;
1382 pIterCell->InterpretTail( SCITP_FROM_ITERATION);
1383 }
1384 rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
1385 }
1386 if (rRecursionHelper.IsInReturn())
1387 {
1388 bResumeIteration = true;
1389 break; // for
1390 // Don't increment iteration.
1391 }
1392 }
1393 if (!bResumeIteration)
1394 {
1395 if (rDone)
1396 {
1397 for (ScFormulaRecursionList::const_iterator aIter(
1398 rRecursionHelper.GetIterationStart());
1399 aIter != rRecursionHelper.GetIterationEnd();
1400 ++aIter)
1401 {
1402 pIterCell = (*aIter).pCell;
1403 pIterCell->bIsIterCell = sal_False;
1404 pIterCell->nSeenInIteration = 0;
1405 pIterCell->bRunning = (*aIter).bOldRunning;
1406 }
1407 }
1408 else
1409 {
1410 for (ScFormulaRecursionList::const_iterator aIter(
1411 rRecursionHelper.GetIterationStart());
1412 aIter != rRecursionHelper.GetIterationEnd();
1413 ++aIter)
1414 {
1415 pIterCell = (*aIter).pCell;
1416 pIterCell->bIsIterCell = sal_False;
1417 pIterCell->nSeenInIteration = 0;
1418 pIterCell->bRunning = (*aIter).bOldRunning;
1419 // If one cell didn't converge, all cells of this
1420 // circular dependency don't, no matter whether
1421 // single cells did.
1422 pIterCell->bDirty = sal_False;
1423 pIterCell->bTableOpDirty = sal_False;
1424 pIterCell->aResult.SetResultError( errNoConvergence);
1425 pIterCell->bChanged = sal_True;
1426 pIterCell->SetTextWidth( TEXTWIDTH_DIRTY);
1427 pIterCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN);
1428 }
1429 }
1430 // End this iteration and remove entries.
1431 rRecursionHelper.EndIteration();
1432 bResumeIteration = rRecursionHelper.IsDoingIteration();
1433 }
1434 }
1435 if (rRecursionHelper.IsInRecursionReturn() &&
1436 rRecursionHelper.GetRecursionCount() == 0 &&
1437 !rRecursionHelper.IsDoingRecursion())
1438 {
1439 bIterationFromRecursion = false;
1440 // Iterate over cells known so far, start with the last cell
1441 // encountered, inserting new cells if another recursion limit
1442 // is reached. Repeat until solved.
1443 rRecursionHelper.SetDoingRecursion( true);
1444 do
1445 {
1446 rRecursionHelper.SetInRecursionReturn( false);
1447 for (ScFormulaRecursionList::const_iterator aIter(
1448 rRecursionHelper.GetStart());
1449 !rRecursionHelper.IsInReturn() && aIter !=
1450 rRecursionHelper.GetEnd(); ++aIter)
1451 {
1452 ScFormulaCell* pCell = (*aIter).pCell;
1453 if (pCell->IsDirtyOrInTableOpDirty())
1454 {
1455 pCell->InterpretTail( SCITP_NORMAL);
1456 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1457 pCell->bRunning = (*aIter).bOldRunning;
1458 }
1459 }
1460 } while (rRecursionHelper.IsInRecursionReturn());
1461 rRecursionHelper.SetDoingRecursion( false);
1462 if (rRecursionHelper.IsInIterationReturn())
1463 {
1464 if (!bResumeIteration)
1465 bIterationFromRecursion = true;
1466 }
1467 else if (bResumeIteration ||
1468 rRecursionHelper.IsDoingIteration())
1469 rRecursionHelper.GetList().erase(
1470 rRecursionHelper.GetStart(),
1471 rRecursionHelper.GetLastIterationStart());
1472 else
1473 rRecursionHelper.Clear();
1474 }
1475 } while (bIterationFromRecursion || bResumeIteration);
1476 }
1477 }
1478
InterpretTail(ScInterpretTailParameter eTailParam)1479 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
1480 {
1481 class RecursionCounter
1482 {
1483 ScRecursionHelper& rRec;
1484 bool bStackedInIteration;
1485 public:
1486 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
1487 {
1488 bStackedInIteration = rRec.IsDoingIteration();
1489 if (bStackedInIteration)
1490 rRec.GetRecursionInIterationStack().push( p);
1491 rRec.IncRecursionCount();
1492 }
1493 ~RecursionCounter()
1494 {
1495 rRec.DecRecursionCount();
1496 if (bStackedInIteration)
1497 rRec.GetRecursionInIterationStack().pop();
1498 }
1499 } aRecursionCounter( pDocument->GetRecursionHelper(), this);
1500 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
1501 if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
1502 {
1503 // #i11719# no UPN and no error and no token code but result string present
1504 // => interpretation of this cell during name-compilation and unknown names
1505 // => can't exchange underlying code array in CompileTokenArray() /
1506 // Compile() because interpreter's token iterator would crash.
1507 // This should only be a temporary condition and, since we set an
1508 // error, if ran into it again we'd bump into the dirty-clearing
1509 // condition further down.
1510 if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1511 {
1512 pCode->SetCodeError( errNoCode );
1513 // This is worth an assertion; if encountered in daily work
1514 // documents we might need another solution. Or just confirm correctness.
1515 DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but string" );
1516 return;
1517 }
1518 CompileTokenArray();
1519 }
1520
1521 if( pCode->GetCodeLen() && pDocument )
1522 {
1523 class StackCleaner
1524 {
1525 ScDocument* pDoc;
1526 ScInterpreter* pInt;
1527 public:
1528 StackCleaner( ScDocument* pD, ScInterpreter* pI )
1529 : pDoc(pD), pInt(pI)
1530 {}
1531 ~StackCleaner()
1532 {
1533 delete pInt;
1534 pDoc->DecInterpretLevel();
1535 }
1536 };
1537 pDocument->IncInterpretLevel();
1538 ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
1539 StackCleaner aStackCleaner( pDocument, p);
1540 sal_uInt16 nOldErrCode = aResult.GetResultError();
1541 if ( nSeenInIteration == 0 )
1542 { // Only the first time
1543 // With bChanged=sal_False, if a newly compiled cell has a result of
1544 // 0.0, no change is detected and the cell will not be repainted.
1545 // bChanged = sal_False;
1546 aResult.SetResultError( 0 );
1547 }
1548
1549 switch ( aResult.GetResultError() )
1550 {
1551 case errCircularReference : // will be determined again if so
1552 aResult.SetResultError( 0 );
1553 break;
1554 }
1555
1556 sal_Bool bOldRunning = bRunning;
1557 bRunning = sal_True;
1558 p->Interpret();
1559 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
1560 {
1561 if (nSeenInIteration > 0)
1562 --nSeenInIteration; // retry when iteration is resumed
1563 return;
1564 }
1565 bRunning = bOldRunning;
1566 //-> i120962: If the cell was applied reference formula, get the valid token
1567 if (pValidRefToken)
1568 DELETEZ(pValidRefToken);
1569 if (p->IsReferenceFunc() && p->GetLastStackRefToken())
1570 pValidRefToken = static_cast<ScToken*>(p->GetLastStackRefToken()->Clone());
1571 //<- i120962
1572
1573 // #i102616# For single-sheet saving consider only content changes, not format type,
1574 // because format type isn't set on loading (might be changed later)
1575 sal_Bool bContentChanged = sal_False;
1576
1577 // Do not create a HyperLink() cell if the formula results in an error.
1578 if( p->GetError() && pCode->IsHyperLink())
1579 pCode->SetHyperLink(sal_False);
1580
1581 if( p->GetError() && p->GetError() != errCircularReference)
1582 {
1583 bDirty = sal_False;
1584 bTableOpDirty = sal_False;
1585 bChanged = sal_True;
1586 }
1587 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1588 {
1589 bool bIsValue = aResult.IsValue(); // the previous type
1590 // Did it converge?
1591 if ((bIsValue && p->GetResultType() == svDouble && fabs(
1592 p->GetNumResult() - aResult.GetDouble()) <=
1593 pDocument->GetDocOptions().GetIterEps()) ||
1594 (!bIsValue && p->GetResultType() == svString &&
1595 p->GetStringResult() == aResult.GetString()))
1596 {
1597 // A convergence in the first iteration doesn't necessarily
1598 // mean that it's done, it may be because not all related cells
1599 // of a circle changed their values yet. If the set really
1600 // converges it will do so also during the next iteration. This
1601 // fixes situations like of #i44115#. If this wasn't wanted an
1602 // initial "uncalculated" value would be needed for all cells
1603 // of a circular dependency => graph needed before calculation.
1604 if (nSeenInIteration > 1 ||
1605 pDocument->GetDocOptions().GetIterCount() == 1)
1606 {
1607 bDirty = sal_False;
1608 bTableOpDirty = sal_False;
1609 }
1610 }
1611 }
1612
1613 // New error code?
1614 if( p->GetError() != nOldErrCode )
1615 {
1616 bChanged = sal_True;
1617 // bContentChanged only has to be set if the file content would be changed
1618 if ( aResult.GetCellResultType() != svUnknown )
1619 bContentChanged = sal_True;
1620 }
1621 // Different number format?
1622 if( nFormatType != p->GetRetFormatType() )
1623 {
1624 nFormatType = p->GetRetFormatType();
1625 bChanged = sal_True;
1626 }
1627 if( nFormatIndex != p->GetRetFormatIndex() )
1628 {
1629 nFormatIndex = p->GetRetFormatIndex();
1630 bChanged = sal_True;
1631 }
1632
1633 // In case of changes just obtain the result, no temporary and
1634 // comparison needed anymore.
1635 if (bChanged)
1636 {
1637 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
1638 // Also handle special cases of initial results after loading.
1639
1640 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1641 {
1642 ScFormulaResult aNewResult( p->GetResultToken());
1643 StackVar eOld = aResult.GetCellResultType();
1644 StackVar eNew = aNewResult.GetCellResultType();
1645 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
1646 {
1647 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
1648 // -> no change
1649 }
1650 else
1651 {
1652 if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
1653 eOld = svString; // ScHybridCellToken has a valid GetString method
1654
1655 // #i106045# use approxEqual to compare with stored value
1656 bContentChanged = (eOld != eNew ||
1657 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
1658 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1659 }
1660 }
1661
1662 aResult.SetToken( p->GetResultToken() );
1663 }
1664 else
1665 {
1666 ScFormulaResult aNewResult( p->GetResultToken());
1667 StackVar eOld = aResult.GetCellResultType();
1668 StackVar eNew = aNewResult.GetCellResultType();
1669 bChanged = (eOld != eNew ||
1670 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
1671 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1672
1673 // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged)
1674 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1675 {
1676 if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) ||
1677 ( eOld == svHybridCell && eNew == svString && aResult.GetString() == aNewResult.GetString() ) ||
1678 ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) )
1679 {
1680 // no change, see above
1681 }
1682 else
1683 bContentChanged = sal_True;
1684 }
1685
1686 aResult.Assign( aNewResult);
1687 }
1688
1689 // Precision as shown?
1690 if ( aResult.IsValue() && !p->GetError()
1691 && pDocument->GetDocOptions().IsCalcAsShown()
1692 && nFormatType != NUMBERFORMAT_DATE
1693 && nFormatType != NUMBERFORMAT_TIME
1694 && nFormatType != NUMBERFORMAT_DATETIME )
1695 {
1696 sal_uLong nFormat = pDocument->GetNumberFormat( aPos );
1697 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1698 nFormat = nFormatIndex;
1699 if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1700 nFormat = ScGlobal::GetStandardFormat(
1701 *pDocument->GetFormatTable(), nFormat, nFormatType );
1702 aResult.SetDouble( pDocument->RoundValueAsShown(
1703 aResult.GetDouble(), nFormat));
1704 }
1705 if (eTailParam == SCITP_NORMAL)
1706 {
1707 bDirty = sal_False;
1708 bTableOpDirty = sal_False;
1709 }
1710 if( aResult.GetMatrix().Is() )
1711 {
1712 // If the formula wasn't entered as a matrix formula, live on with
1713 // the upper left corner and let reference counting delete the matrix.
1714 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
1715 aResult.SetToken( aResult.GetCellResultToken());
1716 }
1717 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1718 {
1719 // Coded double error may occur via filter import.
1720 sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble());
1721 aResult.SetResultError( nErr);
1722 bChanged = bContentChanged = true;
1723 }
1724 if( bChanged )
1725 {
1726 SetTextWidth( TEXTWIDTH_DIRTY );
1727 SetScriptType( SC_SCRIPTTYPE_UNKNOWN );
1728 }
1729 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
1730 {
1731 // pass bIgnoreLock=sal_True, because even if called from pending row height update,
1732 // a changed result must still reset the stream flag
1733 pDocument->SetStreamValid(aPos.Tab(), sal_False, sal_True);
1734 }
1735 if ( !pCode->IsRecalcModeAlways() )
1736 pDocument->RemoveFromFormulaTree( this );
1737
1738 // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten)
1739
1740 if ( pCode->IsRecalcModeForced() )
1741 {
1742 sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
1743 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
1744 if ( nValidation )
1745 {
1746 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
1747 if ( pData && !pData->IsDataValid( this, aPos ) )
1748 pData->DoCalcError( this );
1749 }
1750 }
1751
1752 // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren
1753 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
1754 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
1755 }
1756 else
1757 {
1758 // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen
1759 DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" );
1760 bDirty = sal_False;
1761 bTableOpDirty = sal_False;
1762 }
1763 }
1764
1765
SetMatColsRows(SCCOL nCols,SCROW nRows)1766 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
1767 {
1768 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
1769 if (pMat)
1770 pMat->SetMatColsRows( nCols, nRows);
1771 else if (nCols || nRows)
1772 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
1773 }
1774
1775
GetMatColsRows(SCCOL & nCols,SCROW & nRows) const1776 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
1777 {
1778 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
1779 if (pMat)
1780 pMat->GetMatColsRows( nCols, nRows);
1781 else
1782 {
1783 nCols = 0;
1784 nRows = 0;
1785 }
1786 }
1787
1788
GetStandardFormat(SvNumberFormatter & rFormatter,sal_uLong nFormat) const1789 sal_uLong ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, sal_uLong nFormat ) const
1790 {
1791 if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1792 return nFormatIndex;
1793 //! not ScFormulaCell::IsValue(), that could reinterpret the formula again.
1794 if ( aResult.IsValue() )
1795 return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType );
1796 else
1797 return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType );
1798 }
1799
1800
Notify(SvtBroadcaster &,const SfxHint & rHint)1801 void __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
1802 {
1803 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
1804 {
1805 const ScHint* p = PTR_CAST( ScHint, &rHint );
1806 sal_uLong nHint = (p ? p->GetId() : 0);
1807 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY))
1808 {
1809 sal_Bool bForceTrack = sal_False;
1810 if ( nHint & SC_HINT_TABLEOPDIRTY )
1811 {
1812 bForceTrack = !bTableOpDirty;
1813 if ( !bTableOpDirty )
1814 {
1815 pDocument->AddTableOpFormulaCell( this );
1816 bTableOpDirty = sal_True;
1817 }
1818 }
1819 else
1820 {
1821 bForceTrack = !bDirty;
1822 bDirty = sal_True;
1823 }
1824 // #35962# Don't remove from FormulaTree to put in FormulaTrack to
1825 // put in FormulaTree again and again, only if necessary.
1826 // Any other means except RECALCMODE_ALWAYS by which a cell could
1827 // be in FormulaTree if it would notify other cells through
1828 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
1829 // #87866# Yes. The new TableOpDirty made it necessary to have a
1830 // forced mode where formulas may still be in FormulaTree from
1831 // TableOpDirty but have to notify dependents for normal dirty.
1832 if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
1833 || pCode->IsRecalcModeAlways())
1834 && !pDocument->IsInFormulaTrack( this ) )
1835 pDocument->AppendToFormulaTrack( this );
1836 }
1837 }
1838 }
1839
SetDirty()1840 void ScFormulaCell::SetDirty()
1841 {
1842 if ( !IsInChangeTrack() )
1843 {
1844 if ( pDocument->GetHardRecalcState() )
1845 bDirty = sal_True;
1846 else
1847 {
1848 // Mehrfach-FormulaTracking in Load und in CompileAll
1849 // nach CopyScenario und CopyBlockFromClip vermeiden.
1850 // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=sal_False
1851 // setzen, z.B. in CompileTokenArray
1852 if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
1853 {
1854 bDirty = sal_True;
1855 pDocument->AppendToFormulaTrack( this );
1856 pDocument->TrackFormulas();
1857 }
1858 }
1859
1860 if (pDocument->IsStreamValid(aPos.Tab()))
1861 pDocument->SetStreamValid(aPos.Tab(), sal_False);
1862 }
1863 }
1864
SetDirtyAfterLoad()1865 void ScFormulaCell::SetDirtyAfterLoad()
1866 {
1867 bDirty = sal_True;
1868 if ( !pDocument->GetHardRecalcState() )
1869 pDocument->PutInFormulaTree( this );
1870 }
1871
SetTableOpDirty()1872 void ScFormulaCell::SetTableOpDirty()
1873 {
1874 if ( !IsInChangeTrack() )
1875 {
1876 if ( pDocument->GetHardRecalcState() )
1877 bTableOpDirty = sal_True;
1878 else
1879 {
1880 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
1881 {
1882 if ( !bTableOpDirty )
1883 {
1884 pDocument->AddTableOpFormulaCell( this );
1885 bTableOpDirty = sal_True;
1886 }
1887 pDocument->AppendToFormulaTrack( this );
1888 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
1889 }
1890 }
1891 }
1892 }
1893
1894
IsDirtyOrInTableOpDirty() const1895 sal_Bool ScFormulaCell::IsDirtyOrInTableOpDirty() const
1896 {
1897 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
1898 }
1899
1900
SetErrCode(sal_uInt16 n)1901 void ScFormulaCell::SetErrCode( sal_uInt16 n )
1902 {
1903 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
1904 * used whether it is solely for transport of a simple result error and get
1905 * rid of that abuse. */
1906 pCode->SetCodeError( n );
1907 // Hard set errors are transported as result type value per convention,
1908 // e.g. via clipboard. ScFormulaResult::IsValue() and
1909 // ScFormulaResult::GetDouble() handle that.
1910 aResult.SetResultError( n );
1911 }
1912
AddRecalcMode(ScRecalcMode nBits)1913 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
1914 {
1915 if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL )
1916 bDirty = sal_True;
1917 if ( nBits & RECALCMODE_ONLOAD_ONCE )
1918 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
1919 nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL;
1920 }
1921 pCode->AddRecalcMode( nBits );
1922 }
1923
1924 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
GetURLResult(String & rURL,String & rCellText)1925 void ScFormulaCell::GetURLResult( String& rURL, String& rCellText )
1926 {
1927 String aCellString;
1928
1929 Color* pColor;
1930
1931 // Cell Text uses the Cell format while the URL uses
1932 // the default format for the type.
1933 sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos );
1934 SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
1935
1936 if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 )
1937 nCellFormat = GetStandardFormat( *pFormatter,nCellFormat );
1938
1939 sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER);
1940
1941 if ( IsValue() )
1942 {
1943 double fValue = GetValue();
1944 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
1945 }
1946 else
1947 {
1948 GetString( aCellString );
1949 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
1950 }
1951 ScConstMatrixRef xMat( aResult.GetMatrix());
1952 if (xMat)
1953 {
1954 ScMatValType nMatValType;
1955 // determine if the matrix result is a string or value.
1956 const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType);
1957 if (pMatVal)
1958 {
1959 if (!ScMatrix::IsValueType( nMatValType))
1960 rURL = pMatVal->GetString();
1961 else
1962 pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor );
1963 }
1964 }
1965
1966 if(!rURL.Len())
1967 {
1968 if(IsValue())
1969 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
1970 else
1971 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
1972 }
1973 }
1974
IsMultilineResult()1975 bool ScFormulaCell::IsMultilineResult()
1976 {
1977 if (!IsValue())
1978 return aResult.IsMultiline();
1979 return false;
1980 }
1981
CreateURLObject()1982 EditTextObject* ScFormulaCell::CreateURLObject()
1983 {
1984 String aCellText;
1985 String aURL;
1986 GetURLResult( aURL, aCellText );
1987
1988 SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT);
1989 EditEngine& rEE = pDocument->GetEditEngine();
1990 rEE.SetText( EMPTY_STRING );
1991 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) );
1992
1993 return rEE.CreateTextObject();
1994 }
1995
1996 // ============================================================================
1997
ScDetectiveRefIter(ScFormulaCell * pCell)1998 ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell )
1999 {
2000 pCode = pCell->GetCode();
2001 pCode->Reset();
2002 aPos = pCell->aPos;
2003 }
2004
lcl_ScDetectiveRefIter_SkipRef(ScToken * p)2005 sal_Bool lcl_ScDetectiveRefIter_SkipRef( ScToken* p )
2006 {
2007 ScSingleRefData& rRef1 = p->GetSingleRef();
2008 if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted()
2009 || !rRef1.Valid() )
2010 return sal_True;
2011 if ( p->GetType() == svDoubleRef )
2012 {
2013 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
2014 if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted()
2015 || !rRef2.Valid() )
2016 return sal_True;
2017 }
2018 return sal_False;
2019 }
2020
GetNextRef(ScRange & rRange)2021 sal_Bool ScDetectiveRefIter::GetNextRef( ScRange& rRange )
2022 {
2023 sal_Bool bRet = sal_False;
2024
2025 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2026 if (p)
2027 p->CalcAbsIfRel( aPos );
2028
2029 while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) )
2030 {
2031 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2032 if (p)
2033 p->CalcAbsIfRel( aPos );
2034 }
2035
2036 if( p )
2037 {
2038 SingleDoubleRefProvider aProv( *p );
2039 rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab );
2040 rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab );
2041 bRet = sal_True;
2042 }
2043
2044 return bRet;
2045 }
2046
2047 // ============================================================================
2048