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