fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / data / formulacell.cxx
blob1bb5e4ce71d6e480b30085b8ec90cc71778006d3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <cassert>
23 #include <cstdlib>
25 #include "formulacell.hxx"
26 #include "grouptokenconverter.hxx"
28 #include "compiler.hxx"
29 #include "document.hxx"
30 #include "globalnames.hxx"
31 #include "cellvalue.hxx"
32 #include "interpre.hxx"
33 #include "macromgr.hxx"
34 #include "refupdat.hxx"
35 #include "recursionhelper.hxx"
36 #include "docoptio.hxx"
37 #include "rangenam.hxx"
38 #include "dbdata.hxx"
39 #include "progress.hxx"
40 #include "scmatrix.hxx"
41 #include "rechead.hxx"
42 #include "scitems.hxx"
43 #include "validat.hxx"
44 #include "editutil.hxx"
45 #include "chgtrack.hxx"
46 #include "tokenarray.hxx"
47 #include "clkernelthread.hxx"
49 #include <formula/errorcodes.hxx>
50 #include <formula/vectortoken.hxx>
51 #include <svl/intitem.hxx>
52 #include <rtl/strbuf.hxx>
53 #include "formulagroup.hxx"
54 #include "listenercontext.hxx"
55 #include "types.hxx"
56 #include "scopetools.hxx"
57 #include "refupdatecontext.hxx"
58 #include <opencl/openclwrapper.hxx>
59 #include <tokenstringcontext.hxx>
60 #include <refhint.hxx>
61 #include <listenerquery.hxx>
62 #include <listenerqueryids.hxx>
63 #include <grouparealistener.hxx>
64 #include <officecfg/Office/Common.hxx>
66 #include <boost/scoped_ptr.hpp>
67 #include <boost/ptr_container/ptr_map.hpp>
69 using namespace formula;
71 #ifdef USE_MEMPOOL
72 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell )
73 #endif
75 namespace {
77 // More or less arbitrary, of course all recursions must fit into available
78 // stack space (which is what on all systems we don't know yet?). Choosing a
79 // lower value may be better than trying a much higher value that also isn't
80 // sufficient but temporarily leads to high memory consumption. On the other
81 // hand, if the value fits all recursions, execution is quicker as no resumes
82 // are necessary. Could be made a configurable option.
83 // Allow for a year's calendar (366).
84 const sal_uInt16 MAXRECURSION = 400;
86 using std::deque;
88 typedef SCCOLROW(*DimensionSelector)(const ScAddress&, const ScSingleRefData&);
90 static SCCOLROW lcl_GetCol(const ScAddress& rPos, const ScSingleRefData& rData)
92 return rData.toAbs(rPos).Col();
95 static SCCOLROW lcl_GetRow(const ScAddress& rPos, const ScSingleRefData& rData)
97 return rData.toAbs(rPos).Row();
100 static SCCOLROW lcl_GetTab(const ScAddress& rPos, const ScSingleRefData& rData)
102 return rData.toAbs(rPos).Tab();
105 /** Check if both references span the same range in selected dimension.
107 static bool
108 lcl_checkRangeDimension(
109 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
110 const DimensionSelector aWhich)
112 return aWhich(rPos, rRef1.Ref1) == aWhich(rPos, rRef2.Ref1) &&
113 aWhich(rPos, rRef1.Ref2) == aWhich(rPos, rRef2.Ref2);
116 static bool
117 lcl_checkRangeDimensions(
118 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
119 bool& bCol, bool& bRow, bool& bTab)
121 const bool bSameCols(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetCol));
122 const bool bSameRows(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetRow));
123 const bool bSameTabs(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetTab));
125 // Test if exactly two dimensions are equal
126 if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
128 bCol = !bSameCols;
129 bRow = !bSameRows;
130 bTab = !bSameTabs;
131 return true;
133 return false;
136 /** Check if references in given reference list can possibly
137 form a range. To do that, two of their dimensions must be the same.
139 static bool
140 lcl_checkRangeDimensions(
141 const ScAddress& rPos,
142 const deque<formula::FormulaToken*>::const_iterator& rBegin,
143 const deque<formula::FormulaToken*>::const_iterator& rEnd,
144 bool& bCol, bool& bRow, bool& bTab)
146 deque<formula::FormulaToken*>::const_iterator aCur(rBegin);
147 ++aCur;
148 const SingleDoubleRefProvider aRef(**rBegin);
149 bool bOk(false);
151 const SingleDoubleRefProvider aRefCur(**aCur);
152 bOk = lcl_checkRangeDimensions(rPos, aRef, aRefCur, bCol, bRow, bTab);
154 while (bOk && aCur != rEnd)
156 const SingleDoubleRefProvider aRefCur(**aCur);
157 bool bColTmp(false);
158 bool bRowTmp(false);
159 bool bTabTmp(false);
160 bOk = lcl_checkRangeDimensions(rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
161 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
162 ++aCur;
165 if (bOk && aCur == rEnd)
167 return true;
169 return false;
172 class LessByReference : std::binary_function<const formula::FormulaToken*, const formula::FormulaToken*, bool>
174 ScAddress maPos;
175 DimensionSelector maFunc;
176 public:
177 LessByReference(const ScAddress& rPos, const DimensionSelector& rFunc) :
178 maPos(rPos), maFunc(rFunc) {}
180 bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
182 const SingleDoubleRefProvider aRef1(*pRef1);
183 const SingleDoubleRefProvider aRef2(*pRef2);
184 return maFunc(maPos, aRef1.Ref1) < maFunc(maPos, aRef2.Ref1);
189 * Returns true if range denoted by token p2 starts immediately after range
190 * denoted by token p1. Dimension, in which the comparison takes place, is
191 * given by maFunc.
193 class AdjacentByReference : std::binary_function<const formula::FormulaToken*, const formula::FormulaToken*, bool>
195 ScAddress maPos;
196 DimensionSelector maFunc;
197 public:
198 AdjacentByReference(const ScAddress& rPos, DimensionSelector aFunc) :
199 maPos(rPos), maFunc(aFunc) {}
201 bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
203 const SingleDoubleRefProvider aRef1(*p1);
204 const SingleDoubleRefProvider aRef2(*p2);
205 return maFunc(maPos, aRef2.Ref1) - maFunc(maPos, aRef1.Ref2) == 1;
209 static bool
210 lcl_checkIfAdjacent(
211 const ScAddress& rPos, const deque<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
213 typedef deque<formula::FormulaToken*>::const_iterator Iter;
214 Iter aBegin(rReferences.begin());
215 Iter aEnd(rReferences.end());
216 Iter aBegin1(aBegin);
217 ++aBegin1, --aEnd;
218 return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rPos, aWhich));
221 static void
222 lcl_fillRangeFromRefList(
223 const ScAddress& aPos, const deque<formula::FormulaToken*>& rReferences, ScRange& rRange)
225 const ScSingleRefData aStart(
226 SingleDoubleRefProvider(*rReferences.front()).Ref1);
227 rRange.aStart = aStart.toAbs(aPos);
228 const ScSingleRefData aEnd(
229 SingleDoubleRefProvider(*rReferences.back()).Ref2);
230 rRange.aEnd = aEnd.toAbs(aPos);
233 static bool
234 lcl_refListFormsOneRange(
235 const ScAddress& rPos, deque<formula::FormulaToken*>& rReferences,
236 ScRange& rRange)
238 if (rReferences.size() == 1)
240 lcl_fillRangeFromRefList(rPos, rReferences, rRange);
241 return true;
244 bool bCell(false);
245 bool bRow(false);
246 bool bTab(false);
247 if (lcl_checkRangeDimensions(rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
249 DimensionSelector aWhich;
250 if (bCell)
252 aWhich = lcl_GetCol;
254 else if (bRow)
256 aWhich = lcl_GetRow;
258 else if (bTab)
260 aWhich = lcl_GetTab;
262 else
264 OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
265 aWhich = lcl_GetRow; // initialize to avoid warning
268 // Sort the references by start of range
269 std::sort(rReferences.begin(), rReferences.end(), LessByReference(rPos, aWhich));
270 if (lcl_checkIfAdjacent(rPos, rReferences, aWhich))
272 lcl_fillRangeFromRefList(rPos, rReferences, rRange);
273 return true;
276 return false;
279 bool lcl_isReference(const FormulaToken& rToken)
281 return
282 rToken.GetType() == svSingleRef ||
283 rToken.GetType() == svDoubleRef;
286 void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc, const ScAddress& aNewPos, const ScAddress& aOldPos)
288 bool bOldGlobal = pToken->IsGlobal();
289 SCTAB aOldTab = aOldPos.Tab();
290 OUString aRangeName;
291 int nOldIndex = pToken->GetIndex();
292 ScRangeData* pOldRangeData = NULL;
294 //search the name of the RangeName
295 if (!bOldGlobal)
297 pOldRangeData = pOldDoc->GetRangeName(aOldTab)->findByIndex(nOldIndex);
298 if (!pOldRangeData)
299 return; //might be an error in the formula array
300 aRangeName = pOldRangeData->GetUpperName();
302 else
304 pOldRangeData = pOldDoc->GetRangeName()->findByIndex(nOldIndex);
305 if (!pOldRangeData)
306 return; //might be an error in the formula array
307 aRangeName = pOldRangeData->GetUpperName();
310 //find corresponding range name in new document
311 //first search for local range name then global range names
312 SCTAB aNewTab = aNewPos.Tab();
313 ScRangeName* pRangeName = rNewDoc.GetRangeName(aNewTab);
314 ScRangeData* pRangeData = NULL;
315 bool bNewGlobal = false;
316 //search local range names
317 if (pRangeName)
319 pRangeData = pRangeName->findByUpperName(aRangeName);
321 //search global range names
322 if (!pRangeData)
324 bNewGlobal = true;
325 pRangeName = rNewDoc.GetRangeName();
326 if (pRangeName)
327 pRangeData = pRangeName->findByUpperName(aRangeName);
329 //if no range name was found copy it
330 if (!pRangeData)
332 bNewGlobal = bOldGlobal;
333 pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc);
334 ScTokenArray* pRangeNameToken = pRangeData->GetCode();
335 if (rNewDoc.GetPool() != const_cast<ScDocument*>(pOldDoc)->GetPool())
337 pRangeNameToken->ReadjustAbsolute3DReferences(pOldDoc, &rNewDoc, pRangeData->GetPos(), true);
338 pRangeNameToken->AdjustAbsoluteRefs(pOldDoc, aOldPos, aNewPos, false, true);
341 bool bInserted;
342 if (bNewGlobal)
343 bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
344 else
345 bInserted = rNewDoc.GetRangeName(aNewTab)->insert(pRangeData);
346 if (!bInserted)
348 //if this happened we have a real problem
349 pRangeData = NULL;
350 pToken->SetIndex(0);
351 OSL_FAIL("inserting the range name should not fail");
352 return;
355 sal_Int32 nIndex = pRangeData->GetIndex();
356 pToken->SetIndex(nIndex);
357 pToken->SetGlobal(bNewGlobal);
360 void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
362 ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
363 if (!pOldDBCollection)
364 return;//strange error case, don't do anything
365 ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
366 ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
367 if (!pDBData)
368 return; //invalid index
369 OUString aDBName = pDBData->GetUpperName();
371 //search in new document
372 ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
373 if (!pNewDBCollection)
375 pNewDBCollection = new ScDBCollection(&rNewDoc);
376 rNewDoc.SetDBCollection(pNewDBCollection);
378 ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
379 ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
380 if (!pNewDBData)
382 pNewDBData = new ScDBData(*pDBData);
383 bool ins = aNewNamedDBs.insert(pNewDBData);
384 assert(ins); (void)ins;
386 pToken->SetIndex(pNewDBData->GetIndex());
389 struct AreaListenerKey
391 ScRange maRange;
392 bool mbStartFixed;
393 bool mbEndFixed;
395 AreaListenerKey( const ScRange& rRange, bool bStartFixed, bool bEndFixed ) :
396 maRange(rRange), mbStartFixed(bStartFixed), mbEndFixed(bEndFixed) {}
398 bool operator < ( const AreaListenerKey& r ) const
400 if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
401 return maRange.aStart.Tab() < r.maRange.aStart.Tab();
402 if (maRange.aStart.Col() != r.maRange.aStart.Col())
403 return maRange.aStart.Col() < r.maRange.aStart.Col();
404 if (maRange.aStart.Row() != r.maRange.aStart.Row())
405 return maRange.aStart.Row() < r.maRange.aStart.Row();
406 if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
407 return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
408 if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
409 return maRange.aEnd.Col() < r.maRange.aEnd.Col();
410 if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
411 return maRange.aEnd.Row() < r.maRange.aEnd.Row();
412 if (mbStartFixed != r.mbStartFixed)
413 return r.mbStartFixed;
414 if (mbEndFixed != r.mbEndFixed)
415 return r.mbEndFixed;
417 return false;
421 typedef boost::ptr_map<AreaListenerKey, sc::FormulaGroupAreaListener> AreaListenersType;
425 #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION
426 // The mutex to synchronize access to the OpenCL compilation thread.
427 static osl::Mutex& getOpenCLCompilationThreadMutex()
429 static osl::Mutex* pMutex = NULL;
430 if( !pMutex )
432 osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
433 if( !pMutex )
435 static osl::Mutex aMutex;
436 pMutex = &aMutex;
440 return *pMutex;
443 int ScFormulaCellGroup::snCount = 0;
444 rtl::Reference<sc::CLBuildKernelThread> ScFormulaCellGroup::sxCompilationThread;
445 #endif
447 struct ScFormulaCellGroup::Impl
449 AreaListenersType maAreaListeners;
452 ScFormulaCellGroup::ScFormulaCellGroup() :
453 mpImpl(new Impl),
454 mnRefCount(0),
455 mpCode(NULL),
456 mpCompiledFormula(NULL),
457 mpTopCell(NULL),
458 mnLength(0),
459 mnFormatType(css::util::NumberFormat::NUMBER),
460 mbInvariant(false),
461 mbSubTotal(false),
462 meCalcState(sc::GroupCalcEnabled),
463 meKernelState(sc::OpenCLKernelNone)
465 SAL_INFO( "sc.core.formulacell", "ScFormulaCellGroup ctor this " << this);
466 #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION
467 if (officecfg::Office::Common::Misc::UseOpenCL::get())
469 osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
470 if (snCount++ == 0)
472 assert(!sxCompilationThread.is());
473 sxCompilationThread.set(new sc::CLBuildKernelThread);
474 sxCompilationThread->launch();
477 #endif
480 ScFormulaCellGroup::~ScFormulaCellGroup()
482 SAL_INFO( "sc.core.formulacell", "ScFormulaCellGroup dtor this " << this);
483 #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION
484 if (officecfg::Office::Common::Misc::UseOpenCL::get())
486 osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
487 if (--snCount == 0 && sxCompilationThread.is())
489 assert(sxCompilationThread.is());
490 sxCompilationThread->finish();
491 sxCompilationThread->join();
492 SAL_INFO("sc.opencl", "OpenCL kernel compilation thread has finished");
493 sxCompilationThread.clear();
496 #endif
497 delete mpCode;
498 delete mpCompiledFormula;
499 delete mpImpl;
502 #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION
503 void ScFormulaCellGroup::scheduleCompilation()
505 meKernelState = sc::OpenCLKernelCompilationScheduled;
506 sc::CLBuildKernelWorkItem aWorkItem;
507 aWorkItem.meWhatToDo = sc::CLBuildKernelWorkItem::COMPILE;
508 aWorkItem.mxGroup = this;
509 sxCompilationThread->push(aWorkItem);
511 #endif
513 void ScFormulaCellGroup::setCode( const ScTokenArray& rCode )
515 delete mpCode;
516 mpCode = rCode.Clone();
517 mbInvariant = mpCode->IsInvariant();
518 mpCode->GenHash();
521 void ScFormulaCellGroup::setCode( ScTokenArray* pCode )
523 delete mpCode;
524 mpCode = pCode; // takes ownership of the token array.
525 mbInvariant = mpCode->IsInvariant();
526 mpCode->GenHash();
529 void ScFormulaCellGroup::compileCode(
530 ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
532 if (!mpCode)
533 return;
535 if (mpCode->GetLen() && !mpCode->GetCodeError() && !mpCode->GetCodeLen())
537 ScCompiler aComp(&rDoc, rPos, *mpCode);
538 aComp.SetGrammar(eGram);
539 mbSubTotal = aComp.CompileTokenArray();
540 mnFormatType = aComp.GetNumFormatType();
542 else
544 mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
548 void ScFormulaCellGroup::compileOpenCLKernel()
550 if (meCalcState == sc::GroupCalcDisabled)
551 return;
553 mpCompiledFormula =
554 sc::FormulaGroupInterpreter::getStatic()->createCompiledFormula(*this, *mpCode);
556 meKernelState = sc::OpenCLKernelBinaryCreated;
559 sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener(
560 ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
562 AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);
564 AreaListenersType::iterator it = mpImpl->maAreaListeners.lower_bound(aKey);
565 if (it == mpImpl->maAreaListeners.end() || mpImpl->maAreaListeners.key_comp()(aKey, it->first))
567 // Insert a new one.
568 it = mpImpl->maAreaListeners.insert(
569 it, aKey, new sc::FormulaGroupAreaListener(
570 rRange, *(*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed));
573 return it->second;
576 void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc )
578 AreaListenersType::iterator it = mpImpl->maAreaListeners.begin(), itEnd = mpImpl->maAreaListeners.end();
579 for (; it != itEnd; ++it)
581 sc::FormulaGroupAreaListener* pListener = it->second;
582 ScRange aListenRange = pListener->getListeningRange();
583 // This "always listen" special range is never grouped.
584 bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
585 rDoc.EndListeningArea(aListenRange, bGroupListening, pListener);
588 mpImpl->maAreaListeners.clear();
591 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos ) :
592 eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
593 pCode(new ScTokenArray),
594 pDocument(pDoc),
595 pPrevious(0),
596 pNext(0),
597 pPreviousTrack(0),
598 pNextTrack(0),
599 nSeenInIteration(0),
600 nFormatType(css::util::NumberFormat::NUMBER),
601 cMatrixFlag(MM_NONE),
602 bDirty(false),
603 bChanged(false),
604 bRunning(false),
605 bCompile(false),
606 bSubTotal(false),
607 bIsIterCell(false),
608 bInChangeTrack(false),
609 bTableOpDirty(false),
610 bNeedListening(false),
611 mbNeedsNumberFormat(false),
612 mbPostponedDirty(false),
613 mbIsExtRef(false),
614 aPos(rPos)
616 SAL_INFO( "sc.core.formulacell", "ScFormulaCell ctor this " << this);
619 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
620 const OUString& rFormula,
621 const FormulaGrammar::Grammar eGrammar,
622 sal_uInt8 cMatInd ) :
623 eTempGrammar( eGrammar),
624 pCode( NULL ),
625 pDocument( pDoc ),
626 pPrevious(0),
627 pNext(0),
628 pPreviousTrack(0),
629 pNextTrack(0),
630 nSeenInIteration(0),
631 nFormatType ( css::util::NumberFormat::NUMBER ),
632 cMatrixFlag ( cMatInd ),
633 bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
634 bChanged( false ),
635 bRunning( false ),
636 bCompile( false ),
637 bSubTotal( false ),
638 bIsIterCell( false ),
639 bInChangeTrack( false ),
640 bTableOpDirty( false ),
641 bNeedListening( false ),
642 mbNeedsNumberFormat( false ),
643 mbPostponedDirty(false),
644 mbIsExtRef(false),
645 aPos(rPos)
647 SAL_INFO( "sc.core.formulacell", "ScFormulaCell ctor this " << this);
649 Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
650 if (!pCode)
651 // We need to have a non-NULL token array instance at all times.
652 pCode = new ScTokenArray;
655 ScFormulaCell::ScFormulaCell(
656 ScDocument* pDoc, const ScAddress& rPos, ScTokenArray* pArray,
657 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cMatInd ) :
658 eTempGrammar( eGrammar),
659 pCode(pArray),
660 pDocument( pDoc ),
661 pPrevious(0),
662 pNext(0),
663 pPreviousTrack(0),
664 pNextTrack(0),
665 nSeenInIteration(0),
666 nFormatType ( css::util::NumberFormat::NUMBER ),
667 cMatrixFlag ( cMatInd ),
668 bDirty( true ),
669 bChanged( false ),
670 bRunning( false ),
671 bCompile( false ),
672 bSubTotal( false ),
673 bIsIterCell( false ),
674 bInChangeTrack( false ),
675 bTableOpDirty( false ),
676 bNeedListening( false ),
677 mbNeedsNumberFormat( false ),
678 mbPostponedDirty(false),
679 mbIsExtRef(false),
680 aPos(rPos)
682 SAL_INFO( "sc.core.formulacell", "ScFormulaCell ctor this " << this);
683 assert(pArray); // Never pass a NULL pointer here.
685 // Generate RPN token array.
686 if (pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen())
688 ScCompiler aComp( pDocument, aPos, *pCode);
689 aComp.SetGrammar(eTempGrammar);
690 bSubTotal = aComp.CompileTokenArray();
691 nFormatType = aComp.GetNumFormatType();
693 else
695 if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
696 bSubTotal = true;
699 if (bSubTotal)
700 pDocument->AddSubTotalCell(this);
702 pCode->GenHash();
705 ScFormulaCell::ScFormulaCell(
706 ScDocument* pDoc, const ScAddress& rPos, const ScTokenArray& rArray,
707 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cMatInd ) :
708 eTempGrammar( eGrammar),
709 pCode(new ScTokenArray(rArray)),
710 pDocument( pDoc ),
711 pPrevious(0),
712 pNext(0),
713 pPreviousTrack(0),
714 pNextTrack(0),
715 nSeenInIteration(0),
716 nFormatType ( css::util::NumberFormat::NUMBER ),
717 cMatrixFlag ( cMatInd ),
718 bDirty( true ),
719 bChanged( false ),
720 bRunning( false ),
721 bCompile( false ),
722 bSubTotal( false ),
723 bIsIterCell( false ),
724 bInChangeTrack( false ),
725 bTableOpDirty( false ),
726 bNeedListening( false ),
727 mbNeedsNumberFormat( false ),
728 mbPostponedDirty(false),
729 mbIsExtRef(false),
730 aPos(rPos)
732 SAL_INFO( "sc.core.formulacell", "ScFormulaCell ctor this " << this);
734 // RPN array generation
735 if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
737 ScCompiler aComp( pDocument, aPos, *pCode);
738 aComp.SetGrammar(eTempGrammar);
739 bSubTotal = aComp.CompileTokenArray();
740 nFormatType = aComp.GetNumFormatType();
742 else
744 if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
745 bSubTotal = true;
748 if (bSubTotal)
749 pDocument->AddSubTotalCell(this);
751 pCode->GenHash();
754 ScFormulaCell::ScFormulaCell(
755 ScDocument* pDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
756 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) :
757 mxGroup(xGroup),
758 eTempGrammar( eGrammar),
759 pCode(xGroup->mpCode ? xGroup->mpCode : new ScTokenArray),
760 pDocument( pDoc ),
761 pPrevious(0),
762 pNext(0),
763 pPreviousTrack(0),
764 pNextTrack(0),
765 nSeenInIteration(0),
766 nFormatType(xGroup->mnFormatType),
767 cMatrixFlag ( cInd ),
768 bDirty(true),
769 bChanged( false ),
770 bRunning( false ),
771 bCompile( false ),
772 bSubTotal(xGroup->mbSubTotal),
773 bIsIterCell( false ),
774 bInChangeTrack( false ),
775 bTableOpDirty( false ),
776 bNeedListening( false ),
777 mbNeedsNumberFormat( false ),
778 mbPostponedDirty(false),
779 mbIsExtRef(false),
780 aPos(rPos)
782 SAL_INFO( "sc.core.formulacell", "ScFormulaCell ctor this " << this);
784 if (bSubTotal)
785 pDocument->AddSubTotalCell(this);
788 ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
789 SvtListener(),
790 aResult( rCell.aResult ),
791 eTempGrammar( rCell.eTempGrammar),
792 pDocument( &rDoc ),
793 pPrevious(0),
794 pNext(0),
795 pPreviousTrack(0),
796 pNextTrack(0),
797 nSeenInIteration(0),
798 nFormatType( rCell.nFormatType ),
799 cMatrixFlag ( rCell.cMatrixFlag ),
800 bDirty( rCell.bDirty ),
801 bChanged( rCell.bChanged ),
802 bRunning( false ),
803 bCompile( rCell.bCompile ),
804 bSubTotal( rCell.bSubTotal ),
805 bIsIterCell( false ),
806 bInChangeTrack( false ),
807 bTableOpDirty( false ),
808 bNeedListening( false ),
809 mbNeedsNumberFormat( false ),
810 mbPostponedDirty(false),
811 mbIsExtRef(false),
812 aPos(rPos)
814 SAL_INFO( "sc.core.formulacell", "ScFormulaCell ctor this " << this);
816 pCode = rCell.pCode->Clone();
818 // set back any errors and recompile
819 // not in the Clipboard - it must keep the received error flag
820 // Special Length=0: as bad cells are generated, then they are also retained
821 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
823 pCode->SetCodeError( 0 );
824 bCompile = true;
826 // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
827 bool bCompileLater = false;
828 bool bClipMode = rCell.pDocument->IsClipboard();
830 //update ScNameTokens
831 if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
833 if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
835 formula::FormulaToken* pToken = NULL;
836 while((pToken = pCode->GetNextName())!= NULL)
838 OpCode eOpCode = pToken->GetOpCode();
839 if (eOpCode == ocName)
840 adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos);
841 else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
842 adjustDBRange(pToken, rDoc, rCell.pDocument);
846 bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
847 if (bCopyBetweenDocs && !(nCloneFlags & SC_CLONECELL_NOMAKEABS_EXTERNAL))
849 pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
852 pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, false, bCopyBetweenDocs );
855 if (!pDocument->IsClipOrUndo())
856 pCode->AdjustReferenceOnCopy( aPos);
858 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
859 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
861 if( !bCompile )
862 { // Name references with references and ColRowNames
863 pCode->Reset();
864 formula::FormulaToken* t;
865 while ( ( t = pCode->GetNextReferenceOrName() ) != NULL && !bCompile )
867 if ( t->IsExternalRef() )
869 // External name, cell, and area references.
870 bCompile = true;
872 else if ( t->GetType() == svIndex )
874 ScRangeData* pRangeData = rDoc.GetRangeName()->findByIndex( t->GetIndex() );
875 if( pRangeData )
877 if( pRangeData->HasReferences() )
878 bCompile = true;
880 else
881 bCompile = true; // invalid reference!
883 else if ( t->GetOpCode() == ocColRowName )
885 bCompile = true; // new lookup needed
886 bCompileLater = bClipMode;
890 if( bCompile )
892 if ( !bCompileLater && bClipMode )
894 // Merging ranges needs the actual positions after UpdateReference.
895 // ColRowNames and TableRefs need new lookup after positions are
896 // adjusted.
897 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
898 pCode->HasOpCode( ocTableRef);
900 if ( !bCompileLater )
902 // bNoListening, not at all if in Clipboard/Undo,
903 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
904 CompileTokenArray( true );
908 if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
909 StartListeningTo( &rDoc );
911 if (bSubTotal)
912 pDocument->AddSubTotalCell(this);
915 ScFormulaCell::~ScFormulaCell()
917 SAL_INFO( "sc.core.formulacell", "ScFormulaCell dtor this " << this);
919 pDocument->RemoveFromFormulaTrack( this );
920 pDocument->RemoveFromFormulaTree( this );
921 pDocument->RemoveSubTotalCell(this);
922 if (pCode->HasOpCode(ocMacro))
923 pDocument->GetMacroManager()->RemoveDependentCell(this);
925 if (pDocument->HasExternalRefManager())
926 pDocument->GetExternalRefManager()->removeRefCell(this);
928 if (!mxGroup || !mxGroup->mpCode)
929 // Formula token is not shared.
930 delete pCode;
933 ScFormulaCell* ScFormulaCell::Clone() const
935 return new ScFormulaCell(*this, *pDocument, aPos);
938 ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos, int nCloneFlags ) const
940 return new ScFormulaCell(*this, *pDocument, rPos, nCloneFlags);
943 size_t ScFormulaCell::GetHash() const
945 return pCode->GetHash();
948 ScFormulaVectorState ScFormulaCell::GetVectorState() const
950 return pCode->GetVectorState();
953 void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
954 const FormulaGrammar::Grammar eGrammar ) const
956 if( pCode->GetCodeError() && !pCode->GetLen() )
958 rBuffer = OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
959 return;
961 else if( cMatrixFlag == MM_REFERENCE )
963 // Reference to another cell that contains a matrix formula.
964 pCode->Reset();
965 formula::FormulaToken* p = pCode->GetNextReferenceRPN();
966 if( p )
968 /* FIXME: original GetFormula() code obtained
969 * pCell only if (!this->IsInChangeTrack()),
970 * GetEnglishFormula() omitted that test.
971 * Can we live without in all cases? */
972 ScFormulaCell* pCell = NULL;
973 ScSingleRefData& rRef = *p->GetSingleRef();
974 ScAddress aAbs = rRef.toAbs(aPos);
975 if (ValidAddress(aAbs))
976 pCell = pDocument->GetFormulaCell(aAbs);
978 if (pCell)
980 pCell->GetFormula( rBuffer, eGrammar);
981 return;
983 else
985 ScCompiler aComp( pDocument, aPos, *pCode);
986 aComp.SetGrammar(eGrammar);
987 aComp.CreateStringFromTokenArray( rBuffer );
990 else
992 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
995 else
997 ScCompiler aComp( pDocument, aPos, *pCode);
998 aComp.SetGrammar(eGrammar);
999 aComp.CreateStringFromTokenArray( rBuffer );
1002 rBuffer.insert( 0, '=');
1003 if( cMatrixFlag )
1005 rBuffer.insert( 0, '{');
1006 rBuffer.append( '}');
1010 void ScFormulaCell::GetFormula( OUString& rFormula, const FormulaGrammar::Grammar eGrammar ) const
1012 OUStringBuffer rBuffer( rFormula );
1013 GetFormula( rBuffer, eGrammar );
1014 rFormula = rBuffer.makeStringAndClear();
1017 OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt ) const
1019 OUStringBuffer aBuf;
1020 if (pCode->GetCodeError() && !pCode->GetLen())
1022 aBuf = OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
1023 return aBuf.makeStringAndClear();
1025 else if( cMatrixFlag == MM_REFERENCE )
1027 // Reference to another cell that contains a matrix formula.
1028 pCode->Reset();
1029 formula::FormulaToken* p = pCode->GetNextReferenceRPN();
1030 if( p )
1032 /* FIXME: original GetFormula() code obtained
1033 * pCell only if (!this->IsInChangeTrack()),
1034 * GetEnglishFormula() omitted that test.
1035 * Can we live without in all cases? */
1036 ScFormulaCell* pCell = NULL;
1037 ScSingleRefData& rRef = *p->GetSingleRef();
1038 ScAddress aAbs = rRef.toAbs(aPos);
1039 if (ValidAddress(aAbs))
1040 pCell = pDocument->GetFormulaCell(aAbs);
1042 if (pCell)
1044 return pCell->GetFormula(rCxt);
1046 else
1048 ScCompiler aComp(rCxt, aPos, *pCode);
1049 aComp.CreateStringFromTokenArray(aBuf);
1052 else
1054 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1057 else
1059 ScCompiler aComp(rCxt, aPos, *pCode);
1060 aComp.CreateStringFromTokenArray(aBuf);
1063 aBuf.insert( 0, '=');
1064 if( cMatrixFlag )
1066 aBuf.insert( 0, '{');
1067 aBuf.append( '}');
1070 return aBuf.makeStringAndClear();
1073 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
1075 MaybeInterpret();
1077 const ScMatrix* pMat = NULL;
1078 if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell &&
1079 ((pMat = aResult.GetToken().get()->GetMatrix()) != 0))
1080 pMat->GetDimensions( rCols, rRows );
1081 else
1083 rCols = 0;
1084 rRows = 0;
1088 void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; }
1089 void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; }
1091 void ScFormulaCell::SetNeedsDirty( bool bVar )
1093 mbPostponedDirty = bVar;
1096 void ScFormulaCell::SetNeedNumberFormat( bool bVal ) { mbNeedsNumberFormat = bVal; }
1098 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
1099 const FormulaGrammar::Grammar eGrammar )
1101 if ( pDocument->IsClipOrUndo() )
1102 return;
1103 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1104 if ( bWasInFormulaTree )
1105 pDocument->RemoveFromFormulaTree( this );
1106 // pCode may not deleted for queries, but must be empty
1107 if ( pCode )
1108 pCode->Clear();
1109 ScTokenArray* pCodeOld = pCode;
1110 ScCompiler aComp( pDocument, aPos);
1111 aComp.SetGrammar(eGrammar);
1112 pCode = aComp.CompileString( rFormula );
1113 if ( pCodeOld )
1114 delete pCodeOld;
1115 if( !pCode->GetCodeError() )
1117 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1118 { // not recursive CompileTokenArray/Compile/CompileTokenArray
1119 if ( rFormula[0] == '=' )
1120 pCode->AddBad( rFormula.copy(1) );
1121 else
1122 pCode->AddBad( rFormula );
1124 bCompile = true;
1125 CompileTokenArray( bNoListening );
1127 else
1128 bChanged = true;
1130 if ( bWasInFormulaTree )
1131 pDocument->PutInFormulaTree( this );
1134 void ScFormulaCell::Compile(
1135 sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1137 if ( pDocument->IsClipOrUndo() )
1138 return;
1139 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1140 if ( bWasInFormulaTree )
1141 pDocument->RemoveFromFormulaTree( this );
1142 // pCode may not deleted for queries, but must be empty
1143 if ( pCode )
1144 pCode->Clear();
1145 ScTokenArray* pCodeOld = pCode;
1146 ScCompiler aComp(rCxt, aPos);
1147 pCode = aComp.CompileString( rFormula );
1148 if ( pCodeOld )
1149 delete pCodeOld;
1150 if( !pCode->GetCodeError() )
1152 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1153 { // not recursive CompileTokenArray/Compile/CompileTokenArray
1154 if ( rFormula[0] == '=' )
1155 pCode->AddBad( rFormula.copy(1) );
1156 else
1157 pCode->AddBad( rFormula );
1159 bCompile = true;
1160 CompileTokenArray(rCxt, bNoListening);
1162 else
1163 bChanged = true;
1165 if ( bWasInFormulaTree )
1166 pDocument->PutInFormulaTree( this );
1169 void ScFormulaCell::CompileTokenArray( bool bNoListening )
1171 // Not already compiled?
1172 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1174 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
1176 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
1178 // RPN length may get changed
1179 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1180 if ( bWasInFormulaTree )
1181 pDocument->RemoveFromFormulaTree( this );
1183 // Loading from within filter? No listening yet!
1184 if( pDocument->IsInsertingFromOtherDoc() )
1185 bNoListening = true;
1187 if( !bNoListening && pCode->GetCodeLen() )
1188 EndListeningTo( pDocument );
1189 ScCompiler aComp(pDocument, aPos, *pCode);
1190 aComp.SetGrammar(pDocument->GetGrammar());
1191 bSubTotal = aComp.CompileTokenArray();
1192 if( !pCode->GetCodeError() )
1194 nFormatType = aComp.GetNumFormatType();
1195 bChanged = true;
1196 aResult.SetToken( NULL);
1197 bCompile = false;
1198 if ( !bNoListening )
1199 StartListeningTo( pDocument );
1201 if ( bWasInFormulaTree )
1202 pDocument->PutInFormulaTree( this );
1204 if (bSubTotal)
1205 pDocument->AddSubTotalCell(this);
1209 void ScFormulaCell::CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNoListening )
1211 // Not already compiled?
1212 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1214 rCxt.setGrammar(eTempGrammar);
1215 Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
1217 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
1219 // RPN length may get changed
1220 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1221 if ( bWasInFormulaTree )
1222 pDocument->RemoveFromFormulaTree( this );
1224 // Loading from within filter? No listening yet!
1225 if( pDocument->IsInsertingFromOtherDoc() )
1226 bNoListening = true;
1228 if( !bNoListening && pCode->GetCodeLen() )
1229 EndListeningTo( pDocument );
1230 ScCompiler aComp(rCxt, aPos, *pCode);
1231 bSubTotal = aComp.CompileTokenArray();
1232 if( !pCode->GetCodeError() )
1234 nFormatType = aComp.GetNumFormatType();
1235 bChanged = true;
1236 aResult.SetToken( NULL);
1237 bCompile = false;
1238 if ( !bNoListening )
1239 StartListeningTo( pDocument );
1241 if ( bWasInFormulaTree )
1242 pDocument->PutInFormulaTree( this );
1244 if (bSubTotal)
1245 pDocument->AddSubTotalCell(this);
1249 void ScFormulaCell::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
1251 if ( cMatrixFlag == MM_REFERENCE )
1252 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1253 // just establish listeners
1254 StartListeningTo( pDocument );
1255 return ;
1258 // Compilation changes RPN count, remove and reinsert to FormulaTree if it
1259 // was in to update its count.
1260 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this);
1261 if (bWasInFormulaTree)
1262 pDocument->RemoveFromFormulaTree( this);
1263 rCxt.setGrammar(eTempGrammar);
1264 ScCompiler aComp(rCxt, aPos, *pCode);
1265 OUString aFormula, aFormulaNmsp;
1266 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1267 pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
1268 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
1269 // pCode may not deleted for queries, but must be empty
1270 pCode->Clear();
1272 bool bSkipCompile = false;
1274 if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
1276 ScAddress aPreviousCell( aPos );
1277 aPreviousCell.IncRow( -1 );
1278 ScFormulaCell *pPreviousCell = pDocument->GetFormulaCell( aPreviousCell );
1279 if( pPreviousCell )
1281 // Now try to convert to a string quickly ...
1282 ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
1283 OUStringBuffer aShouldBeBuf;
1284 aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
1286 assert( aFormula[0] == '=' );
1287 OUString aShouldBe = aShouldBeBuf.makeStringAndClear();
1288 if( aFormula.getLength() == aShouldBe.getLength() + 1 &&
1289 aFormula.match( aShouldBe, 1 ) ) // initial '='
1291 // Put them in the same formula group.
1292 ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
1293 if (!xGroup) // Last cell is not grouped yet. Start a new group.
1294 xGroup = pPreviousCell->CreateCellGroup(1, false);
1295 ++xGroup->mnLength;
1296 SetCellGroup( xGroup );
1298 // Do setup here based on previous cell.
1300 nFormatType = pPreviousCell->nFormatType;
1301 bSubTotal = pPreviousCell->bSubTotal;
1302 bChanged = true;
1303 bCompile = false;
1305 if (bSubTotal)
1306 pDocument->AddSubTotalCell(this);
1308 bSkipCompile = true;
1309 pCode = pPreviousCell->pCode;
1310 if (pPreviousCell->mbIsExtRef)
1311 pDocument->GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
1313 SAL_INFO( "sc", "merged '" << aFormula << "' == '" << aShouldBe
1314 << "'extend group to " << xGroup->mnLength );
1319 if (!bSkipCompile)
1321 ScTokenArray* pCodeOld = pCode;
1322 pCode = aComp.CompileString( aFormula, aFormulaNmsp );
1323 delete pCodeOld;
1325 if( !pCode->GetCodeError() )
1327 if ( !pCode->GetLen() )
1329 if ( aFormula[0] == '=' )
1330 pCode->AddBad( aFormula.copy( 1 ) );
1331 else
1332 pCode->AddBad( aFormula );
1334 bSubTotal = aComp.CompileTokenArray();
1335 if( !pCode->GetCodeError() )
1337 nFormatType = aComp.GetNumFormatType();
1338 bChanged = true;
1339 bCompile = false;
1342 if (bSubTotal)
1343 pDocument->AddSubTotalCell(this);
1345 else
1346 bChanged = true;
1349 // Same as in Load: after loading, it must be known if ocMacro is in any formula
1350 // (for macro warning, CompileXML is called at the end of loading XML file)
1351 if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
1352 pDocument->SetHasMacroFunc( true );
1354 //volatile cells must be added here for import
1355 if( pCode->IsRecalcModeAlways() || pCode->IsRecalcModeForced() ||
1356 pCode->IsRecalcModeOnLoad() || pCode->IsRecalcModeOnLoadOnce() )
1358 // During load, only those cells that are marked explicitly dirty get
1359 // recalculated. So we need to set it dirty here.
1360 SetDirtyVar();
1361 pDocument->AppendToFormulaTrack(this);
1362 // Do not call TrackFormulas() here, not all listeners may have been
1363 // established, postponed until ScDocument::CompileXML() finishes.
1365 else if (bWasInFormulaTree)
1366 pDocument->PutInFormulaTree(this);
1369 void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
1371 bool bNewCompiled = false;
1372 // If a Calc 1.0-doc is read, we have a result, but no token array
1373 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1375 rCxt.setGrammar(eTempGrammar);
1376 Compile(rCxt, aResult.GetHybridFormula(), true);
1377 aResult.SetToken( NULL);
1378 bDirty = true;
1379 bNewCompiled = true;
1381 // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1382 if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
1384 ScCompiler aComp(rCxt, aPos, *pCode);
1385 bSubTotal = aComp.CompileTokenArray();
1386 nFormatType = aComp.GetNumFormatType();
1387 bDirty = true;
1388 bCompile = false;
1389 bNewCompiled = true;
1391 if (bSubTotal)
1392 pDocument->AddSubTotalCell(this);
1395 // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1396 // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1397 // We iron this out here for all systems, such that we also have an Err503 here.
1398 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1400 OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1401 aResult.SetResultError( errIllegalFPOperation );
1402 bDirty = true;
1405 // DoubleRefs for binary operators were always a Matrix before version v5.0.
1406 // Now this is only the case when when in an array formula, otherwise it's an implicit intersection
1407 if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
1408 GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
1410 cMatrixFlag = MM_FORMULA;
1411 SetMatColsRows( 1, 1);
1414 // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1415 // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
1416 if( !bNewCompiled || !pCode->GetCodeError() )
1418 if (bStartListening)
1419 StartListeningTo(pDocument);
1421 if( !pCode->IsRecalcModeNormal() )
1422 bDirty = true;
1424 if ( pCode->IsRecalcModeAlways() )
1425 { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1426 // for each F9
1427 bDirty = true;
1429 // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1432 bool ScFormulaCell::MarkUsedExternalReferences()
1434 return pCode && pDocument->MarkUsedExternalReferences(*pCode, aPos);
1437 void ScFormulaCell::Interpret()
1439 if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
1440 return; // no double/triple processing
1442 //FIXME:
1443 // If the call originates from a Reschedule in DdeLink update, leave dirty
1444 // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1445 if ( pDocument->IsInDdeLinkUpdate() )
1446 return;
1448 if (bRunning)
1450 if (!pDocument->GetDocOptions().IsIter())
1452 aResult.SetResultError( errCircularReference );
1453 return;
1456 if (aResult.GetResultError() == errCircularReference)
1457 aResult.SetResultError( 0 );
1459 // Start or add to iteration list.
1460 if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
1461 !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
1462 pDocument->GetRecursionHelper().SetInIterationReturn( true);
1464 return;
1466 // no multiple interprets for GetErrCode, IsValue, GetValue and
1467 // different entry point recursions. Would also lead to premature
1468 // convergence in iterations.
1469 if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
1470 pDocument->GetRecursionHelper().GetIteration())
1471 return ;
1473 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
1474 bool bOldRunning = bRunning;
1475 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1477 bRunning = true;
1478 rRecursionHelper.SetInRecursionReturn( true);
1480 else
1482 if ( ! InterpretFormulaGroup() )
1483 InterpretTail( SCITP_NORMAL);
1486 // While leaving a recursion or iteration stack, insert its cells to the
1487 // recursion list in reverse order.
1488 if (rRecursionHelper.IsInReturn())
1490 if (rRecursionHelper.GetRecursionCount() > 0 ||
1491 !rRecursionHelper.IsDoingRecursion())
1492 rRecursionHelper.Insert( this, bOldRunning, aResult);
1493 bool bIterationFromRecursion = false;
1494 bool bResumeIteration = false;
1497 if ((rRecursionHelper.IsInIterationReturn() &&
1498 rRecursionHelper.GetRecursionCount() == 0 &&
1499 !rRecursionHelper.IsDoingIteration()) ||
1500 bIterationFromRecursion || bResumeIteration)
1502 ScFormulaCell* pIterCell = this; // scope for debug convenience
1503 bool & rDone = rRecursionHelper.GetConvergingReference();
1504 rDone = false;
1505 if (!bIterationFromRecursion && bResumeIteration)
1507 bResumeIteration = false;
1508 // Resuming iteration expands the range.
1509 ScFormulaRecursionList::const_iterator aOldStart(
1510 rRecursionHelper.GetLastIterationStart());
1511 rRecursionHelper.ResumeIteration();
1512 // Mark new cells being in iteration.
1513 for (ScFormulaRecursionList::const_iterator aIter(
1514 rRecursionHelper.GetIterationStart()); aIter !=
1515 aOldStart; ++aIter)
1517 pIterCell = (*aIter).pCell;
1518 pIterCell->bIsIterCell = true;
1520 // Mark older cells dirty again, in case they converted
1521 // without accounting for all remaining cells in the circle
1522 // that weren't touched so far, e.g. conditional. Restore
1523 // backuped result.
1524 sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1525 for (ScFormulaRecursionList::const_iterator aIter(
1526 aOldStart); aIter !=
1527 rRecursionHelper.GetIterationEnd(); ++aIter)
1529 pIterCell = (*aIter).pCell;
1530 if (pIterCell->nSeenInIteration == nIteration)
1532 if (!pIterCell->bDirty || aIter == aOldStart)
1534 pIterCell->aResult = (*aIter).aPreviousResult;
1536 --pIterCell->nSeenInIteration;
1538 pIterCell->bDirty = true;
1541 else
1543 bResumeIteration = false;
1544 // Close circle once.
1545 rRecursionHelper.GetList().back().pCell->InterpretTail(
1546 SCITP_CLOSE_ITERATION_CIRCLE);
1547 // Start at 1, init things.
1548 rRecursionHelper.StartIteration();
1549 // Mark all cells being in iteration.
1550 for (ScFormulaRecursionList::const_iterator aIter(
1551 rRecursionHelper.GetIterationStart()); aIter !=
1552 rRecursionHelper.GetIterationEnd(); ++aIter)
1554 pIterCell = (*aIter).pCell;
1555 pIterCell->bIsIterCell = true;
1558 bIterationFromRecursion = false;
1559 sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
1560 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1561 rRecursionHelper.IncIteration())
1563 rDone = true;
1564 for ( ScFormulaRecursionList::iterator aIter(
1565 rRecursionHelper.GetIterationStart()); aIter !=
1566 rRecursionHelper.GetIterationEnd() &&
1567 !rRecursionHelper.IsInReturn(); ++aIter)
1569 pIterCell = (*aIter).pCell;
1570 if (pIterCell->IsDirtyOrInTableOpDirty() &&
1571 rRecursionHelper.GetIteration() !=
1572 pIterCell->GetSeenInIteration())
1574 (*aIter).aPreviousResult = pIterCell->aResult;
1575 pIterCell->InterpretTail( SCITP_FROM_ITERATION);
1577 rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
1579 if (rRecursionHelper.IsInReturn())
1581 bResumeIteration = true;
1582 break; // for
1583 // Don't increment iteration.
1586 if (!bResumeIteration)
1588 if (rDone)
1590 for (ScFormulaRecursionList::const_iterator aIter(
1591 rRecursionHelper.GetIterationStart());
1592 aIter != rRecursionHelper.GetIterationEnd();
1593 ++aIter)
1595 pIterCell = (*aIter).pCell;
1596 pIterCell->bIsIterCell = false;
1597 pIterCell->nSeenInIteration = 0;
1598 pIterCell->bRunning = (*aIter).bOldRunning;
1601 else
1603 for (ScFormulaRecursionList::const_iterator aIter(
1604 rRecursionHelper.GetIterationStart());
1605 aIter != rRecursionHelper.GetIterationEnd();
1606 ++aIter)
1608 pIterCell = (*aIter).pCell;
1609 pIterCell->bIsIterCell = false;
1610 pIterCell->nSeenInIteration = 0;
1611 pIterCell->bRunning = (*aIter).bOldRunning;
1612 // If one cell didn't converge, all cells of this
1613 // circular dependency don't, no matter whether
1614 // single cells did.
1615 pIterCell->ResetDirty();
1616 pIterCell->aResult.SetResultError( errNoConvergence);
1617 pIterCell->bChanged = true;
1620 // End this iteration and remove entries.
1621 rRecursionHelper.EndIteration();
1622 bResumeIteration = rRecursionHelper.IsDoingIteration();
1625 if (rRecursionHelper.IsInRecursionReturn() &&
1626 rRecursionHelper.GetRecursionCount() == 0 &&
1627 !rRecursionHelper.IsDoingRecursion())
1629 bIterationFromRecursion = false;
1630 // Iterate over cells known so far, start with the last cell
1631 // encountered, inserting new cells if another recursion limit
1632 // is reached. Repeat until solved.
1633 rRecursionHelper.SetDoingRecursion( true);
1636 rRecursionHelper.SetInRecursionReturn( false);
1637 for (ScFormulaRecursionList::const_iterator aIter(
1638 rRecursionHelper.GetIterationStart());
1639 !rRecursionHelper.IsInReturn() && aIter !=
1640 rRecursionHelper.GetIterationEnd(); ++aIter)
1642 ScFormulaCell* pCell = (*aIter).pCell;
1643 if (pCell->IsDirtyOrInTableOpDirty())
1645 pCell->InterpretTail( SCITP_NORMAL);
1646 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1647 pCell->bRunning = (*aIter).bOldRunning;
1650 } while (rRecursionHelper.IsInRecursionReturn());
1651 rRecursionHelper.SetDoingRecursion( false);
1652 if (rRecursionHelper.IsInIterationReturn())
1654 if (!bResumeIteration)
1655 bIterationFromRecursion = true;
1657 else if (bResumeIteration ||
1658 rRecursionHelper.IsDoingIteration())
1659 rRecursionHelper.GetList().erase(
1660 rRecursionHelper.GetIterationStart(),
1661 rRecursionHelper.GetLastIterationStart());
1662 else
1663 rRecursionHelper.Clear();
1665 } while (bIterationFromRecursion || bResumeIteration);
1669 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
1671 class RecursionCounter
1673 ScRecursionHelper& rRec;
1674 bool bStackedInIteration;
1675 public:
1676 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
1678 bStackedInIteration = rRec.IsDoingIteration();
1679 if (bStackedInIteration)
1680 rRec.GetRecursionInIterationStack().push( p);
1681 rRec.IncRecursionCount();
1683 ~RecursionCounter()
1685 rRec.DecRecursionCount();
1686 if (bStackedInIteration)
1687 rRec.GetRecursionInIterationStack().pop();
1689 } aRecursionCounter( pDocument->GetRecursionHelper(), this);
1690 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
1691 if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
1693 // #i11719# no RPN and no error and no token code but result string present
1694 // => interpretation of this cell during name-compilation and unknown names
1695 // => can't exchange underlying code array in CompileTokenArray() /
1696 // Compile() because interpreter's token iterator would crash or pCode
1697 // would be deleted twice if this cell was interpreted during
1698 // compilation.
1699 // This should only be a temporary condition and, since we set an
1700 // error, if ran into it again we'd bump into the dirty-clearing
1701 // condition further down.
1702 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1704 pCode->SetCodeError( errNoCode );
1705 // This is worth an assertion; if encountered in daily work
1706 // documents we might need another solution. Or just confirm correctness.
1707 OSL_FAIL( "ScFormulaCell::Interpret: no RPN, no error, no token, but hybrid formula string" );
1708 return;
1710 CompileTokenArray();
1713 if( pCode->GetCodeLen() && pDocument )
1715 class StackCleaner
1717 ScDocument* pDoc;
1718 ScInterpreter* pInt;
1719 public:
1720 StackCleaner( ScDocument* pD, ScInterpreter* pI )
1721 : pDoc(pD), pInt(pI)
1723 ~StackCleaner()
1725 delete pInt;
1726 pDoc->DecInterpretLevel();
1729 pDocument->IncInterpretLevel();
1730 ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
1731 StackCleaner aStackCleaner( pDocument, p);
1732 sal_uInt16 nOldErrCode = aResult.GetResultError();
1733 if ( nSeenInIteration == 0 )
1734 { // Only the first time
1735 // With bChanged=false, if a newly compiled cell has a result of
1736 // 0.0, no change is detected and the cell will not be repainted.
1737 // bChanged = false;
1738 aResult.SetResultError( 0 );
1741 switch ( aResult.GetResultError() )
1743 case errCircularReference : // will be determined again if so
1744 aResult.SetResultError( 0 );
1745 break;
1748 bool bOldRunning = bRunning;
1749 bRunning = true;
1750 p->Interpret();
1751 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
1753 if (nSeenInIteration > 0)
1754 --nSeenInIteration; // retry when iteration is resumed
1755 return;
1757 bRunning = bOldRunning;
1759 // #i102616# For single-sheet saving consider only content changes, not format type,
1760 // because format type isn't set on loading (might be changed later)
1761 bool bContentChanged = false;
1763 // Do not create a HyperLink() cell if the formula results in an error.
1764 if( p->GetError() && pCode->IsHyperLink())
1765 pCode->SetHyperLink(false);
1767 if( p->GetError() && p->GetError() != errCircularReference)
1769 ResetDirty();
1770 bChanged = true;
1772 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1774 bool bIsValue = aResult.IsValue(); // the previous type
1775 // Did it converge?
1776 if ((bIsValue && p->GetResultType() == svDouble && fabs(
1777 p->GetNumResult() - aResult.GetDouble()) <=
1778 pDocument->GetDocOptions().GetIterEps()) ||
1779 (!bIsValue && p->GetResultType() == svString &&
1780 p->GetStringResult() == aResult.GetString()))
1782 // A convergence in the first iteration doesn't necessarily
1783 // mean that it's done, it may be as not all related cells
1784 // of a circle changed their values yet. If the set really
1785 // converges it will do so also during the next iteration. This
1786 // fixes situations like of #i44115#. If this wasn't wanted an
1787 // initial "uncalculated" value would be needed for all cells
1788 // of a circular dependency => graph needed before calculation.
1789 if (nSeenInIteration > 1 ||
1790 pDocument->GetDocOptions().GetIterCount() == 1)
1792 ResetDirty();
1797 // New error code?
1798 if( p->GetError() != nOldErrCode )
1800 bChanged = true;
1801 // bContentChanged only has to be set if the file content would be changed
1802 if ( aResult.GetCellResultType() != svUnknown )
1803 bContentChanged = true;
1806 if( mbNeedsNumberFormat )
1808 nFormatType = p->GetRetFormatType();
1809 sal_Int32 nFormatIndex = p->GetRetFormatIndex();
1811 // don't set text format as hard format
1812 if(nFormatType == css::util::NumberFormat::TEXT)
1813 nFormatIndex = 0;
1814 else if((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1815 nFormatIndex = ScGlobal::GetStandardFormat(*pDocument->GetFormatTable(),
1816 nFormatIndex, nFormatType);
1818 // Do not replace a General format (which was the reason why
1819 // mbNeedsNumberFormat was set) with a General format.
1820 // 1. setting a format has quite some overhead in the
1821 // ScPatternAttr/ScAttrArray handling, even if identical.
1822 // 2. the General formats may be of different locales.
1823 // XXX if mbNeedsNumberFormat was set even if the current format
1824 // was not General then we'd have to obtain the current format here
1825 // and check at least the types.
1826 if ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
1828 // set number format explicitly
1829 pDocument->SetNumberFormat( aPos, nFormatIndex );
1830 bChanged = true;
1833 mbNeedsNumberFormat = false;
1836 // In case of changes just obtain the result, no temporary and
1837 // comparison needed anymore.
1838 if (bChanged)
1840 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
1841 // Also handle special cases of initial results after loading.
1842 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1844 ScFormulaResult aNewResult( p->GetResultToken().get());
1845 StackVar eOld = aResult.GetCellResultType();
1846 StackVar eNew = aNewResult.GetCellResultType();
1847 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
1849 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
1850 // -> no change
1852 else
1854 if ( eOld == svHybridCell || eOld == svHybridValueCell ) // string result from SetFormulaResultString?
1855 eOld = svString; // ScHybridCellToken has a valid GetString method
1857 // #i106045# use approxEqual to compare with stored value
1858 bContentChanged = (eOld != eNew ||
1859 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
1860 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1864 aResult.SetToken( p->GetResultToken().get() );
1866 else
1868 ScFormulaResult aNewResult( p->GetResultToken().get());
1869 StackVar eOld = aResult.GetCellResultType();
1870 StackVar eNew = aNewResult.GetCellResultType();
1871 bChanged = (eOld != eNew ||
1872 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
1873 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1875 // #i102616# handle special cases of initial results after loading
1876 // (only if the sheet is still marked unchanged)
1877 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1879 if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
1880 ((eOld == svHybridCell || eOld == svHybridValueCell) &&
1881 eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
1882 (eOld == svDouble && eNew == svDouble &&
1883 rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
1885 // no change, see above
1887 else
1888 bContentChanged = true;
1891 aResult.Assign( aNewResult);
1894 // Precision as shown?
1895 if ( aResult.IsValue() && !p->GetError()
1896 && pDocument->GetDocOptions().IsCalcAsShown()
1897 && nFormatType != css::util::NumberFormat::DATE
1898 && nFormatType != css::util::NumberFormat::TIME
1899 && nFormatType != css::util::NumberFormat::DATETIME )
1901 sal_uLong nFormat = pDocument->GetNumberFormat( aPos );
1902 aResult.SetDouble( pDocument->RoundValueAsShown(
1903 aResult.GetDouble(), nFormat));
1905 if (eTailParam == SCITP_NORMAL)
1907 ResetDirty();
1909 if( aResult.GetMatrix() )
1911 // If the formula wasn't entered as a matrix formula, live on with
1912 // the upper left corner and let reference counting delete the matrix.
1913 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
1914 aResult.SetToken( aResult.GetCellResultToken().get());
1916 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1918 // Coded double error may occur via filter import.
1919 sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble());
1920 aResult.SetResultError( nErr);
1921 bChanged = bContentChanged = true;
1924 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
1926 // pass bIgnoreLock=true, because even if called from pending row height update,
1927 // a changed result must still reset the stream flag
1928 pDocument->SetStreamValid(aPos.Tab(), false, true);
1930 if ( !pCode->IsRecalcModeAlways() )
1931 pDocument->RemoveFromFormulaTree( this );
1933 // FORCED cells also immediately tested for validity (start macro possibly)
1935 if ( pCode->IsRecalcModeForced() )
1937 sal_uLong nValidation = static_cast<const SfxUInt32Item*>(pDocument->GetAttr(
1938 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
1939 if ( nValidation )
1941 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
1942 ScRefCellValue aTmpCell(this);
1943 if ( pData && !pData->IsDataValid(aTmpCell, aPos))
1944 pData->DoCalcError( this );
1948 // Reschedule slows the whole thing down considerably, thus only execute on percent change
1949 ScProgress *pProgress = ScProgress::GetInterpretProgress();
1950 if (pProgress && pProgress->Enabled())
1952 pProgress->SetStateCountDownOnPercent(
1953 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
1956 switch (p->GetVolatileType())
1958 case ScInterpreter::VOLATILE:
1959 // Volatile via built-in volatile functions. No actions needed.
1960 break;
1961 case ScInterpreter::VOLATILE_MACRO:
1962 // The formula contains a volatile macro.
1963 pCode->SetExclusiveRecalcModeAlways();
1964 pDocument->PutInFormulaTree(this);
1965 StartListeningTo(pDocument);
1966 break;
1967 case ScInterpreter::NOT_VOLATILE:
1968 if (pCode->IsRecalcModeAlways())
1970 // The formula was previously volatile, but no more.
1971 pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
1972 pCode->SetExclusiveRecalcModeNormal();
1974 else
1976 // non-volatile formula. End listening to the area in case
1977 // it's listening due to macro module change.
1978 pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
1980 pDocument->RemoveFromFormulaTree(this);
1981 break;
1982 default:
1986 else
1988 // Cells with compiler errors should not be marked dirty forever
1989 OSL_ENSURE( pCode->GetCodeError(), "no RPN code und no errors ?!?!" );
1990 ResetDirty();
1994 void ScFormulaCell::SetCompile( bool bVal )
1996 bCompile = bVal;
1999 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows, bool bDirtyFlag )
2001 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
2002 if (pMat)
2003 pMat->SetMatColsRows( nCols, nRows );
2004 else if (nCols || nRows)
2006 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2007 // Setting the new token actually forces an empty result at this top
2008 // left cell, so have that recalculated.
2009 SetDirty( bDirtyFlag );
2013 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2015 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
2016 if (pMat)
2017 pMat->GetMatColsRows( nCols, nRows);
2018 else
2020 nCols = 0;
2021 nRows = 0;
2025 void ScFormulaCell::SetInChangeTrack( bool bVal )
2027 bInChangeTrack = bVal;
2030 void ScFormulaCell::Notify( const SfxHint& rHint )
2032 const SfxSimpleHint* pSimpleHint = dynamic_cast<const SfxSimpleHint*>(&rHint);
2033 if (!pSimpleHint)
2034 return;
2036 sal_uLong nHint = pSimpleHint->GetId();
2037 if (nHint == SC_HINT_REFERENCE)
2039 const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2041 switch (rRefHint.getType())
2043 case sc::RefHint::Moved:
2045 // One of the references has moved.
2047 const sc::RefMovedHint& rRefMoved = static_cast<const sc::RefMovedHint&>(rRefHint);
2048 if (!IsShared() || IsSharedTop())
2050 sc::RefUpdateResult aRes = pCode->MoveReference(aPos, rRefMoved.getContext());
2051 if (aRes.mbNameModified)
2053 // RPN token needs to be re-generated.
2054 bCompile = true;
2055 CompileTokenArray();
2056 SetDirtyVar();
2060 break;
2061 case sc::RefHint::ColumnReordered:
2063 const sc::RefColReorderHint& rRefColReorder =
2064 static_cast<const sc::RefColReorderHint&>(rRefHint);
2065 if (!IsShared() || IsSharedTop())
2066 pCode->MoveReferenceColReorder(
2067 aPos, rRefColReorder.getTab(),
2068 rRefColReorder.getStartRow(),
2069 rRefColReorder.getEndRow(),
2070 rRefColReorder.getColMap());
2072 break;
2073 case sc::RefHint::RowReordered:
2075 const sc::RefRowReorderHint& rRefRowReorder =
2076 static_cast<const sc::RefRowReorderHint&>(rRefHint);
2077 if (!IsShared() || IsSharedTop())
2078 pCode->MoveReferenceRowReorder(
2079 aPos, rRefRowReorder.getTab(),
2080 rRefRowReorder.getStartColumn(),
2081 rRefRowReorder.getEndColumn(),
2082 rRefRowReorder.getRowMap());
2084 break;
2085 case sc::RefHint::StartListening:
2087 StartListeningTo( pDocument);
2089 break;
2090 case sc::RefHint::StopListening:
2092 EndListeningTo( pDocument);
2094 break;
2095 default:
2099 return;
2102 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
2104 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_TABLEOPDIRTY))
2106 bool bForceTrack = false;
2107 if ( nHint & SC_HINT_TABLEOPDIRTY )
2109 bForceTrack = !bTableOpDirty;
2110 if ( !bTableOpDirty )
2112 pDocument->AddTableOpFormulaCell( this );
2113 bTableOpDirty = true;
2116 else
2118 bForceTrack = !bDirty;
2119 SetDirtyVar();
2121 // Don't remove from FormulaTree to put in FormulaTrack to
2122 // put in FormulaTree again and again, only if necessary.
2123 // Any other means except ScRecalcMode::ALWAYS by which a cell could
2124 // be in FormulaTree if it would notify other cells through
2125 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2126 // Yes. The new TableOpDirty made it necessary to have a
2127 // forced mode where formulas may still be in FormulaTree from
2128 // TableOpDirty but have to notify dependents for normal dirty.
2129 if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
2130 || pCode->IsRecalcModeAlways())
2131 && !pDocument->IsInFormulaTrack( this ) )
2132 pDocument->AppendToFormulaTrack( this );
2137 void ScFormulaCell::Query( SvtListener::QueryBase& rQuery ) const
2139 switch (rQuery.getId())
2141 case SC_LISTENER_QUERY_FORMULA_GROUP_POS:
2143 sc::RefQueryFormulaGroup& rRefQuery =
2144 static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2145 if (IsShared())
2146 rRefQuery.add(aPos);
2148 break;
2149 default:
2154 void ScFormulaCell::SetDirty( bool bDirtyFlag )
2156 if (IsInChangeTrack())
2157 return;
2159 if ( pDocument->GetHardRecalcState() )
2161 SetDirtyVar();
2162 pDocument->SetStreamValid(aPos.Tab(), false);
2163 return;
2166 // Avoid multiple formula tracking in Load() and in CompileAll()
2167 // after CopyScenario() and CopyBlockFromClip().
2168 // If unconditional formula tracking is needed, set bDirty=false
2169 // before calling SetDirty(), for example in CompileTokenArray().
2170 if ( !bDirty || mbPostponedDirty || !pDocument->IsInFormulaTree( this ) )
2172 if( bDirtyFlag )
2173 SetDirtyVar();
2174 pDocument->AppendToFormulaTrack( this );
2176 // While loading a document listeners have not been established yet.
2177 // Tracking would remove this cell from the FormulaTrack and add it to
2178 // the FormulaTree, once in there it would be assumed that its
2179 // dependents already had been tracked and it would be skipped on a
2180 // subsequent notify. Postpone tracking until all listeners are set.
2181 if (!pDocument->IsImportingXML())
2182 pDocument->TrackFormulas();
2185 pDocument->SetStreamValid(aPos.Tab(), false);
2188 void ScFormulaCell::SetDirtyVar()
2190 bDirty = true;
2191 mbPostponedDirty = false;
2192 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2193 mxGroup->meCalcState = sc::GroupCalcEnabled;
2195 // mark the sheet of this cell to be calculated
2196 //#FIXME do we need to revert this remnant of old fake vba events? pDocument->AddCalculateTable( aPos.Tab() );
2199 void ScFormulaCell::SetDirtyAfterLoad()
2201 bDirty = true;
2202 if ( !pDocument->GetHardRecalcState() )
2203 pDocument->PutInFormulaTree( this );
2206 void ScFormulaCell::ResetTableOpDirtyVar()
2208 bTableOpDirty = false;
2211 void ScFormulaCell::SetTableOpDirty()
2213 if ( !IsInChangeTrack() )
2215 if ( pDocument->GetHardRecalcState() )
2216 bTableOpDirty = true;
2217 else
2219 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
2221 if ( !bTableOpDirty )
2223 pDocument->AddTableOpFormulaCell( this );
2224 bTableOpDirty = true;
2226 pDocument->AppendToFormulaTrack( this );
2227 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
2233 bool ScFormulaCell::IsDirtyOrInTableOpDirty() const
2235 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
2238 void ScFormulaCell::SetResultDouble( double n )
2240 aResult.SetDouble(n);
2243 void ScFormulaCell::SetResultToken( const formula::FormulaToken* pToken )
2245 aResult.SetToken(pToken);
2248 svl::SharedString ScFormulaCell::GetResultString() const
2250 return aResult.GetString();
2253 void ScFormulaCell::SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, formula::FormulaToken* pUL )
2255 aResult.SetMatrix(nCols, nRows, pMat, pUL);
2258 void ScFormulaCell::SetErrCode( sal_uInt16 n )
2260 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2261 * used whether it is solely for transport of a simple result error and get
2262 * rid of that abuse. */
2263 pCode->SetCodeError( n );
2264 // Hard set errors are transported as result type value per convention,
2265 // e.g. via clipboard. ScFormulaResult::IsValue() and
2266 // ScFormulaResult::GetDouble() handle that.
2267 aResult.SetResultError( n );
2270 void ScFormulaCell::SetResultError( sal_uInt16 n )
2272 aResult.SetResultError( n );
2275 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
2277 if ( (nBits & RECALCMODE_EMASK) != ScRecalcMode::NORMAL )
2278 SetDirtyVar();
2279 if ( nBits & ScRecalcMode::ONLOAD_ONCE )
2280 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
2281 nBits = (nBits & ~RECALCMODE_EMASK) | ScRecalcMode::NORMAL;
2283 pCode->AddRecalcMode( nBits );
2286 void ScFormulaCell::SetHybridDouble( double n )
2288 aResult.SetHybridDouble( n);
2291 void ScFormulaCell::SetHybridString( const svl::SharedString& r )
2293 aResult.SetHybridString( r);
2296 void ScFormulaCell::SetHybridFormula( const OUString& r,
2297 const formula::FormulaGrammar::Grammar eGrammar )
2299 aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2302 OUString ScFormulaCell::GetHybridFormula() const
2304 return aResult.GetHybridFormula();
2307 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2308 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2310 OUString aCellString;
2312 Color* pColor;
2314 // Cell Text uses the Cell format while the URL uses
2315 // the default format for the type.
2316 sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos );
2317 SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
2319 sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, css::util::NumberFormat::NUMBER);
2321 if ( IsValue() )
2323 double fValue = GetValue();
2324 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2326 else
2328 aCellString = GetString().getString();
2329 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2331 ScConstMatrixRef xMat( aResult.GetMatrix());
2332 if (xMat)
2334 // determine if the matrix result is a string or value.
2335 if (!xMat->IsValue(0, 1))
2336 rURL = xMat->GetString(0, 1).getString();
2337 else
2338 pFormatter->GetOutputString(
2339 xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2342 if(rURL.isEmpty())
2344 if(IsValue())
2345 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2346 else
2347 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2351 bool ScFormulaCell::IsMultilineResult()
2353 if (!IsValue())
2354 return aResult.IsMultiline();
2355 return false;
2358 bool ScFormulaCell::NeedsInterpret() const
2360 if (mxGroup && mxGroup->meKernelState == sc::OpenCLKernelCompilationScheduled)
2361 return false;
2363 if (!IsDirtyOrInTableOpDirty())
2364 return false;
2366 return (pDocument->GetAutoCalc() || (cMatrixFlag != MM_NONE));
2369 void ScFormulaCell::MaybeInterpret()
2371 if (NeedsInterpret())
2372 Interpret();
2375 bool ScFormulaCell::IsHyperLinkCell() const
2377 return pCode && pCode->IsHyperLink();
2380 EditTextObject* ScFormulaCell::CreateURLObject()
2382 OUString aCellText;
2383 OUString aURL;
2384 GetURLResult( aURL, aCellText );
2386 return ScEditUtil::CreateURLObjectFromURL( *pDocument, aURL, aCellText );
2389 bool ScFormulaCell::IsEmpty()
2391 MaybeInterpret();
2392 return aResult.GetCellResultType() == formula::svEmptyCell;
2395 bool ScFormulaCell::IsEmptyDisplayedAsString()
2397 MaybeInterpret();
2398 return aResult.IsEmptyDisplayedAsString();
2401 bool ScFormulaCell::IsValue()
2403 MaybeInterpret();
2404 return aResult.IsValue();
2407 bool ScFormulaCell::IsValueNoError()
2409 MaybeInterpret();
2410 if (pCode->GetCodeError())
2411 return false;
2413 return aResult.IsValueNoError();
2416 bool ScFormulaCell::IsValueNoError() const
2418 if (NeedsInterpret())
2419 // false if the cell is dirty & needs to be interpreted.
2420 return false;
2422 if (pCode->GetCodeError())
2423 return false;
2425 return aResult.IsValueNoError();
2428 bool ScFormulaCell::IsHybridValueCell()
2430 return aResult.GetType() == formula::svHybridValueCell;
2433 double ScFormulaCell::GetValue()
2435 MaybeInterpret();
2436 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
2437 !aResult.GetResultError())
2438 return aResult.GetDouble();
2439 return 0.0;
2442 svl::SharedString ScFormulaCell::GetString()
2444 MaybeInterpret();
2445 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
2446 !aResult.GetResultError())
2447 return aResult.GetString();
2449 return svl::SharedString::getEmptyString();
2452 const ScMatrix* ScFormulaCell::GetMatrix()
2454 if ( pDocument->GetAutoCalc() )
2456 if( IsDirtyOrInTableOpDirty()
2457 // Was stored !bDirty but an accompanying matrix cell was bDirty?
2458 || (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix()))
2459 Interpret();
2461 return aResult.GetMatrix().get();
2464 bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
2466 switch ( cMatrixFlag )
2468 case MM_FORMULA :
2469 rPos = aPos;
2470 return true;
2471 case MM_REFERENCE :
2473 pCode->Reset();
2474 formula::FormulaToken* t = pCode->GetNextReferenceRPN();
2475 if( t )
2477 ScSingleRefData& rRef = *t->GetSingleRef();
2478 ScAddress aAbs = rRef.toAbs(aPos);
2479 if (ValidAddress(aAbs))
2481 rPos = aAbs;
2482 return true;
2486 break;
2488 return false;
2492 Edge-Values:
2495 4 16
2498 inside: 1
2499 outside: 0
2500 (reserved: open: 32)
2503 sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) const
2505 switch ( cMatrixFlag )
2507 case MM_FORMULA :
2508 case MM_REFERENCE :
2510 static SCCOL nC;
2511 static SCROW nR;
2512 ScAddress aOrg;
2513 if ( !GetMatrixOrigin( aOrg ) )
2514 return 0; // bad luck..
2515 if ( aOrg != rOrgPos )
2516 { // First time or a different matrix than last time.
2517 rOrgPos = aOrg;
2518 const ScFormulaCell* pFCell;
2519 if ( cMatrixFlag == MM_REFERENCE )
2520 pFCell = pDocument->GetFormulaCell(aOrg);
2521 else
2522 pFCell = this; // this MM_FORMULA
2523 // There's only one this, don't compare pFCell==this.
2524 if (pFCell && pFCell->cMatrixFlag == MM_FORMULA)
2526 pFCell->GetMatColsRows( nC, nR );
2527 if ( nC == 0 || nR == 0 )
2529 // No ScMatrixFormulaCellToken available yet, calculate new.
2530 nC = 1;
2531 nR = 1;
2532 ScAddress aTmpOrg;
2533 ScFormulaCell* pCell;
2534 ScAddress aAdr( aOrg );
2535 aAdr.IncCol();
2536 bool bCont = true;
2539 pCell = pDocument->GetFormulaCell(aAdr);
2540 if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
2541 pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
2543 nC++;
2544 aAdr.IncCol();
2546 else
2547 bCont = false;
2548 } while ( bCont );
2549 aAdr = aOrg;
2550 aAdr.IncRow();
2551 bCont = true;
2554 pCell = pDocument->GetFormulaCell(aAdr);
2555 if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
2556 pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
2558 nR++;
2559 aAdr.IncRow();
2561 else
2562 bCont = false;
2563 } while ( bCont );
2565 const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2568 else
2570 #if OSL_DEBUG_LEVEL > 0
2571 OStringBuffer aMsg("broken Matrix, no MatFormula at origin, Pos: ");
2572 OUString aTmp(aPos.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument));
2573 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
2574 aMsg.append(", MatOrg: ");
2575 aTmp = aOrg.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument);
2576 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
2577 OSL_FAIL(aMsg.getStr());
2578 #endif
2579 return 0; // bad luck ...
2582 // here we are, healthy and clean, somewhere in between
2583 SCsCOL dC = aPos.Col() - aOrg.Col();
2584 SCsROW dR = aPos.Row() - aOrg.Row();
2585 sal_uInt16 nEdges = 0;
2586 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2588 if ( dC == 0 )
2589 nEdges |= sc::MatrixEdgeLeft; // left edge
2590 if ( dC+1 == nC )
2591 nEdges |= sc::MatrixEdgeRight; // right edge
2592 if ( dR == 0 )
2593 nEdges |= sc::MatrixEdgeTop; // top edge
2594 if ( dR+1 == nR )
2595 nEdges |= sc::MatrixEdgeBottom; // bottom edge
2596 if ( !nEdges )
2597 nEdges = sc::MatrixEdgeInside; // inside
2599 #if OSL_DEBUG_LEVEL > 0
2600 else
2602 OStringBuffer aMsg( "broken Matrix, Pos: " );
2603 OUString aTmp(aPos.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument));
2604 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
2605 aMsg.append(", MatOrg: ");
2606 aTmp = aOrg.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument);
2607 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
2608 aMsg.append(", MatCols: ");
2609 aMsg.append(static_cast<sal_Int32>( nC ));
2610 aMsg.append(", MatRows: ");
2611 aMsg.append(static_cast<sal_Int32>( nR ));
2612 aMsg.append(", DiffCols: ");
2613 aMsg.append(static_cast<sal_Int32>( dC ));
2614 aMsg.append(", DiffRows: ");
2615 aMsg.append(static_cast<sal_Int32>( dR ));
2616 OSL_FAIL( aMsg.makeStringAndClear().getStr());
2618 #endif
2619 return nEdges;
2621 default:
2622 return 0;
2626 sal_uInt16 ScFormulaCell::GetErrCode()
2628 MaybeInterpret();
2630 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2631 * and not also abused for signaling other error conditions we could bail
2632 * out even before attempting to interpret broken code. */
2633 sal_uInt16 nErr = pCode->GetCodeError();
2634 if (nErr)
2635 return nErr;
2636 return aResult.GetResultError();
2639 sal_uInt16 ScFormulaCell::GetRawError()
2641 sal_uInt16 nErr = pCode->GetCodeError();
2642 if (nErr)
2643 return nErr;
2644 return aResult.GetResultError();
2647 bool ScFormulaCell::GetErrorOrValue( sal_uInt16& rErr, double& rVal )
2649 MaybeInterpret();
2651 rErr = pCode->GetCodeError();
2652 if (rErr)
2653 return true;
2655 return aResult.GetErrorOrDouble(rErr, rVal);
2658 sc::FormulaResultValue ScFormulaCell::GetResult()
2660 MaybeInterpret();
2662 sal_uInt16 nErr = pCode->GetCodeError();
2663 if (nErr)
2664 return sc::FormulaResultValue(nErr);
2666 return aResult.GetResult();
2669 sc::FormulaResultValue ScFormulaCell::GetResult() const
2671 sal_uInt16 nErr = pCode->GetCodeError();
2672 if (nErr)
2673 return sc::FormulaResultValue(nErr);
2675 return aResult.GetResult();
2678 bool ScFormulaCell::HasOneReference( ScRange& r ) const
2680 pCode->Reset();
2681 formula::FormulaToken* p = pCode->GetNextReferenceRPN();
2682 if( p && !pCode->GetNextReferenceRPN() ) // only one!
2684 SingleDoubleRefProvider aProv( *p );
2685 r.aStart = aProv.Ref1.toAbs(aPos);
2686 r.aEnd = aProv.Ref2.toAbs(aPos);
2687 return true;
2689 else
2690 return false;
2693 bool
2694 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
2696 /* If there appears just one reference in the formula, it's the same
2697 as HasOneReference(). If there are more of them, they can denote
2698 one range if they are (sole) arguments of one function.
2699 Union of these references must form one range and their
2700 intersection must be empty set.
2703 // Detect the simple case of exactly one reference in advance without all
2704 // overhead.
2705 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
2706 // work again, where the function does not have only references.
2707 if (HasOneReference( rRange))
2708 return true;
2710 pCode->Reset();
2711 // Get first reference, if any
2712 formula::FormulaToken* const pFirstReference(pCode->GetNextReferenceRPN());
2713 if (pFirstReference)
2715 // Collect all consecutive references, starting by the one
2716 // already found
2717 std::deque<formula::FormulaToken*> aReferences;
2718 aReferences.push_back(pFirstReference);
2719 FormulaToken* pToken(pCode->NextRPN());
2720 FormulaToken* pFunction(0);
2721 while (pToken)
2723 if (lcl_isReference(*pToken))
2725 aReferences.push_back(pToken);
2726 pToken = pCode->NextRPN();
2728 else
2730 if (pToken->IsFunction())
2732 pFunction = pToken;
2734 break;
2737 if (pFunction && !pCode->GetNextReferenceRPN()
2738 && (pFunction->GetParamCount() == aReferences.size()))
2740 return lcl_refListFormsOneRange(aPos, aReferences, rRange);
2743 return false;
2746 bool ScFormulaCell::HasRelNameReference() const
2748 pCode->Reset();
2749 formula::FormulaToken* t;
2750 while ( ( t = pCode->GetNextReferenceRPN() ) != NULL )
2752 if ( t->GetSingleRef()->IsRelName() ||
2753 (t->GetType() == formula::svDoubleRef &&
2754 t->GetDoubleRef()->Ref2.IsRelName()) )
2755 return true;
2757 return false;
2760 bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext& rCxt )
2762 if (rCxt.meMode != URM_INSDEL)
2763 // Just in case...
2764 return false;
2766 if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
2767 // No movement.
2768 return false;
2770 if (!rCxt.maRange.In(aPos))
2771 return false;
2773 // This formula cell itself is being shifted during cell range
2774 // insertion or deletion. Update its position.
2775 aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
2777 return true;
2780 namespace {
2783 * Check if we need to re-compile column or row names.
2785 bool checkCompileColRowName(
2786 const sc::RefUpdateContext& rCxt, ScDocument& rDoc, ScTokenArray& rCode,
2787 const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
2789 switch (rCxt.meMode)
2791 case URM_INSDEL:
2793 if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
2794 return false;
2796 formula::FormulaToken* t;
2797 ScRangePairList* pColList = rDoc.GetColNameRanges();
2798 ScRangePairList* pRowList = rDoc.GetRowNameRanges();
2799 rCode.Reset();
2800 while ((t = rCode.GetNextColRowName()) != NULL)
2802 ScSingleRefData& rRef = *t->GetSingleRef();
2803 if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
2804 { // ColName
2805 ScAddress aAdr = rRef.toAbs(aPos);
2806 ScRangePair* pR = pColList->Find( aAdr );
2807 if ( pR )
2808 { // defined
2809 if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
2810 return true;
2812 else
2813 { // on the fly
2814 if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
2815 return true;
2818 if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
2819 { // RowName
2820 ScAddress aAdr = rRef.toAbs(aPos);
2821 ScRangePair* pR = pRowList->Find( aAdr );
2822 if ( pR )
2823 { // defined
2824 if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
2825 return true;
2827 else
2828 { // on the fly
2829 if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
2830 return true;
2835 break;
2836 case URM_MOVE:
2837 { // Recomplie for Move/D&D when ColRowName was moved or this Cell
2838 // points to one and was moved.
2839 bool bMoved = (aPos != aOldPos);
2840 if (bMoved)
2841 return true;
2843 rCode.Reset();
2844 const formula::FormulaToken* t = rCode.GetNextColRowName();
2845 for (; t; t = rCode.GetNextColRowName())
2847 const ScSingleRefData& rRef = *t->GetSingleRef();
2848 ScAddress aAbs = rRef.toAbs(aPos);
2849 if (ValidAddress(aAbs))
2851 if (rCxt.maRange.In(aAbs))
2852 return true;
2856 break;
2857 case URM_COPY:
2858 return bValChanged;
2859 default:
2863 return false;
2866 void setOldCodeToUndo(
2867 ScDocument* pUndoDoc, const ScAddress& aUndoPos, ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, sal_uInt8 cMatrixFlag)
2869 // Copy the cell to aUndoPos, which is its current position in the document,
2870 // so this works when UpdateReference is called before moving the cells
2871 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
2872 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
2874 // If there is already a formula cell in the undo document, don't overwrite it,
2875 // the first (oldest) is the important cell.
2876 if (pUndoDoc->GetCellType(aUndoPos) == CELLTYPE_FORMULA)
2877 return;
2879 ScFormulaCell* pFCell =
2880 new ScFormulaCell(
2881 pUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(), eTempGrammar, cMatrixFlag);
2883 pFCell->SetResultToken(NULL); // to recognize it as changed later (Cut/Paste!)
2884 pUndoDoc->SetFormulaCell(aUndoPos, pFCell);
2889 bool ScFormulaCell::UpdateReferenceOnShift(
2890 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
2892 if (rCxt.meMode != URM_INSDEL)
2893 // Just in case...
2894 return false;
2896 bool bCellStateChanged = false;
2897 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
2898 if ( pUndoCellPos )
2899 aUndoPos = *pUndoCellPos;
2900 ScAddress aOldPos( aPos );
2901 bCellStateChanged = UpdatePosOnShift(rCxt);
2903 // Check presence of any references or column row names.
2904 bool bHasRefs = pCode->HasReferences();
2905 bool bHasColRowNames = false;
2906 if (!bHasRefs)
2908 pCode->Reset();
2909 bHasColRowNames = (pCode->GetNextColRowName() != NULL);
2910 bHasRefs = bHasRefs || bHasColRowNames;
2912 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
2914 if (!bHasRefs && !bOnRefMove)
2915 // This formula cell contains no references, nor needs recalculating
2916 // on reference update. Bail out.
2917 return bCellStateChanged;
2919 boost::scoped_ptr<ScTokenArray> pOldCode;
2920 if (pUndoDoc)
2921 pOldCode.reset(pCode->Clone());
2923 bool bValChanged = false;
2924 bool bRefModified = false;
2925 bool bRecompile = bCompile;
2927 if (bHasRefs)
2929 // Update cell or range references.
2930 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
2931 bRefModified = aRes.mbReferenceModified;
2932 bValChanged = aRes.mbValueChanged;
2933 if (aRes.mbNameModified)
2934 bRecompile = true;
2937 if (bValChanged || bRefModified)
2938 bCellStateChanged = true;
2940 if (bOnRefMove)
2941 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
2942 bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
2944 bool bNewListening = false;
2945 bool bInDeleteUndo = false;
2947 if (bHasRefs)
2949 // Upon Insert ColRowNames have to be recompiled in case the
2950 // insertion occurs right in front of the range.
2951 if (bHasColRowNames && !bRecompile)
2952 bRecompile = checkCompileColRowName(rCxt, *pDocument, *pCode, aOldPos, aPos, bValChanged);
2954 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
2955 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
2957 // RelNameRefs are always moved
2958 bool bHasRelName = HasRelNameReference();
2959 // Reference changed and new listening needed?
2960 // Except in Insert/Delete without specialties.
2961 bNewListening = (bRefModified || bRecompile
2962 || (bValChanged && bInDeleteUndo) || bHasRelName);
2964 if ( bNewListening )
2965 EndListeningTo(pDocument, pOldCode.get(), aOldPos);
2968 // NeedDirty for changes except for Copy and Move/Insert without RelNames
2969 bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
2971 if (pUndoDoc && (bValChanged || bOnRefMove))
2972 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
2974 bCompile |= bRecompile;
2975 if (bCompile)
2977 CompileTokenArray( bNewListening ); // no Listening
2978 bNeedDirty = true;
2981 if ( !bInDeleteUndo )
2982 { // In ChangeTrack Delete-Reject listeners are established in
2983 // InsertCol/InsertRow
2984 if ( bNewListening )
2986 // Inserts/Deletes re-establish listeners after all
2987 // UpdateReference calls.
2988 // All replaced shared formula listeners have to be
2989 // established after an Insert or Delete. Do nothing here.
2990 SetNeedsListening( true);
2994 if (bNeedDirty)
2995 { // Cut off references, invalid or similar?
2996 // Postpone SetDirty() until all listeners have been re-established in
2997 // Inserts/Deletes.
2998 mbPostponedDirty = true;
3001 return bCellStateChanged;
3004 bool ScFormulaCell::UpdateReferenceOnMove(
3005 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3007 if (rCxt.meMode != URM_MOVE)
3008 return false;
3010 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3011 if ( pUndoCellPos )
3012 aUndoPos = *pUndoCellPos;
3013 ScAddress aOldPos( aPos );
3015 if (rCxt.maRange.In(aPos))
3017 // The cell is being moved or copied to a new position. I guess the
3018 // position has been updated prior to this call? Determine
3019 // its original position before the move which will be used to adjust
3020 // relative references later.
3021 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3024 // Check presence of any references or column row names.
3025 bool bHasRefs = pCode->HasReferences();
3026 bool bHasColRowNames = false;
3027 if (!bHasRefs)
3029 pCode->Reset();
3030 bHasColRowNames = (pCode->GetNextColRowName() != NULL);
3031 bHasRefs = bHasRefs || bHasColRowNames;
3033 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3035 if (!bHasRefs && !bOnRefMove)
3036 // This formula cell contains no references, nor needs recalculating
3037 // on reference update. Bail out.
3038 return false;
3040 bool bCellStateChanged = false;
3041 boost::scoped_ptr<ScTokenArray> pOldCode;
3042 if (pUndoDoc)
3043 pOldCode.reset(pCode->Clone());
3045 bool bValChanged = false;
3046 bool bRefModified = false;
3048 if (bHasRefs)
3050 // Update cell or range references.
3051 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3052 bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3053 bValChanged = aRes.mbValueChanged;
3054 if (aRes.mbNameModified)
3055 // Re-compile to get the RPN token regenerated to reflect updated names.
3056 bCompile = true;
3059 if (bValChanged || bRefModified)
3060 bCellStateChanged = true;
3062 if (bOnRefMove)
3063 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3064 bOnRefMove = (bValChanged || (aPos != aOldPos));
3066 bool bColRowNameCompile = false;
3067 bool bHasRelName = false;
3068 bool bNewListening = false;
3069 bool bInDeleteUndo = false;
3071 if (bHasRefs)
3073 // Upon Insert ColRowNames have to be recompiled in case the
3074 // insertion occurs right in front of the range.
3075 if (bHasColRowNames)
3076 bColRowNameCompile = checkCompileColRowName(rCxt, *pDocument, *pCode, aOldPos, aPos, bValChanged);
3078 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
3079 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3081 // RelNameRefs are always moved
3082 bHasRelName = HasRelNameReference();
3083 // Reference changed and new listening needed?
3084 // Except in Insert/Delete without specialties.
3085 bNewListening = (bRefModified || bColRowNameCompile
3086 || bValChanged || bHasRelName)
3087 // #i36299# Don't duplicate action during cut&paste / drag&drop
3088 // on a cell in the range moved, start/end listeners is done
3089 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3090 && !(pDocument->IsInsertingFromOtherDoc() && rCxt.maRange.In(aPos));
3092 if ( bNewListening )
3093 EndListeningTo(pDocument, pOldCode.get(), aOldPos);
3096 bool bNeedDirty = false;
3097 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3098 if ( bRefModified || bColRowNameCompile ||
3099 (bValChanged && bHasRelName ) || bOnRefMove)
3100 bNeedDirty = true;
3102 if (pUndoDoc && (bValChanged || bRefModified || bOnRefMove))
3103 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3105 bValChanged = false;
3107 bCompile = (bCompile || bValChanged || bColRowNameCompile);
3108 if ( bCompile )
3110 CompileTokenArray( bNewListening ); // no Listening
3111 bNeedDirty = true;
3114 if ( !bInDeleteUndo )
3115 { // In ChangeTrack Delete-Reject listeners are established in
3116 // InsertCol/InsertRow
3117 if ( bNewListening )
3119 StartListeningTo( pDocument );
3123 if (bNeedDirty)
3124 { // Cut off references, invalid or similar?
3125 sc::AutoCalcSwitch(*pDocument, false);
3126 SetDirty();
3129 return bCellStateChanged;
3132 bool ScFormulaCell::UpdateReferenceOnCopy(
3133 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3135 if (rCxt.meMode != URM_COPY)
3136 return false;
3138 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3139 if ( pUndoCellPos )
3140 aUndoPos = *pUndoCellPos;
3141 ScAddress aOldPos( aPos );
3143 if (rCxt.maRange.In(aPos))
3145 // The cell is being moved or copied to a new position. I guess the
3146 // position has been updated prior to this call? Determine
3147 // its original position before the move which will be used to adjust
3148 // relative references later.
3149 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3152 // Check presence of any references or column row names.
3153 bool bHasRefs = pCode->HasReferences();
3154 pCode->Reset();
3155 bool bHasColRowNames = (pCode->GetNextColRowName() != NULL);
3156 bHasRefs = bHasRefs || bHasColRowNames;
3157 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3159 if (!bHasRefs && !bOnRefMove)
3160 // This formula cell contains no references, nor needs recalculating
3161 // on reference update. Bail out.
3162 return false;
3164 boost::scoped_ptr<ScTokenArray> pOldCode;
3165 if (pUndoDoc)
3166 pOldCode.reset(pCode->Clone());
3168 if (bOnRefMove)
3169 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3170 bOnRefMove = (aPos != aOldPos);
3172 bool bNeedDirty = bOnRefMove;
3174 if (pUndoDoc && bOnRefMove)
3175 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3177 if (bCompile)
3179 CompileTokenArray(false); // no Listening
3180 bNeedDirty = true;
3183 if (bNeedDirty)
3184 { // Cut off references, invalid or similar?
3185 sc::AutoCalcSwitch(*pDocument, false);
3186 SetDirty();
3189 return false;
3192 bool ScFormulaCell::UpdateReference(
3193 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3195 if (pDocument->IsClipOrUndo())
3196 return false;
3198 if (mxGroup && mxGroup->mpTopCell != this)
3200 // This is not a top cell of a formula group. Don't update references.
3202 switch (rCxt.meMode)
3204 case URM_INSDEL:
3205 return UpdatePosOnShift(rCxt);
3206 break;
3207 default:
3210 return false;
3213 switch (rCxt.meMode)
3215 case URM_INSDEL:
3216 return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3217 case URM_MOVE:
3218 return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3219 case URM_COPY:
3220 return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3221 default:
3225 return false;
3228 void ScFormulaCell::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
3230 // Adjust tokens only when it's not grouped or grouped top cell.
3231 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3232 bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3233 if (pDocument->IsClipOrUndo() || !pCode->HasReferences())
3235 if (bPosChanged)
3236 aPos.IncTab(rCxt.mnSheets);
3238 return;
3241 EndListeningTo( pDocument );
3242 ScAddress aOldPos = aPos;
3243 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3244 if (bPosChanged)
3245 aPos.IncTab(rCxt.mnSheets);
3247 if (!bAdjustCode)
3248 return;
3250 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aOldPos);
3251 if (aRes.mbNameModified)
3252 // Re-compile after new sheet(s) have been inserted.
3253 bCompile = true;
3255 // no StartListeningTo because the new sheets have not been inserted yet.
3258 bool ScFormulaCell::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
3260 // Adjust tokens only when it's not grouped or grouped top cell.
3261 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3262 bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3263 if (pDocument->IsClipOrUndo() || !pCode->HasReferences())
3265 if (bPosChanged)
3266 aPos.IncTab(-1*rCxt.mnSheets);
3267 return false;
3270 EndListeningTo( pDocument );
3271 // IncTab _after_ EndListeningTo und _before_ Compiler UpdateDeleteTab!
3272 ScAddress aOldPos = aPos;
3273 if (bPosChanged)
3274 aPos.IncTab(-1*rCxt.mnSheets);
3276 if (!bAdjustCode)
3277 return false;
3279 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
3280 if (aRes.mbNameModified)
3281 // Re-compile after sheet(s) have been deleted.
3282 bCompile = true;
3284 return aRes.mbReferenceModified;
3287 void ScFormulaCell::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
3289 // Adjust tokens only when it's not grouped or grouped top cell.
3290 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3292 if (!pCode->HasReferences() || pDocument->IsClipOrUndo())
3294 aPos.SetTab(nTabNo);
3295 return;
3298 EndListeningTo(pDocument);
3299 ScAddress aOldPos = aPos;
3300 // SetTab _after_ EndListeningTo und _before_ Compiler UpdateMoveTab !
3301 aPos.SetTab(nTabNo);
3303 // no StartListeningTo because pTab[nTab] not yet correct!
3305 if (!bAdjustCode)
3306 return;
3308 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
3309 if (aRes.mbNameModified)
3310 // Re-compile after sheet(s) have been deleted.
3311 bCompile = true;
3314 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
3316 if (pDocument->IsClipOrUndo())
3317 return;
3319 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3320 if (!bAdjustCode)
3321 return;
3323 pCode->Reset();
3324 formula::FormulaToken* p = pCode->GetNextReferenceRPN();
3325 while (p)
3327 ScSingleRefData& rRef1 = *p->GetSingleRef();
3328 if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3329 rRef1.IncTab(1);
3330 if (p->GetType() == formula::svDoubleRef)
3332 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3333 if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3334 rRef2.IncTab(1);
3336 p = pCode->GetNextReferenceRPN();
3340 bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
3342 if (pDocument->IsClipOrUndo())
3343 return false;
3345 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3346 if (!bAdjustCode)
3347 return false;
3349 bool bRet = false;
3350 pCode->Reset();
3351 formula::FormulaToken* p = pCode->GetNextReferenceRPN();
3352 while (p)
3354 ScSingleRefData& rRef1 = *p->GetSingleRef();
3355 if (!rRef1.IsTabRel())
3357 if (nTable != rRef1.Tab())
3358 bRet = true;
3359 else if (nTable != aPos.Tab())
3360 rRef1.SetAbsTab(aPos.Tab());
3362 if (p->GetType() == formula::svDoubleRef)
3364 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3365 if (!rRef2.IsTabRel())
3367 if(nTable != rRef2.Tab())
3368 bRet = true;
3369 else if (nTable != aPos.Tab())
3370 rRef2.SetAbsTab(aPos.Tab());
3373 p = pCode->GetNextReferenceRPN();
3375 return bRet;
3378 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3380 if ( bForceIfNameInUse && !bCompile )
3381 bCompile = pCode->HasNameOrColRowName();
3382 if ( bCompile )
3383 pCode->SetCodeError( 0 ); // make sure it will really be compiled
3384 CompileTokenArray();
3387 // Reference transposition is only called in Clipboard Document
3388 void ScFormulaCell::TransposeReference()
3390 bool bFound = false;
3391 pCode->Reset();
3392 formula::FormulaToken* t;
3393 while ( ( t = pCode->GetNextReference() ) != NULL )
3395 ScSingleRefData& rRef1 = *t->GetSingleRef();
3396 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3398 bool bDouble = (t->GetType() == formula::svDoubleRef);
3399 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3400 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3402 SCCOLROW nTemp;
3404 nTemp = rRef1.Col();
3405 rRef1.SetRelCol(rRef1.Row());
3406 rRef1.SetRelRow(nTemp);
3408 if ( bDouble )
3410 nTemp = rRef2.Col();
3411 rRef2.SetRelCol(rRef2.Row());
3412 rRef2.SetRelRow(nTemp);
3415 bFound = true;
3420 if (bFound)
3421 bCompile = true;
3424 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3425 ScDocument* pUndoDoc )
3427 EndListeningTo( pDocument );
3429 ScAddress aOldPos = aPos;
3430 bool bPosChanged = false; // Whether this cell has been moved
3432 ScRange aDestRange( rDest, ScAddress(
3433 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3434 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3435 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3436 if ( aDestRange.In( aOldPos ) )
3438 // Count back Positions
3439 SCsCOL nRelPosX = aOldPos.Col();
3440 SCsROW nRelPosY = aOldPos.Row();
3441 SCsTAB nRelPosZ = aOldPos.Tab();
3442 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
3443 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3444 bPosChanged = true;
3447 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
3448 bool bRefChanged = false;
3449 formula::FormulaToken* t;
3451 pCode->Reset();
3452 while( (t = pCode->GetNextReferenceOrName()) != NULL )
3454 if( t->GetOpCode() == ocName )
3456 ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
3457 if (pName)
3459 if (pName->IsModified())
3460 bRefChanged = true;
3463 else if( t->GetType() != svIndex )
3465 SingleDoubleRefModifier aMod(*t);
3466 ScComplexRefData& rRef = aMod.Ref();
3467 ScRange aAbs = rRef.toAbs(aOldPos);
3468 bool bMod = (ScRefUpdate::UpdateTranspose(pDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3469 if (bMod)
3471 rRef.SetRange(aAbs, aPos); // based on the new anchor position.
3472 bRefChanged = true;
3477 if (bRefChanged)
3479 if (pUndoDoc)
3481 ScFormulaCell* pFCell = new ScFormulaCell(
3482 pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(), eTempGrammar, cMatrixFlag);
3484 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
3485 pUndoDoc->SetFormulaCell(aPos, pFCell);
3488 bCompile = true;
3489 CompileTokenArray(); // also call StartListeningTo
3490 SetDirty();
3492 else
3493 StartListeningTo( pDocument ); // Listener as previous
3495 delete pOld;
3498 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3500 EndListeningTo( pDocument );
3502 bool bRefChanged = false;
3503 formula::FormulaToken* t;
3505 pCode->Reset();
3506 while( (t = pCode->GetNextReferenceOrName()) != NULL )
3508 if( t->GetOpCode() == ocName )
3510 ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
3511 if (pName)
3513 if (pName->IsModified())
3514 bRefChanged = true;
3517 else if( t->GetType() != svIndex )
3519 SingleDoubleRefModifier aMod(*t);
3520 ScComplexRefData& rRef = aMod.Ref();
3521 ScRange aAbs = rRef.toAbs(aPos);
3522 bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3523 if (bMod)
3525 rRef.SetRange(aAbs, aPos);
3526 bRefChanged = true;
3531 if (bRefChanged)
3533 bCompile = true;
3534 CompileTokenArray(); // Also call StartListeningTo
3535 SetDirty();
3537 else
3538 StartListeningTo( pDocument ); // Listener as previous
3541 static void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
3543 for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
3545 if (p->GetOpCode() == ocName)
3547 sal_uInt16 nTokenIndex = p->GetIndex();
3548 rIndexes.insert( nTokenIndex );
3550 ScRangeData* pSubName = pNames->findByIndex(p->GetIndex());
3551 if (pSubName)
3552 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
3557 void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
3559 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
3562 void ScFormulaCell::SetChanged(bool b)
3564 bChanged = b;
3567 void ScFormulaCell::SetCode( ScTokenArray* pNew )
3569 assert(!mxGroup); // Don't call this if it's shared.
3570 delete pCode;
3571 pCode = pNew; // takes ownership.
3574 void ScFormulaCell::SetRunning( bool bVal )
3576 bRunning = bVal;
3579 void ScFormulaCell::CompileDBFormula( sc::CompileFormulaContext& rCxt )
3581 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
3583 OpCode eOp = p->GetOpCode();
3584 if ( eOp == ocDBArea || eOp == ocTableRef )
3586 bCompile = true;
3587 CompileTokenArray(rCxt);
3588 SetDirty();
3589 break;
3594 void ScFormulaCell::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
3596 pCode->Reset();
3597 for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
3599 if ( p->GetOpCode() == ocColRowName )
3601 bCompile = true;
3602 CompileTokenArray(rCxt);
3603 SetDirty();
3604 break;
3609 void ScFormulaCell::SetPrevious( ScFormulaCell* pF ) { pPrevious = pF; }
3610 void ScFormulaCell::SetNext( ScFormulaCell* pF ) { pNext = pF; }
3611 void ScFormulaCell::SetPreviousTrack( ScFormulaCell* pF ) { pPreviousTrack = pF; }
3612 void ScFormulaCell::SetNextTrack( ScFormulaCell* pF ) { pNextTrack = pF; }
3614 ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant )
3616 if (mxGroup)
3618 // You can't create a new group if the cell is already a part of a group.
3619 // Is this a sign of some inconsistent or incorrect data structures? Or normal?
3620 SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
3621 return ScFormulaCellGroupRef();
3624 mxGroup.reset(new ScFormulaCellGroup);
3625 mxGroup->mpTopCell = this;
3626 mxGroup->mbInvariant = bInvariant;
3627 mxGroup->mnLength = nLen;
3628 mxGroup->mpCode = pCode; // Move this to the shared location.
3629 #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION
3630 if (mxGroup->sxCompilationThread.is())
3631 mxGroup->scheduleCompilation();
3632 #endif
3633 return mxGroup;
3636 void ScFormulaCell::SetCellGroup( const ScFormulaCellGroupRef &xRef )
3638 if (!xRef)
3640 // Make this cell a non-grouped cell.
3641 if (mxGroup)
3642 pCode = mxGroup->mpCode->Clone();
3644 mxGroup = xRef;
3645 return;
3648 // Group object has shared token array.
3649 if (!mxGroup)
3650 // Currently not shared. Delete the existing token array first.
3651 delete pCode;
3653 mxGroup = xRef;
3654 pCode = mxGroup->mpCode;
3657 ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( ScFormulaCell& rOther ) const
3659 // no Matrix formulae yet.
3660 if ( GetMatrixFlag() != MM_NONE )
3661 return NotEqual;
3663 // are these formule at all similar ?
3664 if ( GetHash() != rOther.GetHash() )
3665 return NotEqual;
3667 FormulaToken **pThis = pCode->GetCode();
3668 sal_uInt16 nThisLen = pCode->GetCodeLen();
3669 FormulaToken **pOther = rOther.pCode->GetCode();
3670 sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
3672 if ( !pThis || !pOther )
3674 // Error: no compiled code for cells !"
3675 return NotEqual;
3678 if ( nThisLen != nOtherLen )
3679 return NotEqual;
3681 bool bInvariant = true;
3683 // check we are basically the same function
3684 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
3686 formula::FormulaToken *pThisTok = pThis[i];
3687 formula::FormulaToken *pOtherTok = pOther[i];
3689 if ( pThisTok->GetType() != pOtherTok->GetType() ||
3690 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
3691 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
3693 // Incompatible type, op-code or param counts.
3694 return NotEqual;
3697 switch (pThisTok->GetType())
3699 case formula::svMatrix:
3700 case formula::svExternalSingleRef:
3701 case formula::svExternalDoubleRef:
3702 // Ignoring matrix and external references for now.
3703 return NotEqual;
3705 case formula::svSingleRef:
3707 // Single cell reference.
3708 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
3709 if (rRef != *pOtherTok->GetSingleRef())
3710 return NotEqual;
3712 if (rRef.IsRowRel())
3713 bInvariant = false;
3715 break;
3716 case formula::svDoubleRef:
3718 // Range reference.
3719 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
3720 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
3721 if (rRef1 != *pOtherTok->GetSingleRef())
3722 return NotEqual;
3724 if (rRef2 != *pOtherTok->GetSingleRef2())
3725 return NotEqual;
3727 if (rRef1.IsRowRel())
3728 bInvariant = false;
3730 if (rRef2.IsRowRel())
3731 bInvariant = false;
3733 break;
3734 case formula::svDouble:
3736 if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
3737 return NotEqual;
3739 break;
3740 case formula::svString:
3742 if(pThisTok->GetString() != pOtherTok->GetString())
3743 return NotEqual;
3745 break;
3746 case formula::svIndex:
3748 if(pThisTok->GetIndex() != pOtherTok->GetIndex())
3749 return NotEqual;
3751 break;
3752 case formula::svByte:
3754 if(pThisTok->GetByte() != pOtherTok->GetByte())
3755 return NotEqual;
3757 break;
3758 case formula::svExternal:
3760 if (pThisTok->GetExternal() != pOtherTok->GetExternal())
3761 return NotEqual;
3763 if (pThisTok->GetByte() != pOtherTok->GetByte())
3764 return NotEqual;
3766 break;
3767 default:
3772 return bInvariant ? EqualInvariant : EqualRelativeRef;
3775 namespace {
3777 // Split N into optimally equal-sized pieces, each not larger than K.
3778 // Return value P is number of pieces. A returns the number of pieces
3779 // one larger than N/P, 0..P-1.
3781 int splitup(int N, int K, int& A)
3783 assert(N > 0);
3784 assert(K > 0);
3786 A = 0;
3788 if (N <= K)
3789 return 1;
3791 const int ideal_num_parts = N / K;
3792 if (ideal_num_parts * K == N)
3793 return ideal_num_parts;
3795 const int num_parts = ideal_num_parts + 1;
3796 const int nominal_part_size = N / num_parts;
3798 A = N - num_parts * nominal_part_size;
3800 return num_parts;
3803 } // anonymous namespace
3805 bool ScFormulaCell::InterpretFormulaGroup()
3807 if (!mxGroup || !pCode)
3808 return false;
3810 if (mxGroup->meCalcState == sc::GroupCalcDisabled)
3811 return false;
3813 if (GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize)
3815 mxGroup->meCalcState = sc::GroupCalcDisabled;
3816 return false;
3819 switch (pCode->GetVectorState())
3821 case FormulaVectorEnabled:
3822 case FormulaVectorCheckReference:
3823 // Good.
3824 break;
3825 case FormulaVectorDisabled:
3826 case FormulaVectorUnknown:
3827 default:
3828 // Not good.
3829 return false;
3832 if (!officecfg::Office::Common::Misc::UseOpenCL::get())
3833 return false;
3835 // TODO : Disable invariant formula group interpretation for now in order
3836 // to get implicit intersection to work.
3837 if (mxGroup->mbInvariant && false)
3838 return InterpretInvariantFormulaGroup();
3840 int nMaxGroupLength = INT_MAX;
3842 #ifdef WNT
3843 // Heuristic: Certain old low-end OpenCL implementations don't
3844 // work for us with too large group lengths. 1000 was determined
3845 // empirically to be a good compromise. Looking at the preferred
3846 // float vector width seems to be a way to detect these devices.
3847 if (opencl::gpuEnv.mnPreferredVectorWidthFloat == 4)
3848 nMaxGroupLength = 1000;
3849 #endif
3851 if (std::getenv("SC_MAX_GROUP_LENGTH"))
3852 nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
3854 int nNumOnePlus;
3855 const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
3857 int nOffset = 0;
3858 int nCurChunkSize;
3859 ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
3860 for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
3862 nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
3864 ScFormulaCellGroupRef xGroup;
3866 if (nNumParts == 1)
3867 xGroup = mxGroup;
3868 else
3870 // Ugly hack
3871 xGroup = new ScFormulaCellGroup();
3872 xGroup->mpTopCell = mxGroup->mpTopCell;
3873 xGroup->mpTopCell->aPos = aOrigPos;
3874 xGroup->mpTopCell->aPos.IncRow(nOffset);
3875 xGroup->mbInvariant = mxGroup->mbInvariant;
3876 xGroup->mnLength = nCurChunkSize;
3877 xGroup->mpCode = mxGroup->mpCode;
3880 ScTokenArray aCode;
3881 ScGroupTokenConverter aConverter(aCode, *pDocument, *this, xGroup->mpTopCell->aPos);
3882 std::vector<ScTokenArray*> aLoopControl;
3883 if (!aConverter.convert(*pCode, aLoopControl))
3885 SAL_INFO("sc.opencl", "conversion of group " << this << " failed, disabling");
3886 mxGroup->meCalcState = sc::GroupCalcDisabled;
3888 // Undo the hack above
3889 if (nNumParts > 1)
3891 mxGroup->mpTopCell->aPos = aOrigPos;
3892 xGroup->mpTopCell = NULL;
3893 xGroup->mpCode = NULL;
3896 return false;
3899 // The converted code does not have RPN tokens yet. The interpreter will
3900 // generate them.
3901 xGroup->meCalcState = mxGroup->meCalcState = sc::GroupCalcRunning;
3902 sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic();
3903 if (pInterpreter == NULL ||
3904 !pInterpreter->interpret(*pDocument, xGroup->mpTopCell->aPos, xGroup, aCode))
3906 SAL_INFO("sc.opencl", "interpreting group " << mxGroup << " (state " << (int) mxGroup->meCalcState << ") failed, disabling");
3907 mxGroup->meCalcState = sc::GroupCalcDisabled;
3909 // Undo the hack above
3910 if (nNumParts > 1)
3912 mxGroup->mpTopCell->aPos = aOrigPos;
3913 xGroup->mpTopCell = NULL;
3914 xGroup->mpCode = NULL;
3917 return false;
3919 if (nNumParts > 1)
3921 xGroup->mpTopCell = NULL;
3922 xGroup->mpCode = NULL;
3926 if (nNumParts > 1)
3927 mxGroup->mpTopCell->aPos = aOrigPos;
3928 mxGroup->meCalcState = sc::GroupCalcEnabled;
3929 return true;
3932 bool ScFormulaCell::InterpretInvariantFormulaGroup()
3934 if (pCode->GetVectorState() == FormulaVectorCheckReference)
3936 // An invariant group should only have absolute row references, and no
3937 // external references are allowed.
3939 ScTokenArray aCode;
3940 pCode->Reset();
3941 for (const formula::FormulaToken* p = pCode->First(); p; p = pCode->Next())
3943 switch (p->GetType())
3945 case svSingleRef:
3947 ScSingleRefData aRef = *p->GetSingleRef();
3948 ScAddress aRefPos = aRef.toAbs(aPos);
3949 formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefPos);
3950 if (!pNewToken)
3951 return false;
3953 aCode.AddToken(*pNewToken);
3955 break;
3956 case svDoubleRef:
3958 ScComplexRefData aRef = *p->GetDoubleRef();
3959 ScRange aRefRange = aRef.toAbs(aPos);
3960 formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefRange);
3961 if (!pNewToken)
3962 return false;
3964 aCode.AddToken(*pNewToken);
3966 break;
3967 default:
3968 aCode.AddToken(*p);
3972 ScCompiler aComp(pDocument, aPos, aCode);
3973 aComp.SetGrammar(pDocument->GetGrammar());
3974 aComp.CompileTokenArray(); // Create RPN token array.
3975 ScInterpreter aInterpreter(this, pDocument, aPos, aCode);
3976 aInterpreter.Interpret();
3977 aResult.SetToken(aInterpreter.GetResultToken().get());
3979 else
3981 // Formula contains no references.
3982 ScInterpreter aInterpreter(this, pDocument, aPos, *pCode);
3983 aInterpreter.Interpret();
3984 aResult.SetToken(aInterpreter.GetResultToken().get());
3987 for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ )
3989 ScAddress aTmpPos = aPos;
3990 aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i);
3991 ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
3992 if (!pCell)
3994 SAL_WARN("sc", "GetFormulaCell not found");
3995 continue;
3998 // FIXME: this set of horrors is unclear to me ... certainly
3999 // the above GetCell is profoundly nasty & slow ...
4000 // Ensure the cell truly has a result:
4001 pCell->aResult = aResult;
4002 pCell->ResetDirty();
4003 pCell->SetChanged(true);
4006 return true;
4009 namespace {
4011 void startListeningArea(
4012 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
4014 const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
4015 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
4016 ScAddress aCell1 = rRef1.toAbs(rPos);
4017 ScAddress aCell2 = rRef2.toAbs(rPos);
4018 if (aCell1.IsValid() && aCell2.IsValid())
4020 if (rToken.GetOpCode() == ocColRowNameAuto)
4021 { // automagically
4022 if ( rRef1.IsColRel() )
4023 { // ColName
4024 aCell2.SetRow(MAXROW);
4026 else
4027 { // RowName
4028 aCell2.SetCol(MAXCOL);
4031 rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell);
4037 void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
4039 if (mxGroup)
4040 mxGroup->endAllGroupListening(*pDoc);
4042 if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
4043 return;
4045 pDoc->SetDetectiveDirty(true); // It has changed something
4047 ScTokenArray* pArr = GetCode();
4048 if( pArr->IsRecalcModeAlways() )
4050 pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
4053 pArr->Reset();
4054 formula::FormulaToken* t;
4055 while ( ( t = pArr->GetNextReferenceRPN() ) != NULL )
4057 switch (t->GetType())
4059 case svSingleRef:
4061 ScAddress aCell = t->GetSingleRef()->toAbs(aPos);
4062 if (aCell.IsValid())
4063 pDoc->StartListeningCell(aCell, this);
4065 break;
4066 case svDoubleRef:
4067 startListeningArea(this, *pDoc, aPos, *t);
4068 break;
4069 default:
4070 ; // nothing
4073 SetNeedsListening( false);
4076 void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
4078 ScDocument& rDoc = rCxt.getDoc();
4080 if (mxGroup)
4081 mxGroup->endAllGroupListening(rDoc);
4083 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
4084 return;
4086 rDoc.SetDetectiveDirty(true); // It has changed something
4088 ScTokenArray* pArr = GetCode();
4089 if( pArr->IsRecalcModeAlways() )
4091 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
4094 pArr->Reset();
4095 formula::FormulaToken* t;
4096 while ( ( t = pArr->GetNextReferenceRPN() ) != NULL )
4098 switch (t->GetType())
4100 case svSingleRef:
4102 ScAddress aCell = t->GetSingleRef()->toAbs(aPos);
4103 if (aCell.IsValid())
4104 rDoc.StartListeningCell(rCxt, aCell, *this);
4106 break;
4107 case svDoubleRef:
4108 startListeningArea(this, rDoc, aPos, *t);
4109 break;
4110 default:
4111 ; // nothing
4114 SetNeedsListening( false);
4117 namespace {
4119 void endListeningArea(
4120 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
4122 const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
4123 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
4124 ScAddress aCell1 = rRef1.toAbs(rPos);
4125 ScAddress aCell2 = rRef2.toAbs(rPos);
4126 if (aCell1.IsValid() && aCell2.IsValid())
4128 if (rToken.GetOpCode() == ocColRowNameAuto)
4129 { // automagically
4130 if ( rRef1.IsColRel() )
4131 { // ColName
4132 aCell2.SetRow(MAXROW);
4134 else
4135 { // RowName
4136 aCell2.SetCol(MAXCOL);
4140 rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell);
4146 void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
4147 ScAddress aCellPos )
4149 if (mxGroup)
4150 mxGroup->endAllGroupListening(*pDoc);
4152 if (pDoc->IsClipOrUndo() || IsInChangeTrack())
4153 return;
4155 if (!HasBroadcaster())
4156 return;
4158 pDoc->SetDetectiveDirty(true); // It has changed something
4160 if ( GetCode()->IsRecalcModeAlways() )
4162 pDoc->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
4165 if (!pArr)
4167 pArr = GetCode();
4168 aCellPos = aPos;
4170 pArr->Reset();
4171 formula::FormulaToken* t;
4172 while ( ( t = pArr->GetNextReferenceRPN() ) != NULL )
4174 switch (t->GetType())
4176 case svSingleRef:
4178 ScAddress aCell = t->GetSingleRef()->toAbs(aCellPos);
4179 if (aCell.IsValid())
4180 pDoc->EndListeningCell(aCell, this);
4182 break;
4183 case svDoubleRef:
4184 endListeningArea(this, *pDoc, aCellPos, *t);
4185 break;
4186 default:
4187 ; // nothing
4192 void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
4194 if (mxGroup)
4195 mxGroup->endAllGroupListening(rCxt.getDoc());
4197 if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
4198 return;
4200 if (!HasBroadcaster())
4201 return;
4203 ScDocument& rDoc = rCxt.getDoc();
4204 rDoc.SetDetectiveDirty(true); // It has changed something
4206 ScTokenArray* pArr = rCxt.getOldCode();
4207 ScAddress aCellPos = rCxt.getOldPosition(aPos);
4208 if (!pArr)
4209 pArr = pCode;
4211 if (pArr->IsRecalcModeAlways())
4213 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
4216 pArr->Reset();
4217 formula::FormulaToken* t;
4218 while ( ( t = pArr->GetNextReferenceRPN() ) != NULL )
4220 switch (t->GetType())
4222 case svSingleRef:
4224 ScAddress aCell = t->GetSingleRef()->toAbs(aCellPos);
4225 if (aCell.IsValid())
4226 rDoc.EndListeningCell(rCxt, aCell, *this);
4228 break;
4229 case svDoubleRef:
4230 endListeningArea(this, rDoc, aCellPos, *t);
4231 break;
4232 default:
4233 ; // nothing
4238 bool ScFormulaCell::IsShared() const
4240 return mxGroup.get() != NULL;
4243 bool ScFormulaCell::IsSharedTop() const
4245 if (!mxGroup)
4246 return false;
4248 return mxGroup->mpTopCell == this;
4251 SCROW ScFormulaCell::GetSharedTopRow() const
4253 return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1;
4256 SCROW ScFormulaCell::GetSharedLength() const
4258 return mxGroup ? mxGroup->mnLength : 0;
4261 sal_Int32 ScFormulaCell::GetWeight() const
4263 #if 0
4264 if (!mxGroup)
4265 return pCode->GetWeight();
4266 return GetSharedLength() * GetSharedCode()->GetWeight();
4267 #else
4268 return GetSharedLength();
4269 #endif
4272 ScTokenArray* ScFormulaCell::GetSharedCode()
4274 return mxGroup ? mxGroup->mpCode : NULL;
4277 const ScTokenArray* ScFormulaCell::GetSharedCode() const
4279 return mxGroup ? mxGroup->mpCode : NULL;
4282 void ScFormulaCell::SyncSharedCode()
4284 if (!mxGroup)
4285 // Not a shared formula cell.
4286 return;
4288 pCode = mxGroup->mpCode;
4291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */