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