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