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 "formulacell.hxx"
21 :
22 : #include "compiler.hxx"
23 : #include "document.hxx"
24 : #include "globalnames.hxx"
25 : #include "cellvalue.hxx"
26 : #include "interpre.hxx"
27 : #include "macromgr.hxx"
28 : #include "refupdat.hxx"
29 : #include "recursionhelper.hxx"
30 : #include "docoptio.hxx"
31 : #include "rangenam.hxx"
32 : #include "dbdata.hxx"
33 : #include "progress.hxx"
34 : #include "scmatrix.hxx"
35 : #include "rechead.hxx"
36 : #include "scitems.hxx"
37 : #include "validat.hxx"
38 : #include "editutil.hxx"
39 : #include "chgtrack.hxx"
40 : #include "tokenarray.hxx"
41 :
42 : #include "formula/errorcodes.hxx"
43 : #include "formula/vectortoken.hxx"
44 : #include "svl/intitem.hxx"
45 : #include "rtl/strbuf.hxx"
46 : #include "formulagroup.hxx"
47 : #include "listenercontext.hxx"
48 : #include "types.hxx"
49 : #include "scopetools.hxx"
50 :
51 : #include <boost/bind.hpp>
52 : #include <boost/scoped_ptr.hpp>
53 :
54 : using namespace formula;
55 :
56 : #ifdef USE_MEMPOOL
57 : IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell )
58 : #endif
59 :
60 : namespace {
61 :
62 : // More or less arbitrary, of course all recursions must fit into available
63 : // stack space (which is what on all systems we don't know yet?). Choosing a
64 : // lower value may be better than trying a much higher value that also isn't
65 : // sufficient but temporarily leads to high memory consumption. On the other
66 : // hand, if the value fits all recursions, execution is quicker as no resumes
67 : // are necessary. Could be made a configurable option.
68 : // Allow for a year's calendar (366).
69 : const sal_uInt16 MAXRECURSION = 400;
70 :
71 : using std::deque;
72 :
73 : typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&);
74 :
75 :
76 0 : static SCCOLROW lcl_GetCol(const ScSingleRefData& rData)
77 : {
78 0 : return rData.nCol;
79 : }
80 :
81 :
82 0 : static SCCOLROW lcl_GetRow(const ScSingleRefData& rData)
83 : {
84 0 : return rData.nRow;
85 : }
86 :
87 :
88 0 : static SCCOLROW lcl_GetTab(const ScSingleRefData& rData)
89 : {
90 0 : return rData.nTab;
91 : }
92 :
93 :
94 : /** Check if both references span the same range in selected dimension.
95 : */
96 : static bool
97 0 : lcl_checkRangeDimension(
98 : const SingleDoubleRefProvider& rRef1,
99 : const SingleDoubleRefProvider& rRef2,
100 : const DimensionSelector aWhich)
101 : {
102 : return
103 0 : aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1)
104 0 : && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2);
105 : }
106 :
107 :
108 : static bool
109 0 : lcl_checkRangeDimensions(
110 : const SingleDoubleRefProvider& rRef1,
111 : const SingleDoubleRefProvider& rRef2,
112 : bool& bCol, bool& bRow, bool& bTab)
113 : {
114 0 : const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol));
115 0 : const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow));
116 0 : const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab));
117 :
118 : // Test if exactly two dimensions are equal
119 0 : if (!(bSameCols ^ bSameRows ^ bSameTabs)
120 0 : && (bSameCols || bSameRows || bSameTabs))
121 : {
122 0 : bCol = !bSameCols;
123 0 : bRow = !bSameRows;
124 0 : bTab = !bSameTabs;
125 0 : return true;
126 : }
127 0 : return false;
128 : }
129 :
130 :
131 : /** Check if references in given reference list can possibly
132 : form a range. To do that, two of their dimensions must be the same.
133 : */
134 : static bool
135 0 : lcl_checkRangeDimensions(
136 : const deque<ScToken*>::const_iterator aBegin,
137 : const deque<ScToken*>::const_iterator aEnd,
138 : bool& bCol, bool& bRow, bool& bTab)
139 : {
140 0 : deque<ScToken*>::const_iterator aCur(aBegin);
141 0 : ++aCur;
142 0 : const SingleDoubleRefProvider aRef(**aBegin);
143 0 : bool bOk(false);
144 : {
145 0 : const SingleDoubleRefProvider aRefCur(**aCur);
146 0 : bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab);
147 : }
148 0 : while (bOk && aCur != aEnd)
149 : {
150 0 : const SingleDoubleRefProvider aRefCur(**aCur);
151 0 : bool bColTmp(false);
152 0 : bool bRowTmp(false);
153 0 : bool bTabTmp(false);
154 0 : bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
155 0 : bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
156 0 : ++aCur;
157 0 : }
158 :
159 0 : if (bOk && aCur == aEnd)
160 : {
161 0 : return true;
162 : }
163 0 : return false;
164 : }
165 :
166 :
167 : bool
168 0 : lcl_lessReferenceBy(
169 : const ScToken* const pRef1, const ScToken* const pRef2,
170 : const DimensionSelector aWhich)
171 : {
172 0 : const SingleDoubleRefProvider rRef1(*pRef1);
173 0 : const SingleDoubleRefProvider rRef2(*pRef2);
174 0 : return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1);
175 : }
176 :
177 :
178 : /** Returns true if range denoted by token pRef2 starts immediately after
179 : range denoted by token pRef1. Dimension, in which the comparison takes
180 : place, is given by aWhich.
181 : */
182 : bool
183 0 : lcl_isImmediatelyFollowing(
184 : const ScToken* const pRef1, const ScToken* const pRef2,
185 : const DimensionSelector aWhich)
186 : {
187 0 : const SingleDoubleRefProvider rRef1(*pRef1);
188 0 : const SingleDoubleRefProvider rRef2(*pRef2);
189 0 : return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1;
190 : }
191 :
192 :
193 : static bool
194 0 : lcl_checkIfAdjacent(
195 : const deque<ScToken*>& rReferences,
196 : const DimensionSelector aWhich)
197 : {
198 : typedef deque<ScToken*>::const_iterator Iter;
199 0 : Iter aBegin(rReferences.begin());
200 0 : Iter aEnd(rReferences.end());
201 0 : Iter aBegin1(aBegin);
202 0 : ++aBegin1, --aEnd;
203 : return std::equal(
204 : aBegin, aEnd, aBegin1,
205 0 : boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich));
206 : }
207 :
208 :
209 : static void
210 0 : lcl_fillRangeFromRefList(
211 : const deque<ScToken*>& rReferences, ScRange& rRange)
212 : {
213 : const ScSingleRefData aStart(
214 0 : SingleDoubleRefProvider(*rReferences.front()).Ref1);
215 0 : rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab);
216 : const ScSingleRefData aEnd(
217 0 : SingleDoubleRefProvider(*rReferences.back()).Ref2);
218 0 : rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab);
219 0 : }
220 :
221 :
222 : static bool
223 0 : lcl_refListFormsOneRange(
224 : const ScAddress& aPos, deque<ScToken*>& rReferences,
225 : ScRange& rRange)
226 : {
227 : std::for_each(
228 : rReferences.begin(), rReferences.end(),
229 : bind(&ScToken::CalcAbsIfRel, _1, aPos))
230 0 : ;
231 0 : if (rReferences.size() == 1) {
232 0 : lcl_fillRangeFromRefList(rReferences, rRange);
233 0 : return true;
234 : }
235 :
236 0 : bool bCell(false);
237 0 : bool bRow(false);
238 0 : bool bTab(false);
239 0 : if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(),
240 0 : bCell, bRow, bTab))
241 : {
242 : DimensionSelector aWhich;
243 0 : if (bCell)
244 : {
245 0 : aWhich = lcl_GetCol;
246 : }
247 0 : else if (bRow)
248 : {
249 0 : aWhich = lcl_GetRow;
250 : }
251 0 : else if (bTab)
252 : {
253 0 : aWhich = lcl_GetTab;
254 : }
255 : else
256 : {
257 : OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
258 0 : aWhich = lcl_GetRow; // initialize to avoid warning
259 : }
260 : // Sort the references by start of range
261 : std::sort(rReferences.begin(), rReferences.end(),
262 0 : boost::bind(lcl_lessReferenceBy, _1, _2, aWhich));
263 0 : if (lcl_checkIfAdjacent(rReferences, aWhich))
264 : {
265 0 : lcl_fillRangeFromRefList(rReferences, rRange);
266 0 : return true;
267 : }
268 : }
269 0 : return false;
270 : }
271 :
272 :
273 0 : bool lcl_isReference(const FormulaToken& rToken)
274 : {
275 : return
276 0 : rToken.GetType() == svSingleRef ||
277 0 : rToken.GetType() == svDoubleRef;
278 : }
279 :
280 10 : void adjustRangeName(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc, const ScAddress& aNewPos, const ScAddress& aOldPos)
281 : {
282 10 : bool bOldGlobal = pToken->IsGlobal();
283 10 : SCTAB aOldTab = aOldPos.Tab();
284 10 : OUString aRangeName;
285 10 : int nOldIndex = pToken->GetIndex();
286 10 : ScRangeData* pOldRangeData = NULL;
287 :
288 : //search the name of the RangeName
289 10 : if (!bOldGlobal)
290 : {
291 3 : pOldRangeData = pOldDoc->GetRangeName(aOldTab)->findByIndex(nOldIndex);
292 3 : if (!pOldRangeData)
293 0 : return; //might be an error in the formula array
294 3 : aRangeName = pOldRangeData->GetUpperName();
295 : }
296 : else
297 : {
298 7 : pOldRangeData = pOldDoc->GetRangeName()->findByIndex(nOldIndex);
299 7 : if (!pOldRangeData)
300 0 : return; //might be an error in the formula array
301 7 : aRangeName = pOldRangeData->GetUpperName();
302 : }
303 :
304 : //find corresponding range name in new document
305 : //first search for local range name then global range names
306 10 : SCTAB aNewTab = aNewPos.Tab();
307 10 : ScRangeName* pRangeName = rNewDoc.GetRangeName(aNewTab);
308 10 : ScRangeData* pRangeData = NULL;
309 10 : bool bNewGlobal = false;
310 : //search local range names
311 10 : if (pRangeName)
312 : {
313 10 : pRangeData = pRangeName->findByUpperName(aRangeName);
314 : }
315 : //search global range names
316 10 : if (!pRangeData)
317 : {
318 9 : bNewGlobal = true;
319 9 : pRangeName = rNewDoc.GetRangeName();
320 9 : if (pRangeName)
321 9 : pRangeData = pRangeName->findByUpperName(aRangeName);
322 : }
323 : //if no range name was found copy it
324 10 : if (!pRangeData)
325 : {
326 5 : bNewGlobal = bOldGlobal;
327 5 : pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc);
328 5 : ScTokenArray* pRangeNameToken = pRangeData->GetCode();
329 5 : if (rNewDoc.GetPool() != const_cast<ScDocument*>(pOldDoc)->GetPool())
330 : {
331 2 : pRangeNameToken->ReadjustAbsolute3DReferences(pOldDoc, &rNewDoc, pRangeData->GetPos(), true);
332 2 : pRangeNameToken->AdjustAbsoluteRefs(pOldDoc, aOldPos, aNewPos, false, true);
333 : }
334 :
335 : bool bInserted;
336 5 : if (bNewGlobal)
337 3 : bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
338 : else
339 2 : bInserted = rNewDoc.GetRangeName(aNewTab)->insert(pRangeData);
340 5 : if (!bInserted)
341 : {
342 : //if this happened we have a real problem
343 0 : pRangeData = NULL;
344 0 : pToken->SetIndex(0);
345 : OSL_FAIL("inserting the range name should not fail");
346 0 : return;
347 : }
348 : }
349 10 : sal_Int32 nIndex = pRangeData->GetIndex();
350 10 : pToken->SetIndex(nIndex);
351 10 : pToken->SetGlobal(bNewGlobal);
352 : }
353 :
354 0 : void adjustDBRange(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
355 : {
356 0 : ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
357 0 : if (!pOldDBCollection)
358 0 : return;//strange error case, don't do anything
359 0 : ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
360 0 : ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
361 0 : if (!pDBData)
362 0 : return; //invalid index
363 0 : OUString aDBName = pDBData->GetUpperName();
364 :
365 : //search in new document
366 0 : ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
367 0 : if (!pNewDBCollection)
368 : {
369 0 : pNewDBCollection = new ScDBCollection(&rNewDoc);
370 0 : rNewDoc.SetDBCollection(pNewDBCollection);
371 : }
372 0 : ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
373 0 : ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
374 0 : if (!pNewDBData)
375 : {
376 0 : pNewDBData = new ScDBData(*pDBData);
377 0 : aNewNamedDBs.insert(pNewDBData);
378 : }
379 0 : pToken->SetIndex(pNewDBData->GetIndex());
380 : }
381 :
382 : }
383 :
384 499 : ScFormulaCellGroup::ScFormulaCellGroup() :
385 : mnRefCount(0),
386 : mnStart(0),
387 : mnLength(0),
388 : mbInvariant(false),
389 499 : meCalcState(sc::GroupCalcEnabled)
390 : {
391 499 : }
392 :
393 484 : ScFormulaCellGroup::~ScFormulaCellGroup()
394 : {
395 484 : }
396 :
397 : // ============================================================================
398 :
399 659 : ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
400 : const OUString& rFormula,
401 : const FormulaGrammar::Grammar eGrammar,
402 : sal_uInt8 cMatInd ) :
403 : eTempGrammar( eGrammar),
404 : pCode( NULL ),
405 : pDocument( pDoc ),
406 : pPrevious(0),
407 : pNext(0),
408 : pPreviousTrack(0),
409 : pNextTrack(0),
410 : nSeenInIteration(0),
411 : cMatrixFlag ( cMatInd ),
412 : nFormatType ( NUMBERFORMAT_NUMBER ),
413 : bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
414 : bChanged( false ),
415 : bRunning( false ),
416 : bCompile( false ),
417 : bSubTotal( false ),
418 : bIsIterCell( false ),
419 : bInChangeTrack( false ),
420 : bTableOpDirty( false ),
421 : bNeedListening( false ),
422 : mbNeedsNumberFormat( false ),
423 659 : aPos( rPos )
424 : {
425 659 : Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
426 659 : if (!pCode)
427 : // We need to have a non-NULL token array instance at all times.
428 0 : pCode = new ScTokenArray;
429 659 : }
430 :
431 : // Used by import filters
432 :
433 3963 : ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
434 : const ScTokenArray* pArr,
435 : const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) :
436 : eTempGrammar( eGrammar),
437 3963 : pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ),
438 : pDocument( pDoc ),
439 : pPrevious(0),
440 : pNext(0),
441 : pPreviousTrack(0),
442 : pNextTrack(0),
443 : nSeenInIteration(0),
444 : cMatrixFlag ( cInd ),
445 : nFormatType ( NUMBERFORMAT_NUMBER ),
446 3963 : bDirty( NULL != pArr ), // -> Because of the use of the Auto Pilot Function was: cInd != 0
447 : bChanged( false ),
448 : bRunning( false ),
449 : bCompile( false ),
450 : bSubTotal( false ),
451 : bIsIterCell( false ),
452 : bInChangeTrack( false ),
453 : bTableOpDirty( false ),
454 : bNeedListening( false ),
455 : mbNeedsNumberFormat( false ),
456 11889 : aPos( rPos )
457 : {
458 : // UPN-Array generation
459 3963 : if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
460 : {
461 3939 : ScCompiler aComp( pDocument, aPos, *pCode);
462 3939 : aComp.SetGrammar(eTempGrammar);
463 3939 : bSubTotal = aComp.CompileTokenArray();
464 3939 : nFormatType = aComp.GetNumFormatType();
465 : }
466 : else
467 : {
468 24 : pCode->Reset();
469 24 : if ( pCode->GetNextOpCodeRPN( ocSubTotal ) )
470 0 : bSubTotal = true;
471 : }
472 :
473 3963 : if (bSubTotal)
474 6 : pDocument->AddSubTotalCell(this);
475 :
476 3963 : pCode->GenHash();
477 3963 : }
478 :
479 786 : ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
480 : SvtListener(),
481 : aResult( rCell.aResult ),
482 : eTempGrammar( rCell.eTempGrammar),
483 : pDocument( &rDoc ),
484 : pPrevious(0),
485 : pNext(0),
486 : pPreviousTrack(0),
487 : pNextTrack(0),
488 : nSeenInIteration(0),
489 : cMatrixFlag ( rCell.cMatrixFlag ),
490 : nFormatType( rCell.nFormatType ),
491 : bDirty( rCell.bDirty ),
492 : bChanged( rCell.bChanged ),
493 : bRunning( false ),
494 : bCompile( rCell.bCompile ),
495 : bSubTotal( rCell.bSubTotal ),
496 : bIsIterCell( false ),
497 : bInChangeTrack( false ),
498 : bTableOpDirty( false ),
499 : bNeedListening( false ),
500 : mbNeedsNumberFormat( false ),
501 786 : aPos( rPos )
502 : {
503 786 : pCode = rCell.pCode->Clone();
504 :
505 : // set back any errors and recompile
506 : // not in the Clipboard - it must keep the received error flag
507 : // Special Length=0: as bad cells are generated, then they are also retained
508 786 : if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
509 : {
510 0 : pCode->SetCodeError( 0 );
511 0 : bCompile = true;
512 : }
513 : //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
514 786 : bool bCompileLater = false;
515 786 : bool bClipMode = rCell.pDocument->IsClipboard();
516 :
517 : //update ScNameTokens
518 786 : if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
519 : {
520 764 : if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
521 : {
522 764 : ScToken* pToken = NULL;
523 1538 : while((pToken = static_cast<ScToken*>(pCode->GetNextName()))!= NULL)
524 : {
525 10 : OpCode eOpCode = pToken->GetOpCode();
526 10 : if (eOpCode == ocName)
527 10 : adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos);
528 0 : else if (eOpCode == ocDBArea)
529 0 : adjustDBRange(pToken, rDoc, rCell.pDocument);
530 : }
531 : }
532 :
533 764 : bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
534 764 : if (bCopyBetweenDocs && !(nCloneFlags & SC_CLONECELL_NOMAKEABS_EXTERNAL))
535 : {
536 11 : pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
537 : }
538 :
539 764 : pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, false, bCopyBetweenDocs );
540 : }
541 :
542 786 : if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
543 0 : pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
544 :
545 786 : if( !bCompile )
546 : { // Name references with references and ColRowNames
547 784 : pCode->Reset();
548 : ScToken* t;
549 2135 : while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
550 : {
551 567 : if ( t->IsExternalRef() )
552 : {
553 : // External name, cell, and area references.
554 3 : bCompile = true;
555 : }
556 564 : else if ( t->GetType() == svIndex )
557 : {
558 6 : ScRangeData* pRangeData = rDoc.GetRangeName()->findByIndex( t->GetIndex() );
559 6 : if( pRangeData )
560 : {
561 6 : if( pRangeData->HasReferences() )
562 6 : bCompile = true;
563 : }
564 : else
565 0 : bCompile = true; // invalid reference!
566 : }
567 558 : else if ( t->GetOpCode() == ocColRowName )
568 : {
569 0 : bCompile = true; // new lookup needed
570 0 : bCompileLater = bClipMode;
571 : }
572 : }
573 : }
574 786 : if( bCompile )
575 : {
576 11 : if ( !bCompileLater && bClipMode )
577 : {
578 : // Merging ranges needs the actual positions after UpdateReference.
579 : // ColRowNames need new lookup after positions are adjusted.
580 4 : bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName);
581 : }
582 11 : if ( !bCompileLater )
583 : {
584 : // bNoListening, not at all if in Clipboard/Undo,
585 : // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
586 11 : CompileTokenArray( true );
587 : }
588 : }
589 :
590 786 : if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
591 30 : StartListeningTo( &rDoc );
592 :
593 786 : if (bSubTotal)
594 6 : pDocument->AddSubTotalCell(this);
595 786 : }
596 :
597 15309 : ScFormulaCell::~ScFormulaCell()
598 : {
599 5104 : pDocument->RemoveFromFormulaTree( this );
600 5104 : pDocument->RemoveSubTotalCell(this);
601 5104 : if (pCode->HasOpCode(ocMacro))
602 1 : pDocument->GetMacroManager()->RemoveDependentCell(this);
603 :
604 5104 : if (pDocument->HasExternalRefManager())
605 1764 : pDocument->GetExternalRefManager()->removeRefCell(this);
606 :
607 5104 : delete pCode;
608 10205 : }
609 :
610 560 : ScFormulaCell* ScFormulaCell::Clone() const
611 : {
612 560 : return new ScFormulaCell(*this, *pDocument, aPos);
613 : }
614 :
615 4790 : size_t ScFormulaCell::GetHash() const
616 : {
617 4790 : return pCode->GetHash();
618 : }
619 :
620 6 : ScFormulaVectorState ScFormulaCell::GetVectorState() const
621 : {
622 6 : return pCode->GetVectorState();
623 : }
624 :
625 125 : void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
626 : const FormulaGrammar::Grammar eGrammar ) const
627 : {
628 125 : if( pCode->GetCodeError() && !pCode->GetLen() )
629 : {
630 0 : rBuffer = OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
631 0 : return;
632 : }
633 125 : else if( cMatrixFlag == MM_REFERENCE )
634 : {
635 : // Reference to another cell that contains a matrix formula.
636 0 : pCode->Reset();
637 0 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
638 0 : if( p )
639 : {
640 : /* FIXME: original GetFormula() code obtained
641 : * pCell only if (!this->IsInChangeTrack()),
642 : * GetEnglishFormula() omitted that test.
643 : * Can we live without in all cases? */
644 0 : ScFormulaCell* pCell = NULL;
645 0 : ScSingleRefData& rRef = p->GetSingleRef();
646 0 : rRef.CalcAbsIfRel( aPos );
647 0 : if ( rRef.Valid() )
648 : pCell = pDocument->GetFormulaCell(
649 0 : ScAddress(rRef.nCol, rRef.nRow, rRef.nTab));
650 :
651 0 : if (pCell)
652 : {
653 0 : pCell->GetFormula( rBuffer, eGrammar);
654 0 : return;
655 : }
656 : else
657 : {
658 0 : ScCompiler aComp( pDocument, aPos, *pCode);
659 0 : aComp.SetGrammar(eGrammar);
660 0 : aComp.CreateStringFromTokenArray( rBuffer );
661 : }
662 : }
663 : else
664 : {
665 : OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
666 : }
667 : }
668 : else
669 : {
670 125 : ScCompiler aComp( pDocument, aPos, *pCode);
671 125 : aComp.SetGrammar(eGrammar);
672 125 : aComp.CreateStringFromTokenArray( rBuffer );
673 : }
674 :
675 125 : sal_Unicode ch('=');
676 125 : rBuffer.insert( 0, &ch, 1 );
677 125 : if( cMatrixFlag )
678 : {
679 1 : sal_Unicode ch2('{');
680 1 : rBuffer.insert( 0, &ch2, 1);
681 1 : rBuffer.append( sal_Unicode('}'));
682 : }
683 : }
684 :
685 125 : void ScFormulaCell::GetFormula( OUString& rFormula, const FormulaGrammar::Grammar eGrammar ) const
686 : {
687 125 : OUStringBuffer rBuffer( rFormula );
688 125 : GetFormula( rBuffer, eGrammar );
689 125 : rFormula = rBuffer.makeStringAndClear();
690 125 : }
691 :
692 0 : void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
693 : {
694 0 : MaybeInterpret();
695 :
696 0 : const ScMatrix* pMat = NULL;
697 0 : if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell &&
698 0 : ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0))
699 0 : pMat->GetDimensions( rCols, rRows );
700 : else
701 : {
702 0 : rCols = 0;
703 0 : rRows = 0;
704 : }
705 0 : }
706 :
707 659 : void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
708 : const FormulaGrammar::Grammar eGrammar )
709 : {
710 659 : if ( pDocument->IsClipOrUndo() )
711 659 : return;
712 659 : bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
713 659 : if ( bWasInFormulaTree )
714 0 : pDocument->RemoveFromFormulaTree( this );
715 : // pCode may not deleted for queries, but must be empty
716 659 : if ( pCode )
717 0 : pCode->Clear();
718 659 : ScTokenArray* pCodeOld = pCode;
719 659 : ScCompiler aComp( pDocument, aPos);
720 659 : aComp.SetGrammar(eGrammar);
721 659 : pCode = aComp.CompileString( rFormula );
722 659 : if ( pCodeOld )
723 0 : delete pCodeOld;
724 659 : if( !pCode->GetCodeError() )
725 : {
726 659 : if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
727 : { // not recursive CompileTokenArray/Compile/CompileTokenArray
728 0 : if ( rFormula[0] == '=' )
729 0 : pCode->AddBad( rFormula.copy(1) );
730 : else
731 0 : pCode->AddBad( rFormula );
732 : }
733 659 : bCompile = true;
734 659 : CompileTokenArray( bNoListening );
735 : }
736 : else
737 0 : bChanged = true;
738 :
739 659 : if ( bWasInFormulaTree )
740 0 : pDocument->PutInFormulaTree( this );
741 : }
742 :
743 :
744 734 : void ScFormulaCell::CompileTokenArray( bool bNoListening )
745 : {
746 : // Not already compiled?
747 734 : if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
748 0 : Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
749 734 : else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
750 : {
751 : // RPN length may get changed
752 674 : bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
753 674 : if ( bWasInFormulaTree )
754 0 : pDocument->RemoveFromFormulaTree( this );
755 :
756 : // Loading from within filter? No listening yet!
757 674 : if( pDocument->IsInsertingFromOtherDoc() )
758 27 : bNoListening = true;
759 :
760 674 : if( !bNoListening && pCode->GetCodeLen() )
761 6 : EndListeningTo( pDocument );
762 674 : ScCompiler aComp(pDocument, aPos, *pCode);
763 674 : aComp.SetGrammar(pDocument->GetGrammar());
764 674 : bSubTotal = aComp.CompileTokenArray();
765 674 : if( !pCode->GetCodeError() )
766 : {
767 665 : nFormatType = aComp.GetNumFormatType();
768 665 : bChanged = true;
769 665 : aResult.SetToken( NULL);
770 665 : bCompile = false;
771 665 : if ( !bNoListening )
772 6 : StartListeningTo( pDocument );
773 : }
774 674 : if ( bWasInFormulaTree )
775 0 : pDocument->PutInFormulaTree( this );
776 :
777 674 : if (bSubTotal)
778 0 : pDocument->AddSubTotalCell(this);
779 : }
780 734 : }
781 :
782 :
783 448 : void ScFormulaCell::CompileXML( ScProgress& rProgress )
784 : {
785 448 : if ( cMatrixFlag == MM_REFERENCE )
786 : { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
787 : // just establish listeners
788 53 : StartListeningTo( pDocument );
789 501 : return ;
790 : }
791 :
792 395 : ScCompiler aComp( pDocument, aPos, *pCode);
793 395 : aComp.SetGrammar(eTempGrammar);
794 790 : OUString aFormula, aFormulaNmsp;
795 395 : aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
796 395 : pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
797 395 : rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
798 : // pCode may not deleted for queries, but must be empty
799 395 : if ( pCode )
800 395 : pCode->Clear();
801 395 : ScTokenArray* pCodeOld = pCode;
802 395 : pCode = aComp.CompileString( aFormula, aFormulaNmsp );
803 395 : delete pCodeOld;
804 395 : if( !pCode->GetCodeError() )
805 : {
806 395 : if ( !pCode->GetLen() )
807 : {
808 0 : if ( aFormula[0] == '=' )
809 0 : pCode->AddBad( aFormula.copy( 1 ) );
810 : else
811 0 : pCode->AddBad( aFormula );
812 : }
813 395 : bSubTotal = aComp.CompileTokenArray();
814 395 : if( !pCode->GetCodeError() )
815 : {
816 395 : nFormatType = aComp.GetNumFormatType();
817 395 : bChanged = true;
818 395 : bCompile = false;
819 395 : StartListeningTo( pDocument );
820 : }
821 :
822 395 : if (bSubTotal)
823 0 : pDocument->AddSubTotalCell(this);
824 : }
825 : else
826 0 : bChanged = true;
827 :
828 : // Same as in Load: after loading, it must be known if ocMacro is in any formula
829 : // (for macro warning, CompileXML is called at the end of loading XML file)
830 395 : if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
831 0 : pDocument->SetHasMacroFunc( true );
832 :
833 : //volatile cells must be added here for import
834 1544 : if( pCode->IsRecalcModeAlways() || pCode->IsRecalcModeForced() ||
835 1131 : pCode->IsRecalcModeOnLoad() || pCode->IsRecalcModeOnLoadOnce() )
836 : {
837 : // During load, only those cells that are marked explicitly dirty get
838 : // recalculated. So we need to set it dirty here.
839 36 : SetDirtyVar();
840 36 : pDocument->PutInFormulaTree(this);
841 395 : }
842 : }
843 :
844 :
845 2473 : void ScFormulaCell::CalcAfterLoad()
846 : {
847 2473 : bool bNewCompiled = false;
848 : // If a Calc 1.0-doc is read, we have a result, but no token array
849 2473 : if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
850 : {
851 0 : Compile( aResult.GetHybridFormula(), true, eTempGrammar);
852 0 : aResult.SetToken( NULL);
853 0 : bDirty = true;
854 0 : bNewCompiled = true;
855 : }
856 : // The UPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
857 2473 : if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
858 : {
859 0 : ScCompiler aComp(pDocument, aPos, *pCode);
860 0 : aComp.SetGrammar(pDocument->GetGrammar());
861 0 : bSubTotal = aComp.CompileTokenArray();
862 0 : nFormatType = aComp.GetNumFormatType();
863 0 : bDirty = true;
864 0 : bCompile = false;
865 0 : bNewCompiled = true;
866 :
867 0 : if (bSubTotal)
868 0 : pDocument->AddSubTotalCell(this);
869 : }
870 :
871 : // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
872 : // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
873 : // We iron this out here for all systems, such that we also have an Err503 here.
874 2473 : if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
875 : {
876 : OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
877 0 : aResult.SetResultError( errIllegalFPOperation );
878 0 : bDirty = true;
879 : }
880 :
881 : // DoubleRefs for binary operators were always a Matrix before version v5.0.
882 : // Now this is only the case when when in an array formula, otherwise it's an implicit intersection
883 4946 : if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
884 2473 : GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
885 : {
886 0 : cMatrixFlag = MM_FORMULA;
887 0 : SetMatColsRows( 1, 1);
888 : }
889 :
890 : // Do the cells need to be calculated? After Load cells can contain an error code, and then start
891 : // the listener and Recalculate (if needed) if not RECALCMODE_NORMAL
892 2473 : if( !bNewCompiled || !pCode->GetCodeError() )
893 : {
894 2473 : StartListeningTo( pDocument );
895 2473 : if( !pCode->IsRecalcModeNormal() )
896 1 : bDirty = true;
897 : }
898 2473 : if ( pCode->IsRecalcModeAlways() )
899 : { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
900 : // for each F9
901 0 : bDirty = true;
902 : }
903 : // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
904 2473 : }
905 :
906 :
907 0 : bool ScFormulaCell::MarkUsedExternalReferences()
908 : {
909 0 : return pCode && pDocument->MarkUsedExternalReferences( *pCode);
910 : }
911 :
912 :
913 4763 : void ScFormulaCell::Interpret()
914 : {
915 4763 : if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
916 83 : return; // no double/triple processing
917 :
918 : //! HACK:
919 : // If the call originates from a Reschedule in DdeLink update, leave dirty
920 : // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
921 4680 : if ( pDocument->IsInDdeLinkUpdate() )
922 0 : return;
923 :
924 4680 : if (bRunning)
925 : {
926 1 : if (!pDocument->GetDocOptions().IsIter())
927 : {
928 1 : aResult.SetResultError( errCircularReference );
929 1 : return;
930 : }
931 :
932 0 : if (aResult.GetResultError() == errCircularReference)
933 0 : aResult.SetResultError( 0 );
934 :
935 : // Start or add to iteration list.
936 0 : if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
937 0 : !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
938 0 : pDocument->GetRecursionHelper().SetInIterationReturn( true);
939 :
940 0 : return;
941 : }
942 : // no multiple interprets for GetErrCode, IsValue, GetValue and
943 : // different entry point recursions. Would also lead to premature
944 : // convergence in iterations.
945 4679 : if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
946 0 : pDocument->GetRecursionHelper().GetIteration())
947 0 : return ;
948 :
949 4679 : ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
950 4679 : bool bOldRunning = bRunning;
951 4679 : if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
952 : {
953 0 : bRunning = true;
954 0 : rRecursionHelper.SetInRecursionReturn( true);
955 : }
956 : else
957 : {
958 4679 : if ( ! InterpretFormulaGroup() )
959 4679 : InterpretTail( SCITP_NORMAL);
960 : }
961 :
962 : // While leaving a recursion or iteration stack, insert its cells to the
963 : // recursion list in reverse order.
964 4679 : if (rRecursionHelper.IsInReturn())
965 : {
966 0 : if (rRecursionHelper.GetRecursionCount() > 0 ||
967 0 : !rRecursionHelper.IsDoingRecursion())
968 0 : rRecursionHelper.Insert( this, bOldRunning, aResult);
969 0 : bool bIterationFromRecursion = false;
970 0 : bool bResumeIteration = false;
971 0 : do
972 : {
973 0 : if ((rRecursionHelper.IsInIterationReturn() &&
974 0 : rRecursionHelper.GetRecursionCount() == 0 &&
975 0 : !rRecursionHelper.IsDoingIteration()) ||
976 0 : bIterationFromRecursion || bResumeIteration)
977 : {
978 0 : ScFormulaCell* pIterCell = this; // scope for debug convenience
979 0 : bool & rDone = rRecursionHelper.GetConvergingReference();
980 0 : rDone = false;
981 0 : if (!bIterationFromRecursion && bResumeIteration)
982 : {
983 0 : bResumeIteration = false;
984 : // Resuming iteration expands the range.
985 : ScFormulaRecursionList::const_iterator aOldStart(
986 0 : rRecursionHelper.GetLastIterationStart());
987 0 : rRecursionHelper.ResumeIteration();
988 : // Mark new cells being in iteration.
989 0 : for (ScFormulaRecursionList::const_iterator aIter(
990 0 : rRecursionHelper.GetIterationStart()); aIter !=
991 : aOldStart; ++aIter)
992 : {
993 0 : pIterCell = (*aIter).pCell;
994 0 : pIterCell->bIsIterCell = true;
995 : }
996 : // Mark older cells dirty again, in case they converted
997 : // without accounting for all remaining cells in the circle
998 : // that weren't touched so far, e.g. conditional. Restore
999 : // backuped result.
1000 0 : sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1001 0 : for (ScFormulaRecursionList::const_iterator aIter(
1002 0 : aOldStart); aIter !=
1003 : rRecursionHelper.GetIterationEnd(); ++aIter)
1004 : {
1005 0 : pIterCell = (*aIter).pCell;
1006 0 : if (pIterCell->nSeenInIteration == nIteration)
1007 : {
1008 0 : if (!pIterCell->bDirty || aIter == aOldStart)
1009 : {
1010 0 : pIterCell->aResult = (*aIter).aPreviousResult;
1011 : }
1012 0 : --pIterCell->nSeenInIteration;
1013 : }
1014 0 : pIterCell->bDirty = true;
1015 0 : }
1016 : }
1017 : else
1018 : {
1019 0 : bResumeIteration = false;
1020 : // Close circle once.
1021 0 : rRecursionHelper.GetList().back().pCell->InterpretTail(
1022 0 : SCITP_CLOSE_ITERATION_CIRCLE);
1023 : // Start at 1, init things.
1024 0 : rRecursionHelper.StartIteration();
1025 : // Mark all cells being in iteration.
1026 0 : for (ScFormulaRecursionList::const_iterator aIter(
1027 0 : rRecursionHelper.GetIterationStart()); aIter !=
1028 : rRecursionHelper.GetIterationEnd(); ++aIter)
1029 : {
1030 0 : pIterCell = (*aIter).pCell;
1031 0 : pIterCell->bIsIterCell = true;
1032 : }
1033 : }
1034 0 : bIterationFromRecursion = false;
1035 0 : sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
1036 0 : for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1037 0 : rRecursionHelper.IncIteration())
1038 : {
1039 0 : rDone = true;
1040 0 : for ( ScFormulaRecursionList::iterator aIter(
1041 0 : rRecursionHelper.GetIterationStart()); aIter !=
1042 0 : rRecursionHelper.GetIterationEnd() &&
1043 0 : !rRecursionHelper.IsInReturn(); ++aIter)
1044 : {
1045 0 : pIterCell = (*aIter).pCell;
1046 0 : if (pIterCell->IsDirtyOrInTableOpDirty() &&
1047 0 : rRecursionHelper.GetIteration() !=
1048 0 : pIterCell->GetSeenInIteration())
1049 : {
1050 0 : (*aIter).aPreviousResult = pIterCell->aResult;
1051 0 : pIterCell->InterpretTail( SCITP_FROM_ITERATION);
1052 : }
1053 0 : rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
1054 : }
1055 0 : if (rRecursionHelper.IsInReturn())
1056 : {
1057 0 : bResumeIteration = true;
1058 0 : break; // for
1059 : // Don't increment iteration.
1060 : }
1061 : }
1062 0 : if (!bResumeIteration)
1063 : {
1064 0 : if (rDone)
1065 : {
1066 0 : for (ScFormulaRecursionList::const_iterator aIter(
1067 0 : rRecursionHelper.GetIterationStart());
1068 0 : aIter != rRecursionHelper.GetIterationEnd();
1069 : ++aIter)
1070 : {
1071 0 : pIterCell = (*aIter).pCell;
1072 0 : pIterCell->bIsIterCell = false;
1073 0 : pIterCell->nSeenInIteration = 0;
1074 0 : pIterCell->bRunning = (*aIter).bOldRunning;
1075 : }
1076 : }
1077 : else
1078 : {
1079 0 : for (ScFormulaRecursionList::const_iterator aIter(
1080 0 : rRecursionHelper.GetIterationStart());
1081 0 : aIter != rRecursionHelper.GetIterationEnd();
1082 : ++aIter)
1083 : {
1084 0 : pIterCell = (*aIter).pCell;
1085 0 : pIterCell->bIsIterCell = false;
1086 0 : pIterCell->nSeenInIteration = 0;
1087 0 : pIterCell->bRunning = (*aIter).bOldRunning;
1088 : // If one cell didn't converge, all cells of this
1089 : // circular dependency don't, no matter whether
1090 : // single cells did.
1091 0 : pIterCell->bDirty = false;
1092 0 : pIterCell->bTableOpDirty = false;
1093 0 : pIterCell->aResult.SetResultError( errNoConvergence);
1094 0 : pIterCell->bChanged = true;
1095 : }
1096 : }
1097 : // End this iteration and remove entries.
1098 0 : rRecursionHelper.EndIteration();
1099 0 : bResumeIteration = rRecursionHelper.IsDoingIteration();
1100 : }
1101 : }
1102 0 : if (rRecursionHelper.IsInRecursionReturn() &&
1103 0 : rRecursionHelper.GetRecursionCount() == 0 &&
1104 0 : !rRecursionHelper.IsDoingRecursion())
1105 : {
1106 0 : bIterationFromRecursion = false;
1107 : // Iterate over cells known so far, start with the last cell
1108 : // encountered, inserting new cells if another recursion limit
1109 : // is reached. Repeat until solved.
1110 0 : rRecursionHelper.SetDoingRecursion( true);
1111 0 : do
1112 : {
1113 0 : rRecursionHelper.SetInRecursionReturn( false);
1114 0 : for (ScFormulaRecursionList::const_iterator aIter(
1115 0 : rRecursionHelper.GetIterationStart());
1116 0 : !rRecursionHelper.IsInReturn() && aIter !=
1117 : rRecursionHelper.GetIterationEnd(); ++aIter)
1118 : {
1119 0 : ScFormulaCell* pCell = (*aIter).pCell;
1120 0 : if (pCell->IsDirtyOrInTableOpDirty())
1121 : {
1122 0 : pCell->InterpretTail( SCITP_NORMAL);
1123 0 : if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1124 0 : pCell->bRunning = (*aIter).bOldRunning;
1125 : }
1126 : }
1127 : } while (rRecursionHelper.IsInRecursionReturn());
1128 0 : rRecursionHelper.SetDoingRecursion( false);
1129 0 : if (rRecursionHelper.IsInIterationReturn())
1130 : {
1131 0 : if (!bResumeIteration)
1132 0 : bIterationFromRecursion = true;
1133 : }
1134 0 : else if (bResumeIteration ||
1135 0 : rRecursionHelper.IsDoingIteration())
1136 0 : rRecursionHelper.GetList().erase(
1137 : rRecursionHelper.GetIterationStart(),
1138 0 : rRecursionHelper.GetLastIterationStart());
1139 : else
1140 0 : rRecursionHelper.Clear();
1141 : }
1142 0 : } while (bIterationFromRecursion || bResumeIteration);
1143 : }
1144 : }
1145 :
1146 4679 : void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
1147 : {
1148 : class RecursionCounter
1149 : {
1150 : ScRecursionHelper& rRec;
1151 : bool bStackedInIteration;
1152 : public:
1153 4679 : RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
1154 : {
1155 4679 : bStackedInIteration = rRec.IsDoingIteration();
1156 4679 : if (bStackedInIteration)
1157 0 : rRec.GetRecursionInIterationStack().push( p);
1158 4679 : rRec.IncRecursionCount();
1159 4679 : }
1160 4679 : ~RecursionCounter()
1161 : {
1162 4679 : rRec.DecRecursionCount();
1163 4679 : if (bStackedInIteration)
1164 0 : rRec.GetRecursionInIterationStack().pop();
1165 4679 : }
1166 4679 : } aRecursionCounter( pDocument->GetRecursionHelper(), this);
1167 4679 : nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
1168 4679 : if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
1169 : {
1170 : // #i11719# no UPN and no error and no token code but result string present
1171 : // => interpretation of this cell during name-compilation and unknown names
1172 : // => can't exchange underlying code array in CompileTokenArray() /
1173 : // Compile() because interpreter's token iterator would crash or pCode
1174 : // would be deleted twice if this cell was interpreted during
1175 : // compilation.
1176 : // This should only be a temporary condition and, since we set an
1177 : // error, if ran into it again we'd bump into the dirty-clearing
1178 : // condition further down.
1179 0 : if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1180 : {
1181 0 : pCode->SetCodeError( errNoCode );
1182 : // This is worth an assertion; if encountered in daily work
1183 : // documents we might need another solution. Or just confirm correctness.
1184 : OSL_FAIL( "ScFormulaCell::Interpret: no UPN, no error, no token, but hybrid formula string" );
1185 0 : return;
1186 : }
1187 0 : CompileTokenArray();
1188 : }
1189 :
1190 4679 : if( pCode->GetCodeLen() && pDocument )
1191 : {
1192 : class StackCleaner
1193 : {
1194 : ScDocument* pDoc;
1195 : ScInterpreter* pInt;
1196 : public:
1197 4634 : StackCleaner( ScDocument* pD, ScInterpreter* pI )
1198 4634 : : pDoc(pD), pInt(pI)
1199 4634 : {}
1200 4634 : ~StackCleaner()
1201 : {
1202 4634 : delete pInt;
1203 4634 : pDoc->DecInterpretLevel();
1204 4634 : }
1205 : };
1206 4634 : pDocument->IncInterpretLevel();
1207 4634 : ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
1208 4634 : StackCleaner aStackCleaner( pDocument, p);
1209 4634 : sal_uInt16 nOldErrCode = aResult.GetResultError();
1210 4634 : if ( nSeenInIteration == 0 )
1211 : { // Only the first time
1212 : // With bChanged=false, if a newly compiled cell has a result of
1213 : // 0.0, no change is detected and the cell will not be repainted.
1214 : // bChanged = false;
1215 4634 : aResult.SetResultError( 0 );
1216 : }
1217 :
1218 4634 : switch ( aResult.GetResultError() )
1219 : {
1220 : case errCircularReference : // will be determined again if so
1221 0 : aResult.SetResultError( 0 );
1222 0 : break;
1223 : }
1224 :
1225 4634 : bool bOldRunning = bRunning;
1226 4634 : bRunning = true;
1227 4634 : p->Interpret();
1228 4634 : if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
1229 : {
1230 0 : if (nSeenInIteration > 0)
1231 0 : --nSeenInIteration; // retry when iteration is resumed
1232 0 : return;
1233 : }
1234 4634 : bRunning = bOldRunning;
1235 :
1236 : // #i102616# For single-sheet saving consider only content changes, not format type,
1237 : // because format type isn't set on loading (might be changed later)
1238 4634 : bool bContentChanged = false;
1239 :
1240 : // Do not create a HyperLink() cell if the formula results in an error.
1241 4634 : if( p->GetError() && pCode->IsHyperLink())
1242 0 : pCode->SetHyperLink(false);
1243 :
1244 4634 : if( p->GetError() && p->GetError() != errCircularReference)
1245 : {
1246 182 : bDirty = false;
1247 182 : bTableOpDirty = false;
1248 182 : bChanged = true;
1249 : }
1250 4634 : if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1251 : {
1252 0 : bool bIsValue = aResult.IsValue(); // the previous type
1253 : // Did it converge?
1254 0 : if ((bIsValue && p->GetResultType() == svDouble && fabs(
1255 0 : p->GetNumResult() - aResult.GetDouble()) <=
1256 0 : pDocument->GetDocOptions().GetIterEps()) ||
1257 0 : (!bIsValue && p->GetResultType() == svString &&
1258 0 : p->GetStringResult() == aResult.GetString()))
1259 : {
1260 : // A convergence in the first iteration doesn't necessarily
1261 : // mean that it's done, it may be as not all related cells
1262 : // of a circle changed their values yet. If the set really
1263 : // converges it will do so also during the next iteration. This
1264 : // fixes situations like of #i44115#. If this wasn't wanted an
1265 : // initial "uncalculated" value would be needed for all cells
1266 : // of a circular dependency => graph needed before calculation.
1267 0 : if (nSeenInIteration > 1 ||
1268 0 : pDocument->GetDocOptions().GetIterCount() == 1)
1269 : {
1270 0 : bDirty = false;
1271 0 : bTableOpDirty = false;
1272 : }
1273 : }
1274 : }
1275 :
1276 : // New error code?
1277 4634 : if( p->GetError() != nOldErrCode )
1278 : {
1279 110 : bChanged = true;
1280 : // bContentChanged only has to be set if the file content would be changed
1281 110 : if ( aResult.GetCellResultType() != svUnknown )
1282 44 : bContentChanged = true;
1283 : }
1284 :
1285 4634 : if( mbNeedsNumberFormat )
1286 : {
1287 866 : nFormatType = p->GetRetFormatType();
1288 866 : sal_Int32 nFormatIndex = p->GetRetFormatIndex();
1289 :
1290 : // don't set text format as hard format
1291 866 : if(nFormatType == NUMBERFORMAT_TEXT)
1292 70 : nFormatIndex = 0;
1293 796 : else if((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1294 795 : nFormatIndex = ScGlobal::GetStandardFormat(*pDocument->GetFormatTable(),
1295 1590 : nFormatIndex, nFormatType);
1296 :
1297 : // set number format explicitly
1298 866 : pDocument->SetNumberFormat( aPos, nFormatIndex );
1299 :
1300 866 : bChanged = true;
1301 866 : mbNeedsNumberFormat = false;
1302 : }
1303 :
1304 : // In case of changes just obtain the result, no temporary and
1305 : // comparison needed anymore.
1306 4634 : if (bChanged)
1307 : {
1308 : // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
1309 : // Also handle special cases of initial results after loading.
1310 1257 : if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1311 : {
1312 358 : ScFormulaResult aNewResult( p->GetResultToken().get());
1313 358 : StackVar eOld = aResult.GetCellResultType();
1314 358 : StackVar eNew = aNewResult.GetCellResultType();
1315 358 : if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
1316 : {
1317 : // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
1318 : // -> no change
1319 : }
1320 : else
1321 : {
1322 320 : if ( eOld == svHybridCell || eOld == svHybridValueCell ) // string result from SetFormulaResultString?
1323 22 : eOld = svString; // ScHybridCellToken has a valid GetString method
1324 :
1325 : // #i106045# use approxEqual to compare with stored value
1326 316 : bContentChanged = (eOld != eNew ||
1327 613 : (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
1328 348 : (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1329 358 : }
1330 : }
1331 :
1332 1257 : aResult.SetToken( p->GetResultToken().get() );
1333 : }
1334 : else
1335 : {
1336 3377 : ScFormulaResult aNewResult( p->GetResultToken().get());
1337 3377 : StackVar eOld = aResult.GetCellResultType();
1338 3377 : StackVar eNew = aNewResult.GetCellResultType();
1339 2427 : bChanged = (eOld != eNew ||
1340 6759 : (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
1341 3385 : (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1342 :
1343 : // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged)
1344 3377 : if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1345 : {
1346 0 : if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) ||
1347 0 : ( (eOld == svHybridCell || eOld == svHybridValueCell) && eNew == svString && aResult.GetString() == aNewResult.GetString() ) ||
1348 0 : ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) )
1349 : {
1350 : // no change, see above
1351 : }
1352 : else
1353 0 : bContentChanged = true;
1354 : }
1355 :
1356 3377 : aResult.Assign( aNewResult);
1357 : }
1358 :
1359 : // Precision as shown?
1360 13805 : if ( aResult.IsValue() && !p->GetError()
1361 4354 : && pDocument->GetDocOptions().IsCalcAsShown()
1362 14 : && nFormatType != NUMBERFORMAT_DATE
1363 14 : && nFormatType != NUMBERFORMAT_TIME
1364 4648 : && nFormatType != NUMBERFORMAT_DATETIME )
1365 : {
1366 14 : sal_uLong nFormat = pDocument->GetNumberFormat( aPos );
1367 : aResult.SetDouble( pDocument->RoundValueAsShown(
1368 14 : aResult.GetDouble(), nFormat));
1369 : }
1370 4634 : if (eTailParam == SCITP_NORMAL)
1371 : {
1372 4634 : bDirty = false;
1373 4634 : bTableOpDirty = false;
1374 : }
1375 4634 : if( aResult.GetMatrix() )
1376 : {
1377 : // If the formula wasn't entered as a matrix formula, live on with
1378 : // the upper left corner and let reference counting delete the matrix.
1379 42 : if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
1380 23 : aResult.SetToken( aResult.GetCellResultToken().get());
1381 : }
1382 4634 : if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1383 : {
1384 : // Coded double error may occur via filter import.
1385 0 : sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble());
1386 0 : aResult.SetResultError( nErr);
1387 0 : bChanged = bContentChanged = true;
1388 : }
1389 :
1390 4634 : if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
1391 : {
1392 : // pass bIgnoreLock=true, because even if called from pending row height update,
1393 : // a changed result must still reset the stream flag
1394 6 : pDocument->SetStreamValid(aPos.Tab(), false, true);
1395 : }
1396 4634 : if ( !pCode->IsRecalcModeAlways() )
1397 4598 : pDocument->RemoveFromFormulaTree( this );
1398 :
1399 : // FORCED cells also immediately tested for validity (start macro possibly)
1400 :
1401 4634 : if ( pCode->IsRecalcModeForced() )
1402 : {
1403 : sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
1404 0 : aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
1405 0 : if ( nValidation )
1406 : {
1407 0 : const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
1408 0 : ScRefCellValue aTmpCell(this);
1409 0 : if ( pData && !pData->IsDataValid(aTmpCell, aPos))
1410 0 : pData->DoCalcError( this );
1411 : }
1412 : }
1413 :
1414 : // Reschedule slows the whole thing down considerably, thus only execute on percent change
1415 : ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
1416 4634 : pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
1417 :
1418 4634 : switch (p->GetVolatileType())
1419 : {
1420 : case ScInterpreter::VOLATILE:
1421 : // Volatile via built-in volatile functions. No actions needed.
1422 36 : break;
1423 : case ScInterpreter::VOLATILE_MACRO:
1424 : // The formula contains a volatile macro.
1425 0 : pCode->SetExclusiveRecalcModeAlways();
1426 0 : pDocument->PutInFormulaTree(this);
1427 0 : StartListeningTo(pDocument);
1428 0 : break;
1429 : case ScInterpreter::NOT_VOLATILE:
1430 4598 : if (pCode->IsRecalcModeAlways())
1431 : {
1432 : // The formula was previously volatile, but no more.
1433 0 : EndListeningTo(pDocument);
1434 0 : pCode->SetExclusiveRecalcModeNormal();
1435 : }
1436 : else
1437 : {
1438 : // non-volatile formula. End listening to the area in case
1439 : // it's listening due to macro module change.
1440 4598 : pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
1441 : }
1442 4598 : pDocument->RemoveFromFormulaTree(this);
1443 4598 : break;
1444 : default:
1445 : ;
1446 4634 : }
1447 : }
1448 : else
1449 : {
1450 : // Cells with compiler errors should not be marked dirty forever
1451 : OSL_ENSURE( pCode->GetCodeError(), "no UPN-Code und no errors ?!?!" );
1452 45 : bDirty = false;
1453 45 : bTableOpDirty = false;
1454 4679 : }
1455 : }
1456 :
1457 :
1458 36 : void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows, bool bDirtyFlag )
1459 : {
1460 36 : ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
1461 36 : if (pMat)
1462 0 : pMat->SetMatColsRows( nCols, nRows );
1463 36 : else if (nCols || nRows)
1464 : {
1465 36 : aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
1466 : // Setting the new token actually forces an empty result at this top
1467 : // left cell, so have that recalculated.
1468 36 : SetDirty( bDirtyFlag );
1469 : }
1470 36 : }
1471 :
1472 :
1473 4 : void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
1474 : {
1475 4 : const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
1476 4 : if (pMat)
1477 4 : pMat->GetMatColsRows( nCols, nRows);
1478 : else
1479 : {
1480 0 : nCols = 0;
1481 0 : nRows = 0;
1482 : }
1483 4 : }
1484 :
1485 :
1486 3339 : void ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
1487 : {
1488 3339 : if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
1489 : {
1490 674 : const ScHint* p = PTR_CAST( ScHint, &rHint );
1491 674 : sal_uLong nHint = (p ? p->GetId() : 0);
1492 674 : if (nHint & (SC_HINT_DATACHANGED | SC_HINT_TABLEOPDIRTY))
1493 : {
1494 672 : bool bForceTrack = false;
1495 672 : if ( nHint & SC_HINT_TABLEOPDIRTY )
1496 : {
1497 0 : bForceTrack = !bTableOpDirty;
1498 0 : if ( !bTableOpDirty )
1499 : {
1500 0 : pDocument->AddTableOpFormulaCell( this );
1501 0 : bTableOpDirty = true;
1502 : }
1503 : }
1504 : else
1505 : {
1506 672 : bForceTrack = !bDirty;
1507 672 : SetDirtyVar();
1508 : }
1509 : // Don't remove from FormulaTree to put in FormulaTrack to
1510 : // put in FormulaTree again and again, only if necessary.
1511 : // Any other means except RECALCMODE_ALWAYS by which a cell could
1512 : // be in FormulaTree if it would notify other cells through
1513 : // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
1514 : // Yes. The new TableOpDirty made it necessary to have a
1515 : // forced mode where formulas may still be in FormulaTree from
1516 : // TableOpDirty but have to notify dependents for normal dirty.
1517 1076 : if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
1518 394 : || pCode->IsRecalcModeAlways())
1519 968 : && !pDocument->IsInFormulaTrack( this ) )
1520 291 : pDocument->AppendToFormulaTrack( this );
1521 : }
1522 : }
1523 3339 : }
1524 :
1525 1114 : void ScFormulaCell::SetDirty( bool bDirtyFlag )
1526 : {
1527 1114 : if ( !IsInChangeTrack() )
1528 : {
1529 1114 : if ( pDocument->GetHardRecalcState() )
1530 0 : SetDirtyVar();
1531 : else
1532 : {
1533 : // Multiple Formulas avoid tracking in Load and Copy compileAll
1534 : // by Scenario and Copy Block From Clip.
1535 : // If unconditional required Formula tracking is set before SetDirty
1536 : // bDirty = false, eg in CompileTokenArray
1537 1114 : if ( !bDirty || !pDocument->IsInFormulaTree( this ) )
1538 : {
1539 1041 : if( bDirtyFlag )
1540 1010 : SetDirtyVar();
1541 1041 : pDocument->AppendToFormulaTrack( this );
1542 1041 : pDocument->TrackFormulas();
1543 : }
1544 : }
1545 :
1546 1114 : if (pDocument->IsStreamValid(aPos.Tab()))
1547 1 : pDocument->SetStreamValid(aPos.Tab(), false);
1548 : }
1549 1114 : }
1550 :
1551 2616 : void ScFormulaCell::SetDirtyVar()
1552 : {
1553 2616 : bDirty = true;
1554 2616 : if (xGroup)
1555 424 : xGroup->meCalcState = sc::GroupCalcEnabled;
1556 :
1557 : // mark the sheet of this cell to be calculated
1558 : //#FIXME do we need to revert this remnant of old fake vba events? pDocument->AddCalculateTable( aPos.Tab() );
1559 2616 : }
1560 :
1561 2473 : void ScFormulaCell::SetDirtyAfterLoad()
1562 : {
1563 2473 : bDirty = true;
1564 2473 : if ( !pDocument->GetHardRecalcState() )
1565 2473 : pDocument->PutInFormulaTree( this );
1566 2473 : }
1567 :
1568 0 : void ScFormulaCell::SetTableOpDirty()
1569 : {
1570 0 : if ( !IsInChangeTrack() )
1571 : {
1572 0 : if ( pDocument->GetHardRecalcState() )
1573 0 : bTableOpDirty = true;
1574 : else
1575 : {
1576 0 : if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
1577 : {
1578 0 : if ( !bTableOpDirty )
1579 : {
1580 0 : pDocument->AddTableOpFormulaCell( this );
1581 0 : bTableOpDirty = true;
1582 : }
1583 0 : pDocument->AppendToFormulaTrack( this );
1584 0 : pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
1585 : }
1586 : }
1587 : }
1588 0 : }
1589 :
1590 :
1591 30563 : bool ScFormulaCell::IsDirtyOrInTableOpDirty() const
1592 : {
1593 30563 : return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
1594 : }
1595 :
1596 0 : void ScFormulaCell::SetResultToken( const formula::FormulaToken* pToken )
1597 : {
1598 0 : aResult.SetToken(pToken);
1599 0 : }
1600 :
1601 25 : void ScFormulaCell::SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, formula::FormulaToken* pUL )
1602 : {
1603 25 : aResult.SetMatrix(nCols, nRows, pMat, pUL);
1604 25 : }
1605 :
1606 25 : void ScFormulaCell::SetErrCode( sal_uInt16 n )
1607 : {
1608 : /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
1609 : * used whether it is solely for transport of a simple result error and get
1610 : * rid of that abuse. */
1611 25 : pCode->SetCodeError( n );
1612 : // Hard set errors are transported as result type value per convention,
1613 : // e.g. via clipboard. ScFormulaResult::IsValue() and
1614 : // ScFormulaResult::GetDouble() handle that.
1615 25 : aResult.SetResultError( n );
1616 25 : }
1617 :
1618 20 : void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
1619 : {
1620 20 : if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL )
1621 20 : SetDirtyVar();
1622 20 : if ( nBits & RECALCMODE_ONLOAD_ONCE )
1623 : { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
1624 20 : nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL;
1625 : }
1626 20 : pCode->AddRecalcMode( nBits );
1627 20 : }
1628 :
1629 : // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
1630 0 : void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
1631 : {
1632 0 : OUString aCellString;
1633 :
1634 : Color* pColor;
1635 :
1636 : // Cell Text uses the Cell format while the URL uses
1637 : // the default format for the type.
1638 0 : sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos );
1639 0 : SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
1640 :
1641 0 : sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, NUMBERFORMAT_NUMBER);
1642 :
1643 0 : if ( IsValue() )
1644 : {
1645 0 : double fValue = GetValue();
1646 0 : pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
1647 : }
1648 : else
1649 : {
1650 0 : aCellString = GetString();
1651 0 : pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
1652 : }
1653 0 : ScConstMatrixRef xMat( aResult.GetMatrix());
1654 0 : if (xMat)
1655 : {
1656 : // determine if the matrix result is a string or value.
1657 0 : if (!xMat->IsValue(0, 1))
1658 0 : rURL = xMat->GetString(0, 1);
1659 : else
1660 : pFormatter->GetOutputString(
1661 0 : xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
1662 : }
1663 :
1664 0 : if(rURL.isEmpty())
1665 : {
1666 0 : if(IsValue())
1667 0 : pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
1668 : else
1669 0 : pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
1670 0 : }
1671 0 : }
1672 :
1673 526 : bool ScFormulaCell::IsMultilineResult()
1674 : {
1675 526 : if (!IsValue())
1676 120 : return aResult.IsMultiline();
1677 406 : return false;
1678 : }
1679 :
1680 25714 : void ScFormulaCell::MaybeInterpret()
1681 : {
1682 25714 : if (!IsDirtyOrInTableOpDirty())
1683 47354 : return;
1684 :
1685 4074 : if (pDocument->GetAutoCalc() || (cMatrixFlag != MM_NONE))
1686 3924 : Interpret();
1687 : }
1688 :
1689 0 : bool ScFormulaCell::IsHyperLinkCell() const
1690 : {
1691 0 : return pCode && pCode->IsHyperLink();
1692 : }
1693 :
1694 0 : EditTextObject* ScFormulaCell::CreateURLObject()
1695 : {
1696 0 : OUString aCellText;
1697 0 : OUString aURL;
1698 0 : GetURLResult( aURL, aCellText );
1699 :
1700 0 : return ScEditUtil::CreateURLObjectFromURL( *pDocument, aURL, aCellText );
1701 : }
1702 :
1703 251 : bool ScFormulaCell::IsEmpty()
1704 : {
1705 251 : MaybeInterpret();
1706 251 : return aResult.GetCellResultType() == formula::svEmptyCell;
1707 : }
1708 :
1709 1130 : bool ScFormulaCell::IsEmptyDisplayedAsString()
1710 : {
1711 1130 : MaybeInterpret();
1712 1130 : return aResult.IsEmptyDisplayedAsString();
1713 : }
1714 :
1715 8680 : bool ScFormulaCell::IsValue()
1716 : {
1717 8680 : MaybeInterpret();
1718 8680 : return aResult.IsValue();
1719 : }
1720 :
1721 12 : bool ScFormulaCell::IsValueNoError()
1722 : {
1723 12 : MaybeInterpret();
1724 12 : if (pCode->GetCodeError())
1725 0 : return false;
1726 :
1727 12 : return aResult.IsValueNoError();
1728 : }
1729 :
1730 977 : bool ScFormulaCell::IsHybridValueCell()
1731 : {
1732 977 : return aResult.GetType() == formula::svHybridValueCell;
1733 : }
1734 :
1735 10637 : double ScFormulaCell::GetValue()
1736 : {
1737 10637 : MaybeInterpret();
1738 21182 : if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
1739 10545 : !aResult.GetResultError())
1740 10445 : return aResult.GetDouble();
1741 192 : return 0.0;
1742 : }
1743 :
1744 5 : double ScFormulaCell::GetValueAlways()
1745 : {
1746 : // for goal seek: return result value even if error code is set
1747 5 : MaybeInterpret();
1748 5 : return aResult.GetDouble();
1749 : }
1750 :
1751 177 : OUString ScFormulaCell::GetString()
1752 : {
1753 177 : MaybeInterpret();
1754 338 : if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
1755 161 : !aResult.GetResultError())
1756 161 : return aResult.GetString();
1757 16 : return OUString();
1758 : }
1759 :
1760 86 : const ScMatrix* ScFormulaCell::GetMatrix()
1761 : {
1762 86 : if ( pDocument->GetAutoCalc() )
1763 : {
1764 258 : if( IsDirtyOrInTableOpDirty()
1765 : // Was stored !bDirty but an accompanying matrix cell was bDirty?
1766 344 : || (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix()))
1767 33 : Interpret();
1768 : }
1769 86 : return aResult.GetMatrix().get();
1770 : }
1771 :
1772 14 : bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
1773 : {
1774 14 : switch ( cMatrixFlag )
1775 : {
1776 : case MM_FORMULA :
1777 1 : rPos = aPos;
1778 1 : return true;
1779 : case MM_REFERENCE :
1780 : {
1781 13 : pCode->Reset();
1782 13 : ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1783 13 : if( t )
1784 : {
1785 13 : ScSingleRefData& rRef = t->GetSingleRef();
1786 13 : rRef.CalcAbsIfRel( aPos );
1787 13 : if ( rRef.Valid() )
1788 : {
1789 13 : rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
1790 13 : return true;
1791 : }
1792 : }
1793 : }
1794 0 : break;
1795 : }
1796 0 : return false;
1797 : }
1798 :
1799 :
1800 : /*
1801 : Edge-Values:
1802 :
1803 : 8
1804 : 4 16
1805 : 2
1806 :
1807 : inside: 1
1808 : outside: 0
1809 : (reserved: open: 32)
1810 : */
1811 :
1812 0 : sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) const
1813 : {
1814 0 : switch ( cMatrixFlag )
1815 : {
1816 : case MM_FORMULA :
1817 : case MM_REFERENCE :
1818 : {
1819 : static SCCOL nC;
1820 : static SCROW nR;
1821 0 : ScAddress aOrg;
1822 0 : if ( !GetMatrixOrigin( aOrg ) )
1823 0 : return 0; // bad luck..
1824 0 : if ( aOrg != rOrgPos )
1825 : { // First time or a different matrix than last time.
1826 0 : rOrgPos = aOrg;
1827 : const ScFormulaCell* pFCell;
1828 0 : if ( cMatrixFlag == MM_REFERENCE )
1829 0 : pFCell = pDocument->GetFormulaCell(aOrg);
1830 : else
1831 0 : pFCell = this; // this MM_FORMULA
1832 : // There's only one this, don't compare pFCell==this.
1833 0 : if (pFCell && pFCell->cMatrixFlag == MM_FORMULA)
1834 : {
1835 0 : pFCell->GetMatColsRows( nC, nR );
1836 0 : if ( nC == 0 || nR == 0 )
1837 : {
1838 : // No ScMatrixFormulaCellToken available yet, calculate new.
1839 0 : nC = 1;
1840 0 : nR = 1;
1841 0 : ScAddress aTmpOrg;
1842 : ScFormulaCell* pCell;
1843 0 : ScAddress aAdr( aOrg );
1844 0 : aAdr.IncCol();
1845 0 : bool bCont = true;
1846 0 : do
1847 : {
1848 0 : pCell = pDocument->GetFormulaCell(aAdr);
1849 0 : if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
1850 0 : pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
1851 : {
1852 0 : nC++;
1853 0 : aAdr.IncCol();
1854 : }
1855 : else
1856 0 : bCont = false;
1857 : } while ( bCont );
1858 0 : aAdr = aOrg;
1859 0 : aAdr.IncRow();
1860 0 : bCont = true;
1861 0 : do
1862 : {
1863 0 : pCell = pDocument->GetFormulaCell(aAdr);
1864 0 : if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
1865 0 : pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
1866 : {
1867 0 : nR++;
1868 0 : aAdr.IncRow();
1869 : }
1870 : else
1871 0 : bCont = false;
1872 : } while ( bCont );
1873 :
1874 0 : const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
1875 0 : }
1876 : }
1877 : else
1878 : {
1879 : #if OSL_DEBUG_LEVEL > 0
1880 : OUString aTmp;
1881 : OStringBuffer aMsg(RTL_CONSTASCII_STRINGPARAM(
1882 : "broken Matrix, no MatFormula at origin, Pos: "));
1883 : aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
1884 : aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
1885 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
1886 : aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
1887 : aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
1888 : OSL_FAIL(aMsg.getStr());
1889 : #endif
1890 0 : return 0; // bad luck ...
1891 : }
1892 : }
1893 : // here we are, healthy and clean, somewhere in between
1894 0 : SCsCOL dC = aPos.Col() - aOrg.Col();
1895 0 : SCsROW dR = aPos.Row() - aOrg.Row();
1896 0 : sal_uInt16 nEdges = 0;
1897 0 : if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
1898 : {
1899 0 : if ( dC == 0 )
1900 0 : nEdges |= sc::MatrixEdgeLeft; // left edge
1901 0 : if ( dC+1 == nC )
1902 0 : nEdges |= sc::MatrixEdgeRight; // right edge
1903 0 : if ( dR == 0 )
1904 0 : nEdges |= sc::MatrixEdgeTop; // top edge
1905 0 : if ( dR+1 == nR )
1906 0 : nEdges |= sc::MatrixEdgeBottom; // bottom edge
1907 0 : if ( !nEdges )
1908 0 : nEdges = sc::MatrixEdgeInside; // inside
1909 : }
1910 : #if OSL_DEBUG_LEVEL > 0
1911 : else
1912 : {
1913 : OUString aTmp;
1914 : OStringBuffer aMsg( "broken Matrix, Pos: " );
1915 : aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
1916 : aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
1917 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatOrg: "));
1918 : aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
1919 : aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
1920 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatCols: "));
1921 : aMsg.append(static_cast<sal_Int32>( nC ));
1922 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", MatRows: "));
1923 : aMsg.append(static_cast<sal_Int32>( nR ));
1924 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffCols: "));
1925 : aMsg.append(static_cast<sal_Int32>( dC ));
1926 : aMsg.append(RTL_CONSTASCII_STRINGPARAM(", DiffRows: "));
1927 : aMsg.append(static_cast<sal_Int32>( dR ));
1928 : OSL_FAIL( aMsg.makeStringAndClear().getStr());
1929 : }
1930 : #endif
1931 0 : return nEdges;
1932 : // break;
1933 : }
1934 : default:
1935 0 : return 0;
1936 : }
1937 : }
1938 :
1939 4053 : sal_uInt16 ScFormulaCell::GetErrCode()
1940 : {
1941 4053 : MaybeInterpret();
1942 :
1943 : /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
1944 : * and not also abused for signaling other error conditions we could bail
1945 : * out even before attempting to interpret broken code. */
1946 4053 : sal_uInt16 nErr = pCode->GetCodeError();
1947 4053 : if (nErr)
1948 73 : return nErr;
1949 3980 : return aResult.GetResultError();
1950 : }
1951 :
1952 36 : sal_uInt16 ScFormulaCell::GetRawError()
1953 : {
1954 36 : sal_uInt16 nErr = pCode->GetCodeError();
1955 36 : if (nErr)
1956 0 : return nErr;
1957 36 : return aResult.GetResultError();
1958 : }
1959 :
1960 5 : bool ScFormulaCell::GetErrorOrValue( sal_uInt16& rErr, double& rVal )
1961 : {
1962 5 : MaybeInterpret();
1963 :
1964 5 : rErr = pCode->GetCodeError();
1965 5 : if (rErr)
1966 0 : return true;
1967 :
1968 5 : return aResult.GetErrorOrDouble(rErr, rVal);
1969 : }
1970 :
1971 9 : bool ScFormulaCell::HasOneReference( ScRange& r ) const
1972 : {
1973 9 : pCode->Reset();
1974 9 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1975 9 : if( p && !pCode->GetNextReferenceRPN() ) // only one!
1976 : {
1977 9 : p->CalcAbsIfRel( aPos );
1978 9 : SingleDoubleRefProvider aProv( *p );
1979 : r.aStart.Set( aProv.Ref1.nCol,
1980 : aProv.Ref1.nRow,
1981 9 : aProv.Ref1.nTab );
1982 : r.aEnd.Set( aProv.Ref2.nCol,
1983 : aProv.Ref2.nRow,
1984 9 : aProv.Ref2.nTab );
1985 9 : return true;
1986 : }
1987 : else
1988 0 : return false;
1989 : }
1990 :
1991 : bool
1992 9 : ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
1993 : {
1994 : /* If there appears just one reference in the formula, it's the same
1995 : as HasOneReference(). If there are more of them, they can denote
1996 : one range if they are (sole) arguments of one function.
1997 : Union of these references must form one range and their
1998 : intersection must be empty set.
1999 : */
2000 :
2001 : // Detect the simple case of exactly one reference in advance without all
2002 : // overhead.
2003 : // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
2004 : // work again, where the function does not have only references.
2005 9 : if (HasOneReference( rRange))
2006 9 : return true;
2007 :
2008 0 : pCode->Reset();
2009 : // Get first reference, if any
2010 : ScToken* const pFirstReference(
2011 0 : dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN()));
2012 0 : if (pFirstReference)
2013 : {
2014 : // Collect all consecutive references, starting by the one
2015 : // already found
2016 0 : std::deque<ScToken*> aReferences;
2017 0 : aReferences.push_back(pFirstReference);
2018 0 : FormulaToken* pToken(pCode->NextRPN());
2019 0 : FormulaToken* pFunction(0);
2020 0 : while (pToken)
2021 : {
2022 0 : if (lcl_isReference(*pToken))
2023 : {
2024 0 : aReferences.push_back(dynamic_cast<ScToken*>(pToken));
2025 0 : pToken = pCode->NextRPN();
2026 : }
2027 : else
2028 : {
2029 0 : if (pToken->IsFunction())
2030 : {
2031 0 : pFunction = pToken;
2032 : }
2033 0 : break;
2034 : }
2035 : }
2036 0 : if (pFunction && !pCode->GetNextReferenceRPN()
2037 0 : && (pFunction->GetParamCount() == aReferences.size()))
2038 : {
2039 0 : return lcl_refListFormsOneRange(aPos, aReferences, rRange);
2040 0 : }
2041 : }
2042 0 : return false;
2043 : }
2044 :
2045 72 : bool ScFormulaCell::HasRelNameReference() const
2046 : {
2047 72 : pCode->Reset();
2048 : ScToken* t;
2049 72 : while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
2050 : {
2051 158 : if ( t->GetSingleRef().IsRelName() ||
2052 89 : (t->GetType() == formula::svDoubleRef &&
2053 10 : t->GetDoubleRef().Ref2.IsRelName()) )
2054 0 : return true;
2055 : }
2056 72 : return false;
2057 : }
2058 :
2059 0 : bool ScFormulaCell::HasColRowName() const
2060 : {
2061 0 : pCode->Reset();
2062 0 : return (pCode->GetNextColRowName() != NULL);
2063 : }
2064 :
2065 51 : bool ScFormulaCell::UpdateReference(
2066 : UpdateRefMode eUpdateRefMode, const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
2067 : ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
2068 : {
2069 51 : bool bCellStateChanged = false;
2070 :
2071 51 : SCCOL nCol1 = rRange.aStart.Col();
2072 51 : SCROW nRow1 = rRange.aStart.Row();
2073 51 : SCCOL nCol = aPos.Col();
2074 51 : SCROW nRow = aPos.Row();
2075 51 : SCTAB nTab = aPos.Tab();
2076 51 : ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
2077 51 : if ( pUndoCellPos )
2078 34 : aUndoPos = *pUndoCellPos;
2079 51 : ScAddress aOldPos( aPos );
2080 51 : bool bIsInsert = (eUpdateRefMode == URM_INSDEL && nDx >= 0 && nDy >= 0 && nDz >= 0);
2081 :
2082 51 : if (eUpdateRefMode == URM_INSDEL && rRange.In(aPos))
2083 : {
2084 : // This formula cell itself is being shifted during cell range
2085 : // insertion or deletion. Update its position.
2086 3 : aPos.Move(nDx, nDy, nDz);
2087 3 : bCellStateChanged = aPos != aOldPos;
2088 : }
2089 48 : else if (rRange.In(aPos))
2090 : {
2091 : // The cell is being moved or copied to a new position. I guess the
2092 : // position has been updated prior to this call? Determine
2093 : // its original position before the move which will be used to adjust
2094 : // relative references later.
2095 17 : aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
2096 : }
2097 :
2098 51 : bool bHasRefs = false;
2099 51 : bool bHasColRowNames = false;
2100 51 : bool bOnRefMove = false;
2101 51 : if ( !pDocument->IsClipOrUndo() )
2102 : {
2103 : // Check presence of any references or column row names.
2104 51 : pCode->Reset();
2105 51 : bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
2106 51 : if ( !bHasRefs || eUpdateRefMode == URM_COPY )
2107 : {
2108 25 : pCode->Reset();
2109 25 : bHasColRowNames = (pCode->GetNextColRowName() != NULL);
2110 25 : bHasRefs = bHasRefs || bHasColRowNames;
2111 : }
2112 51 : bOnRefMove = pCode->IsRecalcModeOnRefMove();
2113 : }
2114 :
2115 51 : if (!bHasRefs && !bOnRefMove)
2116 : // This formula cell contains no references, nor needs recalculating
2117 : // on reference update. Bail out.
2118 5 : return bCellStateChanged;
2119 :
2120 46 : boost::scoped_ptr<ScTokenArray> pOldCode;
2121 46 : if (pUndoDoc)
2122 5 : pOldCode.reset(pCode->Clone());
2123 :
2124 46 : ScRangeData* pSharedCode = NULL;
2125 46 : bool bValChanged = false;
2126 46 : bool bRangeModified = false; // any range, not only shared formula
2127 46 : bool bRefSizeChanged = false;
2128 :
2129 46 : if (bHasRefs)
2130 : {
2131 : // Update cell or range references.
2132 43 : ScCompiler aComp(pDocument, aPos, *pCode);
2133 43 : aComp.SetGrammar(pDocument->GetGrammar());
2134 : pSharedCode = aComp.UpdateReference(eUpdateRefMode, aOldPos, rRange,
2135 : nDx, nDy, nDz,
2136 43 : bValChanged, bRefSizeChanged);
2137 43 : bRangeModified = aComp.HasModifiedRange();
2138 : }
2139 :
2140 46 : bCellStateChanged |= bValChanged;
2141 :
2142 46 : if (bOnRefMove)
2143 : // Cell may reference itself, e.g. ocColumn, ocRow without parameter
2144 5 : bOnRefMove = (bValChanged || (aPos != aOldPos));
2145 :
2146 46 : bool bColRowNameCompile = false;
2147 46 : bool bHasRelName = false;
2148 46 : bool bNewListening = false;
2149 46 : bool bInDeleteUndo = false;
2150 :
2151 46 : if (bHasRefs)
2152 : {
2153 : // Upon Insert ColRowNames have to be recompiled in case the
2154 : // insertion occurs right in front of the range.
2155 : bColRowNameCompile =
2156 43 : (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0));
2157 :
2158 43 : if ( bColRowNameCompile )
2159 : {
2160 11 : bColRowNameCompile = false;
2161 : ScToken* t;
2162 11 : ScRangePairList* pColList = pDocument->GetColNameRanges();
2163 11 : ScRangePairList* pRowList = pDocument->GetRowNameRanges();
2164 11 : pCode->Reset();
2165 22 : while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL )
2166 : {
2167 0 : ScSingleRefData& rRef = t->GetSingleRef();
2168 0 : if ( nDy > 0 && rRef.IsColRel() )
2169 : { // ColName
2170 0 : rRef.CalcAbsIfRel( aPos );
2171 0 : ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
2172 0 : ScRangePair* pR = pColList->Find( aAdr );
2173 0 : if ( pR )
2174 : { // defined
2175 0 : if ( pR->GetRange(1).aStart.Row() == nRow1 )
2176 0 : bColRowNameCompile = true;
2177 : }
2178 : else
2179 : { // on the fly
2180 0 : if ( rRef.nRow + 1 == nRow1 )
2181 0 : bColRowNameCompile = true;
2182 : }
2183 : }
2184 0 : if ( nDx > 0 && rRef.IsRowRel() )
2185 : { // RowName
2186 0 : rRef.CalcAbsIfRel( aPos );
2187 0 : ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
2188 0 : ScRangePair* pR = pRowList->Find( aAdr );
2189 0 : if ( pR )
2190 : { // defined
2191 0 : if ( pR->GetRange(1).aStart.Col() == nCol1 )
2192 0 : bColRowNameCompile = true;
2193 : }
2194 : else
2195 : { // on the fly
2196 0 : if ( rRef.nCol + 1 == nCol1 )
2197 0 : bColRowNameCompile = true;
2198 : }
2199 : }
2200 : }
2201 : }
2202 32 : else if ( eUpdateRefMode == URM_MOVE )
2203 : { // Recomplie for Move/D&D when ColRowName was moved or this Cell
2204 : // points to one and was moved.
2205 1 : bColRowNameCompile = bCompile; // Possibly from Copy ctor
2206 1 : if ( !bColRowNameCompile )
2207 : {
2208 1 : bool bMoved = (aPos != aOldPos);
2209 1 : pCode->Reset();
2210 1 : ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
2211 1 : if ( t && bMoved )
2212 0 : bColRowNameCompile = true;
2213 2 : while ( t && !bColRowNameCompile )
2214 : {
2215 0 : ScSingleRefData& rRef = t->GetSingleRef();
2216 0 : rRef.CalcAbsIfRel( aPos );
2217 0 : if ( rRef.Valid() )
2218 : {
2219 0 : ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
2220 0 : if ( rRange.In( aAdr ) )
2221 0 : bColRowNameCompile = true;
2222 : }
2223 0 : t = static_cast<ScToken*>(pCode->GetNextColRowName());
2224 : }
2225 : }
2226 : }
2227 31 : else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
2228 : {
2229 0 : bColRowNameCompile = true;
2230 : }
2231 :
2232 43 : ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
2233 43 : bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
2234 :
2235 : // RelNameRefs are always moved
2236 43 : bHasRelName = HasRelNameReference();
2237 : // Reference changed and new listening needed?
2238 : // Except in Insert/Delete without specialties.
2239 43 : bNewListening = (bRangeModified || pSharedCode || bColRowNameCompile
2240 43 : || (bValChanged && (eUpdateRefMode != URM_INSDEL ||
2241 11 : bInDeleteUndo || bRefSizeChanged)) ||
2242 0 : (bHasRelName && eUpdateRefMode != URM_COPY))
2243 : // #i36299# Don't duplicate action during cut&paste / drag&drop
2244 : // on a cell in the range moved, start/end listeners is done
2245 : // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
2246 43 : && !(eUpdateRefMode == URM_MOVE &&
2247 43 : pDocument->IsInsertingFromOtherDoc() && rRange.In(aPos));
2248 :
2249 43 : if ( bNewListening )
2250 0 : EndListeningTo(pDocument, pOldCode.get(), aOldPos);
2251 : }
2252 :
2253 46 : bool bNeedDirty = false;
2254 : // NeedDirty for changes except for Copy and Move/Insert without RelNames
2255 46 : if ( bRangeModified || pSharedCode || bColRowNameCompile ||
2256 11 : (bValChanged && eUpdateRefMode != URM_COPY &&
2257 11 : (eUpdateRefMode != URM_MOVE || bHasRelName) &&
2258 18 : (!bIsInsert || bHasRelName || bInDeleteUndo ||
2259 42 : bRefSizeChanged)) || bOnRefMove)
2260 8 : bNeedDirty = true;
2261 :
2262 46 : if (pUndoDoc && (bValChanged || pSharedCode || bOnRefMove))
2263 : {
2264 : // Copy the cell to aUndoPos, which is its current position in the document,
2265 : // so this works when UpdateReference is called before moving the cells
2266 : // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
2267 : // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
2268 :
2269 : // If there is already a formula cell in the undo document, don't overwrite it,
2270 : // the first (oldest) is the important cell.
2271 0 : if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA )
2272 : {
2273 : ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos,
2274 0 : pOldCode.get(), eTempGrammar, cMatrixFlag );
2275 0 : pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
2276 0 : pUndoDoc->SetFormulaCell(aUndoPos, pFCell);
2277 : }
2278 : }
2279 :
2280 46 : bValChanged = false;
2281 :
2282 46 : if ( pSharedCode )
2283 : { // Replace shared formula with own formula
2284 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
2285 0 : delete pCode;
2286 0 : pCode = pSharedCode->GetCode()->Clone();
2287 : // #i18937# #i110008# call MoveRelWrap, but with the old position
2288 0 : ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pSharedCode->GetMaxCol(), pSharedCode->GetMaxRow());
2289 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
2290 0 : aComp2.SetGrammar(pDocument->GetGrammar());
2291 : aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, rRange,
2292 0 : nDx, nDy, nDz );
2293 0 : bValChanged = true;
2294 0 : bNeedDirty = true;
2295 : }
2296 :
2297 46 : if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
2298 : {
2299 0 : CompileTokenArray( bNewListening ); // no Listening
2300 0 : bNeedDirty = true;
2301 : }
2302 :
2303 46 : if ( !bInDeleteUndo )
2304 : { // In ChangeTrack Delete-Reject listeners are established in
2305 : // InsertCol/InsertRow
2306 46 : if ( bNewListening )
2307 : {
2308 0 : if ( eUpdateRefMode == URM_INSDEL )
2309 : {
2310 : // Inserts/Deletes re-establish listeners after all
2311 : // UpdateReference calls.
2312 : // All replaced shared formula listeners have to be
2313 : // established after an Insert or Delete. Do nothing here.
2314 0 : SetNeedsListening( true);
2315 : }
2316 : else
2317 0 : StartListeningTo( pDocument );
2318 : }
2319 : }
2320 :
2321 46 : if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pSharedCode) )
2322 : { // Cut off references, invalid or similar?
2323 8 : sc::AutoCalcSwitch(*pDocument, false);
2324 8 : SetDirty();
2325 : }
2326 :
2327 46 : return bCellStateChanged;
2328 : }
2329 :
2330 6 : void ScFormulaCell::UpdateInsertTab(SCTAB nTable, SCTAB nNewSheets)
2331 : {
2332 6 : bool bPosChanged = ( aPos.Tab() >= nTable ? true : false );
2333 6 : pCode->Reset();
2334 6 : if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
2335 : {
2336 6 : EndListeningTo( pDocument );
2337 : // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
2338 6 : if ( bPosChanged )
2339 6 : aPos.IncTab(nNewSheets);
2340 : ScRangeData* pRangeData;
2341 6 : ScCompiler aComp(pDocument, aPos, *pCode);
2342 6 : aComp.SetGrammar(pDocument->GetGrammar());
2343 6 : pRangeData = aComp.UpdateInsertTab( nTable, false, nNewSheets );
2344 6 : if (pRangeData) // Exchange Shared Formula with real Formula
2345 : {
2346 : bool bRefChanged;
2347 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
2348 0 : delete pCode;
2349 0 : pCode = new ScTokenArray( *pRangeData->GetCode() );
2350 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
2351 0 : aComp2.SetGrammar(pDocument->GetGrammar());
2352 0 : aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
2353 0 : aComp2.UpdateInsertTab( nTable, false, nNewSheets );
2354 : // If the shared formula contained a named range/formula containing
2355 : // an absolute reference to a sheet, those have to be readjusted.
2356 0 : aComp2.UpdateDeleteTab( nTable, false, true, bRefChanged, nNewSheets );
2357 0 : bCompile = true;
2358 6 : }
2359 : // no StartListeningTo because pTab[nTab] does not exsist!
2360 : }
2361 0 : else if ( bPosChanged )
2362 0 : aPos.IncTab();
2363 6 : }
2364 :
2365 60 : bool ScFormulaCell::UpdateDeleteTab(SCTAB nTable, bool bIsMove, SCTAB nSheets)
2366 : {
2367 60 : bool bRefChanged = false;
2368 60 : bool bPosChanged = ( aPos.Tab() >= nTable + nSheets ? true : false );
2369 60 : pCode->Reset();
2370 60 : if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
2371 : {
2372 59 : EndListeningTo( pDocument );
2373 : // IncTab _after_ EndListeningTo und _before_ Compiler UpdateDeleteTab!
2374 59 : if ( bPosChanged )
2375 17 : aPos.IncTab(-1*nSheets);
2376 : ScRangeData* pRangeData;
2377 59 : ScCompiler aComp(pDocument, aPos, *pCode);
2378 59 : aComp.SetGrammar(pDocument->GetGrammar());
2379 59 : pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, false, bRefChanged, nSheets);
2380 59 : if (pRangeData) // Exchange Shared Formula with real Formula
2381 : {
2382 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
2383 0 : delete pCode;
2384 0 : pCode = pRangeData->GetCode()->Clone();
2385 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
2386 0 : aComp2.SetGrammar(pDocument->GetGrammar());
2387 0 : aComp2.CompileTokenArray();
2388 0 : aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
2389 0 : aComp2.UpdateDeleteTab( nTable, false, false, bRefChanged, nSheets );
2390 : // If the shared formula contained a named range/formula containing
2391 : // an absolute reference to a sheet, those have to be readjusted.
2392 0 : aComp2.UpdateInsertTab( nTable,true, nSheets );
2393 : // bRefChanged could have been reset at the last UpdateDeleteTab
2394 0 : bRefChanged = true;
2395 0 : bCompile = true;
2396 59 : }
2397 : // no StartListeningTo because pTab[nTab] not yet correct!
2398 : }
2399 1 : else if ( bPosChanged )
2400 0 : aPos.IncTab(-1*nSheets);
2401 :
2402 60 : return bRefChanged;
2403 : }
2404 :
2405 0 : void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
2406 : {
2407 0 : pCode->Reset();
2408 0 : if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
2409 : {
2410 0 : EndListeningTo( pDocument );
2411 : // SetTab _after_ EndListeningTo und _before_ Compiler UpdateMoveTab !
2412 0 : aPos.SetTab( nTabNo );
2413 : ScRangeData* pRangeData;
2414 0 : ScCompiler aComp(pDocument, aPos, *pCode);
2415 0 : aComp.SetGrammar(pDocument->GetGrammar());
2416 0 : pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, false );
2417 0 : if (pRangeData) // Exchange Shared Formula with real Formula
2418 : {
2419 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
2420 0 : delete pCode;
2421 0 : pCode = pRangeData->GetCode()->Clone();
2422 0 : ScCompiler aComp2(pDocument, aPos, *pCode);
2423 0 : aComp2.SetGrammar(pDocument->GetGrammar());
2424 0 : aComp2.CompileTokenArray();
2425 0 : aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
2426 0 : aComp2.UpdateMoveTab( nOldPos, nNewPos, true );
2427 0 : bCompile = true;
2428 0 : }
2429 : // no StartListeningTo because pTab[nTab] not yet correct!
2430 : }
2431 : else
2432 0 : aPos.SetTab( nTabNo );
2433 0 : }
2434 :
2435 0 : void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
2436 : {
2437 0 : if( !pDocument->IsClipOrUndo() )
2438 : {
2439 0 : pCode->Reset();
2440 0 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2441 0 : while( p )
2442 : {
2443 0 : ScSingleRefData& rRef1 = p->GetSingleRef();
2444 0 : if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab )
2445 0 : rRef1.nTab++;
2446 0 : if( p->GetType() == formula::svDoubleRef )
2447 : {
2448 0 : ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
2449 0 : if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab )
2450 0 : rRef2.nTab++;
2451 : }
2452 0 : p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2453 : }
2454 : }
2455 0 : }
2456 :
2457 2 : bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
2458 : {
2459 2 : bool bRet = false;
2460 2 : if( !pDocument->IsClipOrUndo() )
2461 : {
2462 2 : pCode->Reset();
2463 2 : ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2464 7 : while( p )
2465 : {
2466 3 : ScSingleRefData& rRef1 = p->GetSingleRef();
2467 3 : if( !rRef1.IsTabRel() )
2468 : {
2469 1 : if( (SCsTAB) nTable != rRef1.nTab )
2470 1 : bRet = true;
2471 0 : else if (nTable != aPos.Tab())
2472 0 : rRef1.nTab = aPos.Tab();
2473 : }
2474 3 : if( p->GetType() == formula::svDoubleRef )
2475 : {
2476 0 : ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
2477 0 : if( !rRef2.IsTabRel() )
2478 : {
2479 0 : if( (SCsTAB) nTable != rRef2.nTab )
2480 0 : bRet = true;
2481 0 : else if (nTable != aPos.Tab())
2482 0 : rRef2.nTab = aPos.Tab();
2483 : }
2484 : }
2485 3 : p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2486 : }
2487 : }
2488 2 : return bRet;
2489 : }
2490 :
2491 58 : void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
2492 : {
2493 58 : if ( bForceIfNameInUse && !bCompile )
2494 0 : bCompile = pCode->HasNameOrColRowName();
2495 58 : if ( bCompile )
2496 0 : pCode->SetCodeError( 0 ); // make sure it will really be compiled
2497 58 : CompileTokenArray();
2498 58 : }
2499 :
2500 : // Reference transposition is only called in Clipboard Document
2501 0 : void ScFormulaCell::TransposeReference()
2502 : {
2503 0 : bool bFound = false;
2504 0 : pCode->Reset();
2505 : ScToken* t;
2506 0 : while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
2507 : {
2508 0 : ScSingleRefData& rRef1 = t->GetSingleRef();
2509 0 : if ( rRef1.IsColRel() && rRef1.IsRowRel() )
2510 : {
2511 0 : bool bDouble = (t->GetType() == formula::svDoubleRef);
2512 0 : ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1);
2513 0 : if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
2514 : {
2515 : sal_Int16 nTemp;
2516 :
2517 0 : nTemp = rRef1.nRelCol;
2518 0 : rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow);
2519 0 : rRef1.nRelRow = static_cast<SCROW>(nTemp);
2520 :
2521 0 : if ( bDouble )
2522 : {
2523 0 : nTemp = rRef2.nRelCol;
2524 0 : rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
2525 0 : rRef2.nRelRow = static_cast<SCROW>(nTemp);
2526 : }
2527 :
2528 0 : bFound = true;
2529 : }
2530 : }
2531 : }
2532 :
2533 0 : if (bFound)
2534 0 : bCompile = true;
2535 0 : }
2536 :
2537 0 : void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
2538 : ScDocument* pUndoDoc )
2539 : {
2540 0 : EndListeningTo( pDocument );
2541 :
2542 0 : ScAddress aOldPos = aPos;
2543 0 : bool bPosChanged = false; // Whether this cell has been moved
2544 :
2545 : ScRange aDestRange( rDest, ScAddress(
2546 0 : static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
2547 0 : static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
2548 0 : rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
2549 0 : if ( aDestRange.In( aOldPos ) )
2550 : {
2551 : // Count back Positions
2552 0 : SCsCOL nRelPosX = aOldPos.Col();
2553 0 : SCsROW nRelPosY = aOldPos.Row();
2554 0 : SCsTAB nRelPosZ = aOldPos.Tab();
2555 0 : ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
2556 0 : aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
2557 0 : bPosChanged = true;
2558 : }
2559 :
2560 0 : ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
2561 0 : bool bRefChanged = false;
2562 : ScToken* t;
2563 :
2564 0 : ScRangeData* pShared = NULL;
2565 0 : pCode->Reset();
2566 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
2567 : {
2568 0 : if( t->GetOpCode() == ocName )
2569 : {
2570 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
2571 0 : if (pName)
2572 : {
2573 0 : if (pName->IsModified())
2574 0 : bRefChanged = true;
2575 0 : if (pName->HasType(RT_SHAREDMOD))
2576 0 : pShared = pName;
2577 : }
2578 : }
2579 0 : else if( t->GetType() != svIndex )
2580 : {
2581 0 : t->CalcAbsIfRel( aOldPos );
2582 : bool bMod;
2583 : { // Own scope for SingleDoubleRefModifier dtor if SingleRef
2584 0 : SingleDoubleRefModifier aMod( *t );
2585 0 : ScComplexRefData& rRef = aMod.Ref();
2586 : bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
2587 0 : rDest, rRef ) != UR_NOTHING || bPosChanged);
2588 : }
2589 0 : if ( bMod )
2590 : {
2591 0 : t->CalcRelFromAbs( aPos );
2592 0 : bRefChanged = true;
2593 : }
2594 : }
2595 : }
2596 :
2597 0 : if (pShared) // Exchange Shared Formula with real Formula
2598 : {
2599 0 : pDocument->RemoveFromFormulaTree( this ); // update formula count
2600 0 : delete pCode;
2601 0 : pCode = new ScTokenArray( *pShared->GetCode() );
2602 0 : bRefChanged = true;
2603 0 : pCode->Reset();
2604 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
2605 : {
2606 0 : if( t->GetType() != svIndex )
2607 : {
2608 0 : t->CalcAbsIfRel( aOldPos );
2609 : bool bMod;
2610 : { // Own scope for SingleDoubleRefModifier dtor if SingleRef
2611 0 : SingleDoubleRefModifier aMod( *t );
2612 0 : ScComplexRefData& rRef = aMod.Ref();
2613 : bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
2614 0 : rDest, rRef ) != UR_NOTHING || bPosChanged);
2615 : }
2616 0 : if ( bMod )
2617 0 : t->CalcRelFromAbs( aPos );
2618 : }
2619 : }
2620 : }
2621 :
2622 0 : if (bRefChanged)
2623 : {
2624 0 : if (pUndoDoc)
2625 : {
2626 : ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld,
2627 0 : eTempGrammar, cMatrixFlag);
2628 0 : pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
2629 0 : pUndoDoc->SetFormulaCell(aPos, pFCell);
2630 : }
2631 :
2632 0 : bCompile = true;
2633 0 : CompileTokenArray(); // also call StartListeningTo
2634 0 : SetDirty();
2635 : }
2636 : else
2637 0 : StartListeningTo( pDocument ); // Listener as previous
2638 :
2639 0 : delete pOld;
2640 0 : }
2641 :
2642 0 : void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
2643 : {
2644 0 : EndListeningTo( pDocument );
2645 :
2646 0 : bool bRefChanged = false;
2647 : ScToken* t;
2648 0 : ScRangeData* pShared = NULL;
2649 :
2650 0 : pCode->Reset();
2651 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
2652 : {
2653 0 : if( t->GetOpCode() == ocName )
2654 : {
2655 0 : ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
2656 0 : if (pName)
2657 : {
2658 0 : if (pName->IsModified())
2659 0 : bRefChanged = true;
2660 0 : if (pName->HasType(RT_SHAREDMOD))
2661 0 : pShared = pName;
2662 : }
2663 : }
2664 0 : else if( t->GetType() != svIndex )
2665 : {
2666 0 : t->CalcAbsIfRel( aPos );
2667 : bool bMod;
2668 : { // Own scope for SingleDoubleRefModifier dtor if SingleRef
2669 0 : SingleDoubleRefModifier aMod( *t );
2670 0 : ScComplexRefData& rRef = aMod.Ref();
2671 : bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
2672 0 : rRef ) != UR_NOTHING);
2673 : }
2674 0 : if ( bMod )
2675 : {
2676 0 : t->CalcRelFromAbs( aPos );
2677 0 : bRefChanged = true;
2678 : }
2679 : }
2680 : }
2681 :
2682 0 : if (pShared) // Exchange Shared Formula with real Formula
2683 : {
2684 0 : pDocument->RemoveFromFormulaTree( this ); // Update formula count
2685 0 : delete pCode;
2686 0 : pCode = new ScTokenArray( *pShared->GetCode() );
2687 0 : bRefChanged = true;
2688 0 : pCode->Reset();
2689 0 : while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
2690 : {
2691 0 : if( t->GetType() != svIndex )
2692 : {
2693 0 : t->CalcAbsIfRel( aPos );
2694 : bool bMod;
2695 : { // Own scope for SingleDoubleRefModifier dtor if SingleRef
2696 0 : SingleDoubleRefModifier aMod( *t );
2697 0 : ScComplexRefData& rRef = aMod.Ref();
2698 : bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
2699 0 : rRef ) != UR_NOTHING);
2700 : }
2701 0 : if ( bMod )
2702 0 : t->CalcRelFromAbs( aPos );
2703 : }
2704 : }
2705 : }
2706 :
2707 0 : if (bRefChanged)
2708 : {
2709 0 : bCompile = true;
2710 0 : CompileTokenArray(); // Also call StartListeningTo
2711 0 : SetDirty();
2712 : }
2713 : else
2714 0 : StartListeningTo( pDocument ); // Listener as previous
2715 0 : }
2716 :
2717 3 : static void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
2718 : {
2719 13 : for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
2720 : {
2721 10 : if (p->GetOpCode() == ocName)
2722 : {
2723 2 : sal_uInt16 nTokenIndex = p->GetIndex();
2724 2 : rIndexes.insert( nTokenIndex );
2725 :
2726 2 : ScRangeData* pSubName = pNames->findByIndex(p->GetIndex());
2727 2 : if (pSubName)
2728 2 : lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
2729 : }
2730 : }
2731 3 : }
2732 :
2733 1 : void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
2734 : {
2735 1 : lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
2736 1 : }
2737 :
2738 4050 : bool ScFormulaCell::IsChanged() const
2739 : {
2740 4050 : return bChanged;
2741 : }
2742 :
2743 291 : void ScFormulaCell::SetChanged(bool b)
2744 : {
2745 291 : bChanged = b;
2746 291 : }
2747 :
2748 3 : void ScFormulaCell::CompileDBFormula()
2749 : {
2750 24 : for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
2751 : {
2752 42 : if ( p->GetOpCode() == ocDBArea
2753 21 : || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) )
2754 : {
2755 0 : bCompile = true;
2756 0 : CompileTokenArray();
2757 0 : SetDirty();
2758 0 : break;
2759 : }
2760 : }
2761 3 : }
2762 :
2763 14 : void ScFormulaCell::CompileDBFormula( bool bCreateFormulaString )
2764 : {
2765 : // Two phases must be called after each other
2766 : // 1. Formula String with old generated names
2767 : // 2. Formula String with new generated names
2768 14 : if ( bCreateFormulaString )
2769 : {
2770 7 : bool bRecompile = false;
2771 7 : pCode->Reset();
2772 35 : for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
2773 : {
2774 28 : switch ( p->GetOpCode() )
2775 : {
2776 : case ocBad: // DB Area eventually goes bad
2777 : case ocColRowName: // in case of the same names
2778 : case ocDBArea: // DB Area
2779 0 : bRecompile = true;
2780 0 : break;
2781 : case ocName:
2782 0 : if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
2783 0 : bRecompile = true; // DB Area
2784 0 : break;
2785 : default:
2786 : ; // nothing
2787 : }
2788 : }
2789 7 : if ( bRecompile )
2790 : {
2791 0 : OUString aFormula;
2792 0 : GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
2793 0 : if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
2794 : {
2795 0 : if ( aFormula[ aFormula.getLength()-1 ] == '}' )
2796 0 : aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
2797 0 : if ( aFormula[0] == '{' )
2798 0 : aFormula = aFormula.copy( 1 );
2799 : }
2800 0 : EndListeningTo( pDocument );
2801 0 : pDocument->RemoveFromFormulaTree( this );
2802 0 : pCode->Clear();
2803 0 : SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
2804 : }
2805 : }
2806 7 : else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
2807 : {
2808 0 : Compile( aResult.GetHybridFormula(), false, eTempGrammar );
2809 0 : aResult.SetToken( NULL);
2810 0 : SetDirty();
2811 : }
2812 14 : }
2813 :
2814 42 : void ScFormulaCell::CompileNameFormula( bool bCreateFormulaString )
2815 : {
2816 : // Two phases must be called after each other
2817 : // 1. Formula String with old generated names
2818 : // 2. Formula String with new generated names
2819 42 : if ( bCreateFormulaString )
2820 : {
2821 0 : bool bRecompile = false;
2822 0 : pCode->Reset();
2823 0 : for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
2824 : {
2825 0 : switch ( p->GetOpCode() )
2826 : {
2827 : case ocBad: // in case RangeName goes bad
2828 : case ocColRowName: // in case the names are the same
2829 0 : bRecompile = true;
2830 0 : break;
2831 : default:
2832 0 : if ( p->GetType() == svIndex )
2833 0 : bRecompile = true; // RangeName
2834 : }
2835 : }
2836 0 : if ( bRecompile )
2837 : {
2838 0 : OUString aFormula;
2839 0 : GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
2840 0 : if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
2841 : {
2842 0 : if ( aFormula[ aFormula.getLength()-1 ] == '}' )
2843 0 : aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
2844 0 : if ( aFormula[0] == '{' )
2845 0 : aFormula = aFormula.copy( 1 );
2846 : }
2847 0 : EndListeningTo( pDocument );
2848 0 : pDocument->RemoveFromFormulaTree( this );
2849 0 : pCode->Clear();
2850 0 : SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
2851 : }
2852 : }
2853 42 : else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
2854 : {
2855 0 : Compile( aResult.GetHybridFormula(), false, eTempGrammar );
2856 0 : aResult.SetToken( NULL);
2857 0 : SetDirty();
2858 : }
2859 42 : }
2860 :
2861 0 : void ScFormulaCell::CompileColRowNameFormula()
2862 : {
2863 0 : pCode->Reset();
2864 0 : for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
2865 : {
2866 0 : if ( p->GetOpCode() == ocColRowName )
2867 : {
2868 0 : bCompile = true;
2869 0 : CompileTokenArray();
2870 0 : SetDirty();
2871 0 : break;
2872 : }
2873 : }
2874 0 : }
2875 :
2876 2469 : ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( ScFormulaCell& rOther ) const
2877 : {
2878 : // no Matrix formulae yet.
2879 2469 : if ( GetMatrixFlag() != MM_NONE )
2880 93 : return NotEqual;
2881 :
2882 : // are these formule at all similar ?
2883 2376 : if ( GetHash() != rOther.GetHash() )
2884 670 : return NotEqual;
2885 :
2886 1706 : FormulaToken **pThis = pCode->GetCode();
2887 1706 : sal_uInt16 nThisLen = pCode->GetCodeLen();
2888 1706 : FormulaToken **pOther = rOther.pCode->GetCode();
2889 1706 : sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
2890 :
2891 1706 : if ( !pThis || !pOther )
2892 : {
2893 : // Error: no compiled code for cells !"
2894 241 : return NotEqual;
2895 : }
2896 :
2897 1465 : if ( nThisLen != nOtherLen )
2898 0 : return NotEqual;
2899 :
2900 1465 : bool bInvariant = true;
2901 :
2902 : // check we are basically the same function
2903 11992 : for ( sal_uInt16 i = 0; i < nThisLen; i++ )
2904 : {
2905 10662 : ScToken *pThisTok = static_cast<ScToken*>( pThis[i] );
2906 10662 : ScToken *pOtherTok = static_cast<ScToken*>( pOther[i] );
2907 :
2908 31985 : if ( pThisTok->GetType() != pOtherTok->GetType() ||
2909 21323 : pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
2910 10661 : pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
2911 : {
2912 : // Incompatible type, op-code or param counts.
2913 1 : return NotEqual;
2914 : }
2915 :
2916 10661 : switch (pThisTok->GetType())
2917 : {
2918 : case formula::svMatrix:
2919 : case formula::svExternalSingleRef:
2920 : case formula::svExternalDoubleRef:
2921 : // Ignoring matrix and external references for now.
2922 18 : return NotEqual;
2923 :
2924 : case formula::svSingleRef:
2925 : {
2926 : // Single cell reference.
2927 2360 : const ScSingleRefData& rRef = pThisTok->GetSingleRef();
2928 2360 : if (rRef != pOtherTok->GetSingleRef())
2929 52 : return NotEqual;
2930 :
2931 2308 : if (rRef.IsRowRel())
2932 2248 : bInvariant = false;
2933 : }
2934 2308 : break;
2935 : case formula::svDoubleRef:
2936 : {
2937 : // Range reference.
2938 1194 : const ScSingleRefData& rRef1 = pThisTok->GetSingleRef();
2939 1194 : const ScSingleRefData& rRef2 = pThisTok->GetSingleRef2();
2940 1194 : if (rRef1 != pOtherTok->GetSingleRef())
2941 64 : return NotEqual;
2942 :
2943 1130 : if (rRef2 != pOtherTok->GetSingleRef2())
2944 0 : return NotEqual;
2945 :
2946 1130 : if (rRef1.IsRowRel())
2947 1130 : bInvariant = false;
2948 :
2949 1130 : if (rRef2.IsRowRel())
2950 1130 : bInvariant = false;
2951 : }
2952 1130 : break;
2953 : default:
2954 : ;
2955 : }
2956 : }
2957 :
2958 1330 : return bInvariant ? EqualInvariant : EqualRelativeRef;
2959 : }
2960 :
2961 : namespace {
2962 :
2963 : class GroupTokenConverter
2964 : {
2965 : sc::FormulaGroupContext& mrCxt;
2966 : ScTokenArray& mrGroupTokens;
2967 : ScDocument& mrDoc;
2968 : ScFormulaCell& mrCell;
2969 : const ScAddress& mrPos;
2970 : public:
2971 0 : GroupTokenConverter(sc::FormulaGroupContext& rCxt, ScTokenArray& rGroupTokens, ScDocument& rDoc, ScFormulaCell& rCell, const ScAddress& rPos) :
2972 0 : mrCxt(rCxt), mrGroupTokens(rGroupTokens), mrDoc(rDoc), mrCell(rCell), mrPos(rPos) {}
2973 :
2974 0 : bool convert(ScTokenArray& rCode)
2975 : {
2976 0 : rCode.Reset();
2977 0 : for (const formula::FormulaToken* p = rCode.First(); p; p = rCode.Next())
2978 : {
2979 : // A reference can be either absolute or relative. If it's absolute,
2980 : // convert it to a static value token. If relative, convert it to a
2981 : // vector reference token. Note: we only care about relative vs
2982 : // absolute reference state for row directions.
2983 :
2984 0 : const ScToken* pToken = static_cast<const ScToken*>(p);
2985 0 : switch (pToken->GetType())
2986 : {
2987 : case svSingleRef:
2988 : {
2989 0 : ScSingleRefData aRef = pToken->GetSingleRef();
2990 0 : ScAddress aRefPos = aRef.toAbs(mrPos);
2991 0 : if (aRef.IsRowRel())
2992 : {
2993 : // Fetch double array guarantees that the length of the
2994 : // returned array equals or greater than the requested
2995 : // length.
2996 :
2997 : // TODO: For now, it returns an array pointer only when
2998 : // the entire array is in contiguous memory space. Once
2999 : // we finish cell storage rework, we'll support temporary
3000 : // generation of a double array which is a combination of
3001 : // multiple cell array segments.
3002 0 : const double* pArray = mrDoc.FetchDoubleArray(mrCxt, aRefPos, mrCell.GetCellGroup()->mnLength);
3003 0 : if (!pArray)
3004 0 : return false;
3005 :
3006 0 : formula::SingleVectorRefToken aTok(pArray, mrCell.GetCellGroup()->mnLength);
3007 0 : mrGroupTokens.AddToken(aTok);
3008 : }
3009 : else
3010 : {
3011 : // Absolute row reference.
3012 0 : formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefPos);
3013 0 : if (!pNewToken)
3014 0 : return false;
3015 :
3016 0 : mrGroupTokens.AddToken(*pNewToken);
3017 : }
3018 : }
3019 0 : break;
3020 : case svDoubleRef:
3021 : {
3022 0 : ScComplexRefData aRef = pToken->GetDoubleRef();
3023 0 : aRef.CalcAbsIfRel(mrCell.aPos);
3024 0 : if (aRef.Ref1.IsRowRel() || aRef.Ref2.IsRowRel())
3025 : {
3026 : // Row reference is relative.
3027 0 : bool bAbsFirst = !aRef.Ref1.IsRowRel();
3028 0 : bool bAbsLast = !aRef.Ref2.IsRowRel();
3029 0 : ScAddress aRefPos(aRef.Ref1.nCol, aRef.Ref1.nRow, aRef.Ref1.nTab);
3030 0 : size_t nCols = aRef.Ref2.nCol - aRef.Ref1.nCol + 1;
3031 0 : std::vector<const double*> aArrays;
3032 0 : aArrays.reserve(nCols);
3033 0 : SCROW nArrayLength = mrCell.GetCellGroup()->mnLength;
3034 0 : SCROW nRefRowSize = aRef.Ref2.nRow - aRef.Ref1.nRow + 1;
3035 0 : if (!bAbsLast)
3036 : {
3037 : // range end position is relative. Extend the array length.
3038 0 : nArrayLength += nRefRowSize - 1;
3039 : }
3040 :
3041 0 : for (SCCOL i = aRef.Ref1.nCol; i <= aRef.Ref2.nCol; ++i)
3042 : {
3043 0 : aRefPos.SetCol(i);
3044 0 : const double* pArray = mrDoc.FetchDoubleArray(mrCxt, aRefPos, nArrayLength);
3045 0 : if (!pArray)
3046 0 : return false;
3047 :
3048 0 : aArrays.push_back(pArray);
3049 : }
3050 :
3051 0 : formula::DoubleVectorRefToken aTok(aArrays, nArrayLength, nRefRowSize, bAbsFirst, bAbsLast);
3052 0 : mrGroupTokens.AddToken(aTok);
3053 : }
3054 : else
3055 : {
3056 : // Absolute row reference.
3057 : ScRange aRefRange(
3058 : aRef.Ref1.nCol, aRef.Ref1.nRow, aRef.Ref1.nTab,
3059 0 : aRef.Ref2.nCol, aRef.Ref2.nRow, aRef.Ref2.nTab);
3060 :
3061 0 : formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefRange);
3062 0 : if (!pNewToken)
3063 0 : return false;
3064 :
3065 0 : mrGroupTokens.AddToken(*pNewToken);
3066 : }
3067 : }
3068 0 : break;
3069 : case svIndex:
3070 : {
3071 : // Named range.
3072 0 : ScRangeName* pNames = mrDoc.GetRangeName();
3073 0 : if (!pNames)
3074 : // This should never fail.
3075 0 : return false;
3076 :
3077 0 : ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
3078 0 : if (!pRange)
3079 : // No named range exists by that index.
3080 0 : return false;
3081 :
3082 0 : ScTokenArray* pNamedTokens = pRange->GetCode();
3083 0 : if (!pNamedTokens)
3084 : // This named range is empty.
3085 0 : return false;
3086 :
3087 0 : mrGroupTokens.AddOpCode(ocOpen);
3088 :
3089 0 : if (!convert(*pNamedTokens))
3090 0 : return false;
3091 :
3092 0 : mrGroupTokens.AddOpCode(ocClose);
3093 : }
3094 0 : break;
3095 : default:
3096 0 : mrGroupTokens.AddToken(*pToken);
3097 : }
3098 : }
3099 :
3100 0 : return true;
3101 : }
3102 : };
3103 :
3104 : }
3105 :
3106 4679 : bool ScFormulaCell::InterpretFormulaGroup()
3107 : {
3108 4679 : if (!ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
3109 4679 : return false;
3110 :
3111 : // Re-build formulae groups if necessary - ideally this is done at
3112 : // import / insert / delete etc. and is integral to the data structures
3113 0 : pDocument->RebuildFormulaGroups();
3114 :
3115 0 : if (!xGroup || !pCode)
3116 0 : return false;
3117 :
3118 0 : if (xGroup->meCalcState == sc::GroupCalcDisabled)
3119 0 : return false;
3120 :
3121 0 : switch (pCode->GetVectorState())
3122 : {
3123 : case FormulaVectorEnabled:
3124 : case FormulaVectorCheckReference:
3125 : // Good.
3126 0 : break;
3127 : case FormulaVectorDisabled:
3128 : case FormulaVectorUnknown:
3129 : default:
3130 : // Not good.
3131 0 : return false;
3132 : }
3133 :
3134 0 : if (xGroup->mbInvariant)
3135 0 : return InterpretInvariantFormulaGroup();
3136 :
3137 0 : sc::FormulaGroupContext aCxt;
3138 0 : ScTokenArray aCode;
3139 0 : ScAddress aTopPos = aPos;
3140 0 : aTopPos.SetRow(xGroup->mnStart);
3141 0 : GroupTokenConverter aConverter(aCxt, aCode, *pDocument, *this, aTopPos);
3142 0 : if (!aConverter.convert(*pCode))
3143 : {
3144 0 : xGroup->meCalcState = sc::GroupCalcDisabled;
3145 0 : return false;
3146 : }
3147 :
3148 0 : xGroup->meCalcState = sc::GroupCalcRunning;
3149 0 : if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, aTopPos, xGroup, aCode))
3150 : {
3151 0 : xGroup->meCalcState = sc::GroupCalcDisabled;
3152 0 : return false;
3153 : }
3154 :
3155 0 : xGroup->meCalcState = sc::GroupCalcEnabled;
3156 0 : return true;
3157 : }
3158 :
3159 0 : bool ScFormulaCell::InterpretInvariantFormulaGroup()
3160 : {
3161 0 : if (pCode->GetVectorState() == FormulaVectorCheckReference)
3162 : {
3163 : // An invariant group should only have absolute row references, and no
3164 : // external references are allowed.
3165 :
3166 0 : ScTokenArray aCode;
3167 0 : pCode->Reset();
3168 0 : for (const formula::FormulaToken* p = pCode->First(); p; p = pCode->Next())
3169 : {
3170 0 : const ScToken* pToken = static_cast<const ScToken*>(p);
3171 0 : switch (pToken->GetType())
3172 : {
3173 : case svSingleRef:
3174 : {
3175 0 : ScSingleRefData aRef = pToken->GetSingleRef();
3176 0 : aRef.CalcAbsIfRel(aPos); // column may be relative.
3177 0 : ScAddress aRefPos(aRef.nCol, aRef.nRow, aRef.nTab);
3178 0 : formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefPos);
3179 0 : if (!pNewToken)
3180 0 : return false;
3181 :
3182 0 : aCode.AddToken(*pNewToken);
3183 : }
3184 0 : break;
3185 : case svDoubleRef:
3186 : {
3187 0 : ScComplexRefData aRef = pToken->GetDoubleRef();
3188 0 : aRef.CalcAbsIfRel(aPos); // column may be relative.
3189 : ScRange aRefRange(
3190 : aRef.Ref1.nCol, aRef.Ref1.nRow, aRef.Ref1.nTab,
3191 0 : aRef.Ref2.nCol, aRef.Ref2.nRow, aRef.Ref2.nTab);
3192 :
3193 0 : formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefRange);
3194 0 : if (!pNewToken)
3195 0 : return false;
3196 :
3197 0 : aCode.AddToken(*pNewToken);
3198 : }
3199 0 : break;
3200 : default:
3201 0 : aCode.AddToken(*pToken);
3202 : }
3203 : }
3204 :
3205 0 : ScCompiler aComp(pDocument, aPos, aCode);
3206 0 : aComp.SetGrammar(pDocument->GetGrammar());
3207 0 : aComp.CompileTokenArray(); // Create RPN token array.
3208 0 : ScInterpreter aInterpreter(this, pDocument, aPos, aCode);
3209 0 : aInterpreter.Interpret();
3210 0 : aResult.SetToken(aInterpreter.GetResultToken().get());
3211 : }
3212 : else
3213 : {
3214 : // Formula contains no references.
3215 0 : ScInterpreter aInterpreter(this, pDocument, aPos, *pCode);
3216 0 : aInterpreter.Interpret();
3217 0 : aResult.SetToken(aInterpreter.GetResultToken().get());
3218 : }
3219 :
3220 0 : for ( sal_Int32 i = 0; i < xGroup->mnLength; i++ )
3221 : {
3222 0 : ScAddress aTmpPos = aPos;
3223 0 : aTmpPos.SetRow(xGroup->mnStart + i);
3224 0 : ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
3225 : assert( pCell != NULL );
3226 :
3227 : // FIXME: this set of horrors is unclear to me ... certainly
3228 : // the above GetCell is profoundly nasty & slow ...
3229 :
3230 : // Ensure the cell truly has a result:
3231 0 : pCell->aResult = aResult;
3232 0 : pCell->ResetDirty();
3233 0 : pCell->SetChanged(true);
3234 : }
3235 :
3236 0 : return true;
3237 : }
3238 :
3239 : namespace {
3240 :
3241 2292 : void startListeningArea(
3242 : ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const ScToken& rToken)
3243 : {
3244 2292 : const ScSingleRefData& rRef1 = rToken.GetSingleRef();
3245 2292 : const ScSingleRefData& rRef2 = rToken.GetSingleRef2();
3246 2292 : ScAddress aCell1 = rRef1.toAbs(rPos);
3247 2292 : ScAddress aCell2 = rRef2.toAbs(rPos);
3248 2292 : if (aCell1.IsValid() && aCell2.IsValid())
3249 : {
3250 2292 : if (rToken.GetOpCode() == ocColRowNameAuto)
3251 : { // automagically
3252 0 : if ( rRef1.IsColRel() )
3253 : { // ColName
3254 0 : aCell2.SetRow(MAXROW);
3255 : }
3256 : else
3257 : { // RowName
3258 0 : aCell2.SetCol(MAXCOL);
3259 : }
3260 : }
3261 2292 : rDoc.StartListeningArea(ScRange(aCell1, aCell2), pCell);
3262 : }
3263 2292 : }
3264 :
3265 : }
3266 :
3267 4256 : void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
3268 : {
3269 4256 : if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
3270 8 : return;
3271 :
3272 4248 : pDoc->SetDetectiveDirty(true); // It has changed something
3273 :
3274 4248 : ScTokenArray* pArr = GetCode();
3275 4248 : if( pArr->IsRecalcModeAlways() )
3276 : {
3277 24 : pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, this);
3278 24 : SetNeedsListening( false);
3279 24 : return;
3280 : }
3281 :
3282 4224 : pArr->Reset();
3283 : ScToken* t;
3284 16404 : while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
3285 : {
3286 7956 : switch (t->GetType())
3287 : {
3288 : case svSingleRef:
3289 : {
3290 5635 : ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3291 5635 : if (aCell.IsValid())
3292 5622 : pDoc->StartListeningCell(aCell, this);
3293 : }
3294 5635 : break;
3295 : case svDoubleRef:
3296 2291 : startListeningArea(this, *pDoc, aPos, *t);
3297 2291 : break;
3298 : default:
3299 : ; // nothing
3300 : }
3301 : }
3302 4224 : SetNeedsListening( false);
3303 : }
3304 :
3305 12 : void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
3306 : {
3307 12 : ScDocument& rDoc = rCxt.getDoc();
3308 :
3309 12 : if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
3310 0 : return;
3311 :
3312 12 : rDoc.SetDetectiveDirty(true); // It has changed something
3313 :
3314 12 : ScTokenArray* pArr = GetCode();
3315 12 : if( pArr->IsRecalcModeAlways() )
3316 : {
3317 0 : rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, this);
3318 0 : SetNeedsListening( false);
3319 0 : return;
3320 : }
3321 :
3322 12 : pArr->Reset();
3323 : ScToken* t;
3324 40 : while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
3325 : {
3326 16 : switch (t->GetType())
3327 : {
3328 : case svSingleRef:
3329 : {
3330 12 : ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3331 12 : if (aCell.IsValid())
3332 12 : rDoc.StartListeningCell(rCxt, aCell, *this);
3333 : }
3334 12 : break;
3335 : case svDoubleRef:
3336 1 : startListeningArea(this, rDoc, aPos, *t);
3337 1 : break;
3338 : default:
3339 : ; // nothing
3340 : }
3341 : }
3342 12 : SetNeedsListening( false);
3343 : }
3344 :
3345 : namespace {
3346 :
3347 65 : void endListeningArea(
3348 : ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const ScToken& rToken)
3349 : {
3350 65 : const ScSingleRefData& rRef1 = rToken.GetSingleRef();
3351 65 : const ScSingleRefData& rRef2 = rToken.GetSingleRef2();
3352 65 : ScAddress aCell1 = rRef1.toAbs(rPos);
3353 65 : ScAddress aCell2 = rRef2.toAbs(rPos);
3354 65 : if (aCell1.IsValid() && aCell2.IsValid())
3355 : {
3356 65 : if (rToken.GetOpCode() == ocColRowNameAuto)
3357 : { // automagically
3358 0 : if ( rRef1.IsColRel() )
3359 : { // ColName
3360 0 : aCell2.SetRow(MAXROW);
3361 : }
3362 : else
3363 : { // RowName
3364 0 : aCell2.SetCol(MAXCOL);
3365 : }
3366 : }
3367 :
3368 65 : rDoc.EndListeningArea(ScRange(aCell1, aCell2), pCell);
3369 : }
3370 65 : }
3371 :
3372 : }
3373 :
3374 281 : void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
3375 : ScAddress aCellPos )
3376 : {
3377 281 : if (pDoc->IsClipOrUndo() || IsInChangeTrack())
3378 0 : return;
3379 :
3380 281 : pDoc->SetDetectiveDirty(true); // It has changed something
3381 :
3382 281 : if ( GetCode()->IsRecalcModeAlways() )
3383 : {
3384 0 : pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, this );
3385 0 : return;
3386 : }
3387 :
3388 281 : if (!pArr)
3389 : {
3390 281 : pArr = GetCode();
3391 281 : aCellPos = aPos;
3392 : }
3393 281 : pArr->Reset();
3394 : ScToken* t;
3395 874 : while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
3396 : {
3397 312 : switch (t->GetType())
3398 : {
3399 : case svSingleRef:
3400 : {
3401 249 : ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3402 249 : if (aCell.IsValid())
3403 248 : pDoc->EndListeningCell(aCell, this);
3404 : }
3405 249 : break;
3406 : case svDoubleRef:
3407 57 : endListeningArea(this, *pDoc, aCellPos, *t);
3408 57 : break;
3409 : default:
3410 : ; // nothing
3411 : }
3412 : }
3413 : }
3414 :
3415 117 : void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
3416 : {
3417 117 : if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
3418 0 : return;
3419 :
3420 117 : ScDocument& rDoc = rCxt.getDoc();
3421 117 : rDoc.SetDetectiveDirty(true); // It has changed something
3422 :
3423 117 : if (pCode->IsRecalcModeAlways())
3424 : {
3425 0 : rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, this);
3426 0 : return;
3427 : }
3428 :
3429 117 : pCode->Reset();
3430 : ScToken* t;
3431 281 : while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
3432 : {
3433 47 : switch (t->GetType())
3434 : {
3435 : case svSingleRef:
3436 : {
3437 38 : ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3438 38 : if (aCell.IsValid())
3439 38 : rDoc.EndListeningCell(rCxt, aCell, *this);
3440 : }
3441 38 : break;
3442 : case svDoubleRef:
3443 8 : endListeningArea(this, rDoc, aPos, *t);
3444 8 : break;
3445 : default:
3446 : ; // nothing
3447 : }
3448 : }
3449 : }
3450 :
3451 284 : bool ScFormulaCell::IsShared() const
3452 : {
3453 284 : return xGroup.get() != NULL;
3454 : }
3455 :
3456 0 : bool ScFormulaCell::IsSharedInvariant() const
3457 : {
3458 0 : return xGroup ? xGroup->mbInvariant : false;
3459 : }
3460 :
3461 152 : SCROW ScFormulaCell::GetSharedTopRow() const
3462 : {
3463 152 : return xGroup ? xGroup->mnStart : -1;
3464 : }
3465 74 : SCROW ScFormulaCell::GetSharedLength() const
3466 : {
3467 74 : return xGroup ? xGroup->mnLength : 0;
3468 93 : }
3469 :
3470 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|