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