Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / data / formulacell.cxx
blobcad21446ad05ce3ca4e6a8d0881bff42056a365a
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 "formulacell.hxx"
21 #include "grouptokenconverter.hxx"
23 #include "compiler.hxx"
24 #include "document.hxx"
25 #include "globalnames.hxx"
26 #include "cellvalue.hxx"
27 #include "interpre.hxx"
28 #include "macromgr.hxx"
29 #include "refupdat.hxx"
30 #include "recursionhelper.hxx"
31 #include "docoptio.hxx"
32 #include "rangenam.hxx"
33 #include "dbdata.hxx"
34 #include "progress.hxx"
35 #include "scmatrix.hxx"
36 #include "rechead.hxx"
37 #include "scitems.hxx"
38 #include "validat.hxx"
39 #include "editutil.hxx"
40 #include "chgtrack.hxx"
41 #include "tokenarray.hxx"
42 #include "clkernelthread.hxx"
44 #include "formula/errorcodes.hxx"
45 #include "formula/vectortoken.hxx"
46 #include "svl/intitem.hxx"
47 #include "rtl/strbuf.hxx"
48 #include "formulagroup.hxx"
49 #include "listenercontext.hxx"
50 #include "types.hxx"
51 #include "scopetools.hxx"
52 #include "refupdatecontext.hxx"
54 #include <boost/scoped_ptr.hpp>
56 using namespace formula;
58 #ifdef USE_MEMPOOL
59 IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell )
60 #endif
62 namespace {
64 // More or less arbitrary, of course all recursions must fit into available
65 // stack space (which is what on all systems we don't know yet?). Choosing a
66 // lower value may be better than trying a much higher value that also isn't
67 // sufficient but temporarily leads to high memory consumption. On the other
68 // hand, if the value fits all recursions, execution is quicker as no resumes
69 // are necessary. Could be made a configurable option.
70 // Allow for a year's calendar (366).
71 const sal_uInt16 MAXRECURSION = 400;
73 using std::deque;
75 typedef SCCOLROW(*DimensionSelector)(const ScAddress&, const ScSingleRefData&);
78 static SCCOLROW lcl_GetCol(const ScAddress& rPos, const ScSingleRefData& rData)
80 return rData.toAbs(rPos).Col();
84 static SCCOLROW lcl_GetRow(const ScAddress& rPos, const ScSingleRefData& rData)
86 return rData.toAbs(rPos).Row();
90 static SCCOLROW lcl_GetTab(const ScAddress& rPos, const ScSingleRefData& rData)
92 return rData.toAbs(rPos).Tab();
96 /** Check if both references span the same range in selected dimension.
98 static bool
99 lcl_checkRangeDimension(
100 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
101 const DimensionSelector aWhich)
103 return aWhich(rPos, rRef1.Ref1) == aWhich(rPos, rRef2.Ref1) &&
104 aWhich(rPos, rRef1.Ref2) == aWhich(rPos, rRef2.Ref2);
108 static bool
109 lcl_checkRangeDimensions(
110 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
111 bool& bCol, bool& bRow, bool& bTab)
113 const bool bSameCols(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetCol));
114 const bool bSameRows(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetRow));
115 const bool bSameTabs(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetTab));
117 // Test if exactly two dimensions are equal
118 if (!(bSameCols ^ bSameRows ^ bSameTabs)
119 && (bSameCols || bSameRows || bSameTabs))
121 bCol = !bSameCols;
122 bRow = !bSameRows;
123 bTab = !bSameTabs;
124 return true;
126 return false;
130 /** Check if references in given reference list can possibly
131 form a range. To do that, two of their dimensions must be the same.
133 static bool
134 lcl_checkRangeDimensions(
135 const ScAddress& rPos,
136 const deque<ScToken*>::const_iterator aBegin,
137 const deque<ScToken*>::const_iterator aEnd,
138 bool& bCol, bool& bRow, bool& bTab)
140 deque<ScToken*>::const_iterator aCur(aBegin);
141 ++aCur;
142 const SingleDoubleRefProvider aRef(**aBegin);
143 bool bOk(false);
145 const SingleDoubleRefProvider aRefCur(**aCur);
146 bOk = lcl_checkRangeDimensions(rPos, aRef, aRefCur, bCol, bRow, bTab);
148 while (bOk && aCur != aEnd)
150 const SingleDoubleRefProvider aRefCur(**aCur);
151 bool bColTmp(false);
152 bool bRowTmp(false);
153 bool bTabTmp(false);
154 bOk = lcl_checkRangeDimensions(rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
155 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
156 ++aCur;
159 if (bOk && aCur == aEnd)
161 return true;
163 return false;
166 class LessByReference : std::binary_function<const ScToken*, const ScToken*, bool>
168 ScAddress maPos;
169 DimensionSelector maFunc;
170 public:
171 LessByReference(const ScAddress& rPos, const DimensionSelector& rFunc) :
172 maPos(rPos), maFunc(rFunc) {}
174 bool operator() (const ScToken* pRef1, const ScToken* pRef2)
176 const SingleDoubleRefProvider aRef1(*pRef1);
177 const SingleDoubleRefProvider aRef2(*pRef2);
178 return maFunc(maPos, aRef1.Ref1) < maFunc(maPos, aRef2.Ref1);
183 * Returns true if range denoted by token p2 starts immediately after range
184 * denoted by token p1. Dimension, in which the comparison takes place, is
185 * given by maFunc.
187 class AdjacentByReference : std::binary_function<const ScToken*, const ScToken*, bool>
189 ScAddress maPos;
190 DimensionSelector maFunc;
191 public:
192 AdjacentByReference(const ScAddress& rPos, DimensionSelector aFunc) :
193 maPos(rPos), maFunc(aFunc) {}
195 bool operator() (const ScToken* p1, const ScToken* p2)
197 const SingleDoubleRefProvider aRef1(*p1);
198 const SingleDoubleRefProvider aRef2(*p2);
199 return maFunc(maPos, aRef2.Ref1) - maFunc(maPos, aRef1.Ref2) == 1;
203 static bool
204 lcl_checkIfAdjacent(
205 const ScAddress& rPos, const deque<ScToken*>& rReferences, const DimensionSelector aWhich)
207 typedef deque<ScToken*>::const_iterator Iter;
208 Iter aBegin(rReferences.begin());
209 Iter aEnd(rReferences.end());
210 Iter aBegin1(aBegin);
211 ++aBegin1, --aEnd;
212 return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rPos, aWhich));
216 static void
217 lcl_fillRangeFromRefList(
218 const ScAddress& aPos, const deque<ScToken*>& rReferences, ScRange& rRange)
220 const ScSingleRefData aStart(
221 SingleDoubleRefProvider(*rReferences.front()).Ref1);
222 rRange.aStart = aStart.toAbs(aPos);
223 const ScSingleRefData aEnd(
224 SingleDoubleRefProvider(*rReferences.back()).Ref2);
225 rRange.aEnd = aEnd.toAbs(aPos);
229 static bool
230 lcl_refListFormsOneRange(
231 const ScAddress& rPos, deque<ScToken*>& rReferences,
232 ScRange& rRange)
234 if (rReferences.size() == 1)
236 lcl_fillRangeFromRefList(rPos, rReferences, rRange);
237 return true;
240 bool bCell(false);
241 bool bRow(false);
242 bool bTab(false);
243 if (lcl_checkRangeDimensions(rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
245 DimensionSelector aWhich;
246 if (bCell)
248 aWhich = lcl_GetCol;
250 else if (bRow)
252 aWhich = lcl_GetRow;
254 else if (bTab)
256 aWhich = lcl_GetTab;
258 else
260 OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
261 aWhich = lcl_GetRow; // initialize to avoid warning
264 // Sort the references by start of range
265 std::sort(rReferences.begin(), rReferences.end(), LessByReference(rPos, aWhich));
266 if (lcl_checkIfAdjacent(rPos, rReferences, aWhich))
268 lcl_fillRangeFromRefList(rPos, rReferences, rRange);
269 return true;
272 return false;
276 bool lcl_isReference(const FormulaToken& rToken)
278 return
279 rToken.GetType() == svSingleRef ||
280 rToken.GetType() == svDoubleRef;
283 void adjustRangeName(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc, const ScAddress& aNewPos, const ScAddress& aOldPos)
285 bool bOldGlobal = pToken->IsGlobal();
286 SCTAB aOldTab = aOldPos.Tab();
287 OUString aRangeName;
288 int nOldIndex = pToken->GetIndex();
289 ScRangeData* pOldRangeData = NULL;
291 //search the name of the RangeName
292 if (!bOldGlobal)
294 pOldRangeData = pOldDoc->GetRangeName(aOldTab)->findByIndex(nOldIndex);
295 if (!pOldRangeData)
296 return; //might be an error in the formula array
297 aRangeName = pOldRangeData->GetUpperName();
299 else
301 pOldRangeData = pOldDoc->GetRangeName()->findByIndex(nOldIndex);
302 if (!pOldRangeData)
303 return; //might be an error in the formula array
304 aRangeName = pOldRangeData->GetUpperName();
307 //find corresponding range name in new document
308 //first search for local range name then global range names
309 SCTAB aNewTab = aNewPos.Tab();
310 ScRangeName* pRangeName = rNewDoc.GetRangeName(aNewTab);
311 ScRangeData* pRangeData = NULL;
312 bool bNewGlobal = false;
313 //search local range names
314 if (pRangeName)
316 pRangeData = pRangeName->findByUpperName(aRangeName);
318 //search global range names
319 if (!pRangeData)
321 bNewGlobal = true;
322 pRangeName = rNewDoc.GetRangeName();
323 if (pRangeName)
324 pRangeData = pRangeName->findByUpperName(aRangeName);
326 //if no range name was found copy it
327 if (!pRangeData)
329 bNewGlobal = bOldGlobal;
330 pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc);
331 ScTokenArray* pRangeNameToken = pRangeData->GetCode();
332 if (rNewDoc.GetPool() != const_cast<ScDocument*>(pOldDoc)->GetPool())
334 pRangeNameToken->ReadjustAbsolute3DReferences(pOldDoc, &rNewDoc, pRangeData->GetPos(), true);
335 pRangeNameToken->AdjustAbsoluteRefs(pOldDoc, aOldPos, aNewPos, false, true);
338 bool bInserted;
339 if (bNewGlobal)
340 bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
341 else
342 bInserted = rNewDoc.GetRangeName(aNewTab)->insert(pRangeData);
343 if (!bInserted)
345 //if this happened we have a real problem
346 pRangeData = NULL;
347 pToken->SetIndex(0);
348 OSL_FAIL("inserting the range name should not fail");
349 return;
352 sal_Int32 nIndex = pRangeData->GetIndex();
353 pToken->SetIndex(nIndex);
354 pToken->SetGlobal(bNewGlobal);
357 void adjustDBRange(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
359 ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
360 if (!pOldDBCollection)
361 return;//strange error case, don't do anything
362 ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
363 ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
364 if (!pDBData)
365 return; //invalid index
366 OUString aDBName = pDBData->GetUpperName();
368 //search in new document
369 ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
370 if (!pNewDBCollection)
372 pNewDBCollection = new ScDBCollection(&rNewDoc);
373 rNewDoc.SetDBCollection(pNewDBCollection);
375 ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
376 ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
377 if (!pNewDBData)
379 pNewDBData = new ScDBData(*pDBData);
380 aNewNamedDBs.insert(pNewDBData);
382 pToken->SetIndex(pNewDBData->GetIndex());
387 // The mutex to synchronize access to the OpenCL compilation thread.
388 static osl::Mutex& getOpenCLCompilationThreadMutex()
390 static osl::Mutex* pMutex = NULL;
391 if( !pMutex )
393 osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
394 if( !pMutex )
396 static osl::Mutex aMutex;
397 pMutex = &aMutex;
401 return *pMutex;
404 int ScFormulaCellGroup::snCount = 0;
405 rtl::Reference<sc::CLBuildKernelThread> ScFormulaCellGroup::sxCompilationThread;
407 ScFormulaCellGroup::ScFormulaCellGroup() :
408 mnRefCount(0),
409 mpCode(NULL),
410 mpCompiledFormula(NULL),
411 mpTopCell(NULL),
412 mnLength(0),
413 mnFormatType(NUMBERFORMAT_NUMBER),
414 mbInvariant(false),
415 mbSubTotal(false),
416 meCalcState(sc::GroupCalcEnabled)
418 if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
420 osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
421 if (snCount++ == 0)
423 assert(!sxCompilationThread.is());
424 sxCompilationThread.set(new sc::CLBuildKernelThread);
425 sxCompilationThread->launch();
430 ScFormulaCellGroup::~ScFormulaCellGroup()
432 if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
434 osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
435 if (--snCount == 0 && sxCompilationThread.is())
437 assert(sxCompilationThread.is());
438 sxCompilationThread->finish();
439 sxCompilationThread->join();
440 SAL_INFO("sc.opencl", "OpenCL kernel compilation thread has finished");
441 sxCompilationThread.clear();
444 delete mpCode;
447 void ScFormulaCellGroup::scheduleCompilation()
449 meCalcState = sc::GroupCalcOpenCLKernelCompilationScheduled;
450 sc::CLBuildKernelWorkItem aWorkItem;
451 aWorkItem.meWhatToDo = sc::CLBuildKernelWorkItem::COMPILE;
452 aWorkItem.mxGroup = this;
453 sxCompilationThread->push(aWorkItem);
456 void ScFormulaCellGroup::setCode( const ScTokenArray& rCode )
458 delete mpCode;
459 mpCode = rCode.Clone();
460 mbInvariant = mpCode->IsInvariant();
461 mpCode->GenHash();
464 void ScFormulaCellGroup::compileCode(
465 ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
467 if (!mpCode)
468 return;
470 if (mpCode->GetLen() && !mpCode->GetCodeError() && !mpCode->GetCodeLen())
472 ScCompiler aComp(&rDoc, rPos, *mpCode);
473 aComp.SetGrammar(eGram);
474 mbSubTotal = aComp.CompileTokenArray();
475 mnFormatType = aComp.GetNumFormatType();
477 else
479 mpCode->Reset();
480 mbSubTotal = mpCode->GetNextOpCodeRPN(ocSubTotal) != NULL;
484 // ============================================================================
486 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos ) :
487 eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
488 pCode(new ScTokenArray),
489 pDocument(pDoc),
490 pPrevious(0),
491 pNext(0),
492 pPreviousTrack(0),
493 pNextTrack(0),
494 nSeenInIteration(0),
495 cMatrixFlag(MM_NONE),
496 nFormatType(NUMBERFORMAT_NUMBER),
497 bDirty(false),
498 bChanged(false),
499 bRunning(false),
500 bCompile(false),
501 bSubTotal(false),
502 bIsIterCell(false),
503 bInChangeTrack(false),
504 bTableOpDirty(false),
505 bNeedListening(false),
506 mbNeedsNumberFormat(false),
507 mbPostponedDirty(false),
508 aPos(rPos)
512 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
513 const OUString& rFormula,
514 const FormulaGrammar::Grammar eGrammar,
515 sal_uInt8 cMatInd ) :
516 eTempGrammar( eGrammar),
517 pCode( NULL ),
518 pDocument( pDoc ),
519 pPrevious(0),
520 pNext(0),
521 pPreviousTrack(0),
522 pNextTrack(0),
523 nSeenInIteration(0),
524 cMatrixFlag ( cMatInd ),
525 nFormatType ( NUMBERFORMAT_NUMBER ),
526 bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
527 bChanged( false ),
528 bRunning( false ),
529 bCompile( false ),
530 bSubTotal( false ),
531 bIsIterCell( false ),
532 bInChangeTrack( false ),
533 bTableOpDirty( false ),
534 bNeedListening( false ),
535 mbNeedsNumberFormat( false ),
536 mbPostponedDirty(false),
537 aPos( rPos )
539 Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
540 if (!pCode)
541 // We need to have a non-NULL token array instance at all times.
542 pCode = new ScTokenArray;
545 ScFormulaCell::ScFormulaCell(
546 ScDocument* pDoc, const ScAddress& rPos, ScTokenArray* pArray,
547 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cMatInd ) :
548 eTempGrammar( eGrammar),
549 pCode(pArray),
550 pDocument( pDoc ),
551 pPrevious(0),
552 pNext(0),
553 pPreviousTrack(0),
554 pNextTrack(0),
555 nSeenInIteration(0),
556 cMatrixFlag ( cMatInd ),
557 nFormatType ( NUMBERFORMAT_NUMBER ),
558 bDirty( true ),
559 bChanged( false ),
560 bRunning( false ),
561 bCompile( false ),
562 bSubTotal( false ),
563 bIsIterCell( false ),
564 bInChangeTrack( false ),
565 bTableOpDirty( false ),
566 bNeedListening( false ),
567 mbNeedsNumberFormat( false ),
568 mbPostponedDirty(false),
569 aPos( rPos )
571 assert(pArray); // Never pass a NULL pointer here.
573 // Generate RPN token array.
574 if (pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen())
576 ScCompiler aComp( pDocument, aPos, *pCode);
577 aComp.SetGrammar(eTempGrammar);
578 bSubTotal = aComp.CompileTokenArray();
579 nFormatType = aComp.GetNumFormatType();
581 else
583 pCode->Reset();
584 if (pCode->GetNextOpCodeRPN(ocSubTotal))
585 bSubTotal = true;
588 if (bSubTotal)
589 pDocument->AddSubTotalCell(this);
591 pCode->GenHash();
594 ScFormulaCell::ScFormulaCell(
595 ScDocument* pDoc, const ScAddress& rPos, const ScTokenArray& rArray,
596 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cMatInd ) :
597 eTempGrammar( eGrammar),
598 pCode(new ScTokenArray(rArray)),
599 pDocument( pDoc ),
600 pPrevious(0),
601 pNext(0),
602 pPreviousTrack(0),
603 pNextTrack(0),
604 nSeenInIteration(0),
605 cMatrixFlag ( cMatInd ),
606 nFormatType ( NUMBERFORMAT_NUMBER ),
607 bDirty( true ),
608 bChanged( false ),
609 bRunning( false ),
610 bCompile( false ),
611 bSubTotal( false ),
612 bIsIterCell( false ),
613 bInChangeTrack( false ),
614 bTableOpDirty( false ),
615 bNeedListening( false ),
616 mbNeedsNumberFormat( false ),
617 mbPostponedDirty(false),
618 aPos( rPos )
620 // RPN array generation
621 if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() )
623 ScCompiler aComp( pDocument, aPos, *pCode);
624 aComp.SetGrammar(eTempGrammar);
625 bSubTotal = aComp.CompileTokenArray();
626 nFormatType = aComp.GetNumFormatType();
628 else
630 pCode->Reset();
631 if ( pCode->GetNextOpCodeRPN( ocSubTotal ) )
632 bSubTotal = true;
635 if (bSubTotal)
636 pDocument->AddSubTotalCell(this);
638 pCode->GenHash();
641 ScFormulaCell::ScFormulaCell(
642 ScDocument* pDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
643 const FormulaGrammar::Grammar eGrammar, sal_uInt8 cInd ) :
644 mxGroup(xGroup),
645 eTempGrammar( eGrammar),
646 pCode(xGroup->mpCode ? xGroup->mpCode : new ScTokenArray),
647 pDocument( pDoc ),
648 pPrevious(0),
649 pNext(0),
650 pPreviousTrack(0),
651 pNextTrack(0),
652 nSeenInIteration(0),
653 cMatrixFlag ( cInd ),
654 nFormatType(xGroup->mnFormatType),
655 bDirty(false),
656 bChanged( false ),
657 bRunning( false ),
658 bCompile( false ),
659 bSubTotal(xGroup->mbSubTotal),
660 bIsIterCell( false ),
661 bInChangeTrack( false ),
662 bTableOpDirty( false ),
663 bNeedListening( false ),
664 mbNeedsNumberFormat( false ),
665 mbPostponedDirty(false),
666 aPos( rPos )
668 if (bSubTotal)
669 pDocument->AddSubTotalCell(this);
672 ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) :
673 SvtListener(),
674 aResult( rCell.aResult ),
675 eTempGrammar( rCell.eTempGrammar),
676 pDocument( &rDoc ),
677 pPrevious(0),
678 pNext(0),
679 pPreviousTrack(0),
680 pNextTrack(0),
681 nSeenInIteration(0),
682 cMatrixFlag ( rCell.cMatrixFlag ),
683 nFormatType( rCell.nFormatType ),
684 bDirty( rCell.bDirty ),
685 bChanged( rCell.bChanged ),
686 bRunning( false ),
687 bCompile( rCell.bCompile ),
688 bSubTotal( rCell.bSubTotal ),
689 bIsIterCell( false ),
690 bInChangeTrack( false ),
691 bTableOpDirty( false ),
692 bNeedListening( false ),
693 mbNeedsNumberFormat( false ),
694 mbPostponedDirty(false),
695 aPos( rPos )
697 pCode = rCell.pCode->Clone();
699 // set back any errors and recompile
700 // not in the Clipboard - it must keep the received error flag
701 // Special Length=0: as bad cells are generated, then they are also retained
702 if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() )
704 pCode->SetCodeError( 0 );
705 bCompile = true;
707 //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference
708 bool bCompileLater = false;
709 bool bClipMode = rCell.pDocument->IsClipboard();
711 //update ScNameTokens
712 if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
714 if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
716 ScToken* pToken = NULL;
717 while((pToken = static_cast<ScToken*>(pCode->GetNextName()))!= NULL)
719 OpCode eOpCode = pToken->GetOpCode();
720 if (eOpCode == ocName)
721 adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos);
722 else if (eOpCode == ocDBArea)
723 adjustDBRange(pToken, rDoc, rCell.pDocument);
727 bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
728 if (bCopyBetweenDocs && !(nCloneFlags & SC_CLONECELL_NOMAKEABS_EXTERNAL))
730 pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
733 pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, false, bCopyBetweenDocs );
736 if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL )
737 pCode->ReadjustRelative3DReferences( rCell.aPos, aPos );
739 if( !bCompile )
740 { // Name references with references and ColRowNames
741 pCode->Reset();
742 ScToken* t;
743 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile )
745 if ( t->IsExternalRef() )
747 // External name, cell, and area references.
748 bCompile = true;
750 else if ( t->GetType() == svIndex )
752 ScRangeData* pRangeData = rDoc.GetRangeName()->findByIndex( t->GetIndex() );
753 if( pRangeData )
755 if( pRangeData->HasReferences() )
756 bCompile = true;
758 else
759 bCompile = true; // invalid reference!
761 else if ( t->GetOpCode() == ocColRowName )
763 bCompile = true; // new lookup needed
764 bCompileLater = bClipMode;
768 if( bCompile )
770 if ( !bCompileLater && bClipMode )
772 // Merging ranges needs the actual positions after UpdateReference.
773 // ColRowNames need new lookup after positions are adjusted.
774 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName);
776 if ( !bCompileLater )
778 // bNoListening, not at all if in Clipboard/Undo,
779 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
780 CompileTokenArray( true );
784 if( nCloneFlags & SC_CLONECELL_STARTLISTENING )
785 StartListeningTo( &rDoc );
787 if (bSubTotal)
788 pDocument->AddSubTotalCell(this);
791 ScFormulaCell::~ScFormulaCell()
793 pDocument->RemoveFromFormulaTree( this );
794 pDocument->RemoveSubTotalCell(this);
795 if (pCode->HasOpCode(ocMacro))
796 pDocument->GetMacroManager()->RemoveDependentCell(this);
798 if (pDocument->HasExternalRefManager())
799 pDocument->GetExternalRefManager()->removeRefCell(this);
801 if (!mxGroup || !mxGroup->mpCode)
802 // Formula token is not shared.
803 delete pCode;
806 ScFormulaCell* ScFormulaCell::Clone() const
808 return new ScFormulaCell(*this, *pDocument, aPos);
811 size_t ScFormulaCell::GetHash() const
813 return pCode->GetHash();
816 ScFormulaVectorState ScFormulaCell::GetVectorState() const
818 return pCode->GetVectorState();
821 void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
822 const FormulaGrammar::Grammar eGrammar ) const
824 if( pCode->GetCodeError() && !pCode->GetLen() )
826 rBuffer = OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError()));
827 return;
829 else if( cMatrixFlag == MM_REFERENCE )
831 // Reference to another cell that contains a matrix formula.
832 pCode->Reset();
833 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
834 if( p )
836 /* FIXME: original GetFormula() code obtained
837 * pCell only if (!this->IsInChangeTrack()),
838 * GetEnglishFormula() omitted that test.
839 * Can we live without in all cases? */
840 ScFormulaCell* pCell = NULL;
841 ScSingleRefData& rRef = p->GetSingleRef();
842 ScAddress aAbs = rRef.toAbs(aPos);
843 if (ValidAddress(aAbs))
844 pCell = pDocument->GetFormulaCell(aAbs);
846 if (pCell)
848 pCell->GetFormula( rBuffer, eGrammar);
849 return;
851 else
853 ScCompiler aComp( pDocument, aPos, *pCode);
854 aComp.SetGrammar(eGrammar);
855 aComp.CreateStringFromTokenArray( rBuffer );
858 else
860 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
863 else
865 ScCompiler aComp( pDocument, aPos, *pCode);
866 aComp.SetGrammar(eGrammar);
867 aComp.CreateStringFromTokenArray( rBuffer );
870 rBuffer.insert( 0, '=');
871 if( cMatrixFlag )
873 rBuffer.insert( 0, '{');
874 rBuffer.append( '}');
878 void ScFormulaCell::GetFormula( OUString& rFormula, const FormulaGrammar::Grammar eGrammar ) const
880 OUStringBuffer rBuffer( rFormula );
881 GetFormula( rBuffer, eGrammar );
882 rFormula = rBuffer.makeStringAndClear();
885 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
887 MaybeInterpret();
889 const ScMatrix* pMat = NULL;
890 if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell &&
891 ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0))
892 pMat->GetDimensions( rCols, rRows );
893 else
895 rCols = 0;
896 rRows = 0;
900 bool ScFormulaCell::GetDirty() const { return bDirty; }
901 void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; }
902 bool ScFormulaCell::NeedsListening() const { return bNeedListening; }
903 void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; }
904 void ScFormulaCell::SetNeedNumberFormat( bool bVal ) { mbNeedsNumberFormat = bVal; }
905 short ScFormulaCell::GetFormatType() const { return nFormatType; }
907 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
908 const FormulaGrammar::Grammar eGrammar )
910 if ( pDocument->IsClipOrUndo() )
911 return;
912 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
913 if ( bWasInFormulaTree )
914 pDocument->RemoveFromFormulaTree( this );
915 // pCode may not deleted for queries, but must be empty
916 if ( pCode )
917 pCode->Clear();
918 ScTokenArray* pCodeOld = pCode;
919 ScCompiler aComp( pDocument, aPos);
920 aComp.SetGrammar(eGrammar);
921 pCode = aComp.CompileString( rFormula );
922 if ( pCodeOld )
923 delete pCodeOld;
924 if( !pCode->GetCodeError() )
926 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
927 { // not recursive CompileTokenArray/Compile/CompileTokenArray
928 if ( rFormula[0] == '=' )
929 pCode->AddBad( rFormula.copy(1) );
930 else
931 pCode->AddBad( rFormula );
933 bCompile = true;
934 CompileTokenArray( bNoListening );
936 else
937 bChanged = true;
939 if ( bWasInFormulaTree )
940 pDocument->PutInFormulaTree( this );
944 void ScFormulaCell::CompileTokenArray( bool bNoListening )
946 // Not already compiled?
947 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
949 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
951 else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
953 // RPN length may get changed
954 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
955 if ( bWasInFormulaTree )
956 pDocument->RemoveFromFormulaTree( this );
958 // Loading from within filter? No listening yet!
959 if( pDocument->IsInsertingFromOtherDoc() )
960 bNoListening = true;
962 if( !bNoListening && pCode->GetCodeLen() )
963 EndListeningTo( pDocument );
964 ScCompiler aComp(pDocument, aPos, *pCode);
965 aComp.SetGrammar(pDocument->GetGrammar());
966 bSubTotal = aComp.CompileTokenArray();
967 if( !pCode->GetCodeError() )
969 nFormatType = aComp.GetNumFormatType();
970 bChanged = true;
971 aResult.SetToken( NULL);
972 bCompile = false;
973 if ( !bNoListening )
974 StartListeningTo( pDocument );
976 if ( bWasInFormulaTree )
977 pDocument->PutInFormulaTree( this );
979 if (bSubTotal)
980 pDocument->AddSubTotalCell(this);
985 void ScFormulaCell::CompileXML( ScProgress& rProgress )
987 if ( cMatrixFlag == MM_REFERENCE )
988 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
989 // just establish listeners
990 StartListeningTo( pDocument );
991 return ;
994 // Compilation changes RPN count, remove and reinsert to FormulaTree if it
995 // was in to update its count.
996 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this);
997 if (bWasInFormulaTree)
998 pDocument->RemoveFromFormulaTree( this);
999 ScCompiler aComp( pDocument, aPos, *pCode);
1000 aComp.SetGrammar(eTempGrammar);
1001 OUString aFormula, aFormulaNmsp;
1002 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1003 pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
1004 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
1005 // pCode may not deleted for queries, but must be empty
1006 if ( pCode )
1007 pCode->Clear();
1008 ScTokenArray* pCodeOld = pCode;
1009 pCode = aComp.CompileString( aFormula, aFormulaNmsp );
1010 delete pCodeOld;
1011 if( !pCode->GetCodeError() )
1013 if ( !pCode->GetLen() )
1015 if ( aFormula[0] == '=' )
1016 pCode->AddBad( aFormula.copy( 1 ) );
1017 else
1018 pCode->AddBad( aFormula );
1020 bSubTotal = aComp.CompileTokenArray();
1021 if( !pCode->GetCodeError() )
1023 nFormatType = aComp.GetNumFormatType();
1024 bChanged = true;
1025 bCompile = false;
1026 StartListeningTo( pDocument );
1029 if (bSubTotal)
1030 pDocument->AddSubTotalCell(this);
1032 else
1033 bChanged = true;
1035 // Same as in Load: after loading, it must be known if ocMacro is in any formula
1036 // (for macro warning, CompileXML is called at the end of loading XML file)
1037 if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) )
1038 pDocument->SetHasMacroFunc( true );
1040 //volatile cells must be added here for import
1041 if( pCode->IsRecalcModeAlways() || pCode->IsRecalcModeForced() ||
1042 pCode->IsRecalcModeOnLoad() || pCode->IsRecalcModeOnLoadOnce() )
1044 // During load, only those cells that are marked explicitly dirty get
1045 // recalculated. So we need to set it dirty here.
1046 SetDirtyVar();
1047 pDocument->PutInFormulaTree(this);
1049 else if (bWasInFormulaTree)
1050 pDocument->PutInFormulaTree(this);
1054 void ScFormulaCell::CalcAfterLoad()
1056 bool bNewCompiled = false;
1057 // If a Calc 1.0-doc is read, we have a result, but no token array
1058 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1060 Compile( aResult.GetHybridFormula(), true, eTempGrammar);
1061 aResult.SetToken( NULL);
1062 bDirty = true;
1063 bNewCompiled = true;
1065 // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1066 if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() )
1068 ScCompiler aComp(pDocument, aPos, *pCode);
1069 aComp.SetGrammar(pDocument->GetGrammar());
1070 bSubTotal = aComp.CompileTokenArray();
1071 nFormatType = aComp.GetNumFormatType();
1072 bDirty = true;
1073 bCompile = false;
1074 bNewCompiled = true;
1076 if (bSubTotal)
1077 pDocument->AddSubTotalCell(this);
1080 // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1081 // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1082 // We iron this out here for all systems, such that we also have an Err503 here.
1083 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1085 OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1086 aResult.SetResultError( errIllegalFPOperation );
1087 bDirty = true;
1090 // DoubleRefs for binary operators were always a Matrix before version v5.0.
1091 // Now this is only the case when when in an array formula, otherwise it's an implicit intersection
1092 if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
1093 GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() )
1095 cMatrixFlag = MM_FORMULA;
1096 SetMatColsRows( 1, 1);
1099 // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1100 // the listener and Recalculate (if needed) if not RECALCMODE_NORMAL
1101 if( !bNewCompiled || !pCode->GetCodeError() )
1103 StartListeningTo( pDocument );
1104 if( !pCode->IsRecalcModeNormal() )
1105 bDirty = true;
1107 if ( pCode->IsRecalcModeAlways() )
1108 { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1109 // for each F9
1110 bDirty = true;
1112 // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1116 bool ScFormulaCell::MarkUsedExternalReferences()
1118 return pCode && pDocument->MarkUsedExternalReferences(*pCode, aPos);
1122 void ScFormulaCell::Interpret()
1124 if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn())
1125 return; // no double/triple processing
1127 //! HACK:
1128 // If the call originates from a Reschedule in DdeLink update, leave dirty
1129 // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1130 if ( pDocument->IsInDdeLinkUpdate() )
1131 return;
1133 if (bRunning)
1135 if (!pDocument->GetDocOptions().IsIter())
1137 aResult.SetResultError( errCircularReference );
1138 return;
1141 if (aResult.GetResultError() == errCircularReference)
1142 aResult.SetResultError( 0 );
1144 // Start or add to iteration list.
1145 if (!pDocument->GetRecursionHelper().IsDoingIteration() ||
1146 !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell)
1147 pDocument->GetRecursionHelper().SetInIterationReturn( true);
1149 return;
1151 // no multiple interprets for GetErrCode, IsValue, GetValue and
1152 // different entry point recursions. Would also lead to premature
1153 // convergence in iterations.
1154 if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration ==
1155 pDocument->GetRecursionHelper().GetIteration())
1156 return ;
1158 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
1159 bool bOldRunning = bRunning;
1160 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1162 bRunning = true;
1163 rRecursionHelper.SetInRecursionReturn( true);
1165 else
1167 if ( ! InterpretFormulaGroup() )
1168 InterpretTail( SCITP_NORMAL);
1171 // While leaving a recursion or iteration stack, insert its cells to the
1172 // recursion list in reverse order.
1173 if (rRecursionHelper.IsInReturn())
1175 if (rRecursionHelper.GetRecursionCount() > 0 ||
1176 !rRecursionHelper.IsDoingRecursion())
1177 rRecursionHelper.Insert( this, bOldRunning, aResult);
1178 bool bIterationFromRecursion = false;
1179 bool bResumeIteration = false;
1182 if ((rRecursionHelper.IsInIterationReturn() &&
1183 rRecursionHelper.GetRecursionCount() == 0 &&
1184 !rRecursionHelper.IsDoingIteration()) ||
1185 bIterationFromRecursion || bResumeIteration)
1187 ScFormulaCell* pIterCell = this; // scope for debug convenience
1188 bool & rDone = rRecursionHelper.GetConvergingReference();
1189 rDone = false;
1190 if (!bIterationFromRecursion && bResumeIteration)
1192 bResumeIteration = false;
1193 // Resuming iteration expands the range.
1194 ScFormulaRecursionList::const_iterator aOldStart(
1195 rRecursionHelper.GetLastIterationStart());
1196 rRecursionHelper.ResumeIteration();
1197 // Mark new cells being in iteration.
1198 for (ScFormulaRecursionList::const_iterator aIter(
1199 rRecursionHelper.GetIterationStart()); aIter !=
1200 aOldStart; ++aIter)
1202 pIterCell = (*aIter).pCell;
1203 pIterCell->bIsIterCell = true;
1205 // Mark older cells dirty again, in case they converted
1206 // without accounting for all remaining cells in the circle
1207 // that weren't touched so far, e.g. conditional. Restore
1208 // backuped result.
1209 sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1210 for (ScFormulaRecursionList::const_iterator aIter(
1211 aOldStart); aIter !=
1212 rRecursionHelper.GetIterationEnd(); ++aIter)
1214 pIterCell = (*aIter).pCell;
1215 if (pIterCell->nSeenInIteration == nIteration)
1217 if (!pIterCell->bDirty || aIter == aOldStart)
1219 pIterCell->aResult = (*aIter).aPreviousResult;
1221 --pIterCell->nSeenInIteration;
1223 pIterCell->bDirty = true;
1226 else
1228 bResumeIteration = false;
1229 // Close circle once.
1230 rRecursionHelper.GetList().back().pCell->InterpretTail(
1231 SCITP_CLOSE_ITERATION_CIRCLE);
1232 // Start at 1, init things.
1233 rRecursionHelper.StartIteration();
1234 // Mark all cells being in iteration.
1235 for (ScFormulaRecursionList::const_iterator aIter(
1236 rRecursionHelper.GetIterationStart()); aIter !=
1237 rRecursionHelper.GetIterationEnd(); ++aIter)
1239 pIterCell = (*aIter).pCell;
1240 pIterCell->bIsIterCell = true;
1243 bIterationFromRecursion = false;
1244 sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
1245 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1246 rRecursionHelper.IncIteration())
1248 rDone = true;
1249 for ( ScFormulaRecursionList::iterator aIter(
1250 rRecursionHelper.GetIterationStart()); aIter !=
1251 rRecursionHelper.GetIterationEnd() &&
1252 !rRecursionHelper.IsInReturn(); ++aIter)
1254 pIterCell = (*aIter).pCell;
1255 if (pIterCell->IsDirtyOrInTableOpDirty() &&
1256 rRecursionHelper.GetIteration() !=
1257 pIterCell->GetSeenInIteration())
1259 (*aIter).aPreviousResult = pIterCell->aResult;
1260 pIterCell->InterpretTail( SCITP_FROM_ITERATION);
1262 rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty();
1264 if (rRecursionHelper.IsInReturn())
1266 bResumeIteration = true;
1267 break; // for
1268 // Don't increment iteration.
1271 if (!bResumeIteration)
1273 if (rDone)
1275 for (ScFormulaRecursionList::const_iterator aIter(
1276 rRecursionHelper.GetIterationStart());
1277 aIter != rRecursionHelper.GetIterationEnd();
1278 ++aIter)
1280 pIterCell = (*aIter).pCell;
1281 pIterCell->bIsIterCell = false;
1282 pIterCell->nSeenInIteration = 0;
1283 pIterCell->bRunning = (*aIter).bOldRunning;
1286 else
1288 for (ScFormulaRecursionList::const_iterator aIter(
1289 rRecursionHelper.GetIterationStart());
1290 aIter != rRecursionHelper.GetIterationEnd();
1291 ++aIter)
1293 pIterCell = (*aIter).pCell;
1294 pIterCell->bIsIterCell = false;
1295 pIterCell->nSeenInIteration = 0;
1296 pIterCell->bRunning = (*aIter).bOldRunning;
1297 // If one cell didn't converge, all cells of this
1298 // circular dependency don't, no matter whether
1299 // single cells did.
1300 pIterCell->ResetDirty();
1301 pIterCell->aResult.SetResultError( errNoConvergence);
1302 pIterCell->bChanged = true;
1305 // End this iteration and remove entries.
1306 rRecursionHelper.EndIteration();
1307 bResumeIteration = rRecursionHelper.IsDoingIteration();
1310 if (rRecursionHelper.IsInRecursionReturn() &&
1311 rRecursionHelper.GetRecursionCount() == 0 &&
1312 !rRecursionHelper.IsDoingRecursion())
1314 bIterationFromRecursion = false;
1315 // Iterate over cells known so far, start with the last cell
1316 // encountered, inserting new cells if another recursion limit
1317 // is reached. Repeat until solved.
1318 rRecursionHelper.SetDoingRecursion( true);
1321 rRecursionHelper.SetInRecursionReturn( false);
1322 for (ScFormulaRecursionList::const_iterator aIter(
1323 rRecursionHelper.GetIterationStart());
1324 !rRecursionHelper.IsInReturn() && aIter !=
1325 rRecursionHelper.GetIterationEnd(); ++aIter)
1327 ScFormulaCell* pCell = (*aIter).pCell;
1328 if (pCell->IsDirtyOrInTableOpDirty())
1330 pCell->InterpretTail( SCITP_NORMAL);
1331 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1332 pCell->bRunning = (*aIter).bOldRunning;
1335 } while (rRecursionHelper.IsInRecursionReturn());
1336 rRecursionHelper.SetDoingRecursion( false);
1337 if (rRecursionHelper.IsInIterationReturn())
1339 if (!bResumeIteration)
1340 bIterationFromRecursion = true;
1342 else if (bResumeIteration ||
1343 rRecursionHelper.IsDoingIteration())
1344 rRecursionHelper.GetList().erase(
1345 rRecursionHelper.GetIterationStart(),
1346 rRecursionHelper.GetLastIterationStart());
1347 else
1348 rRecursionHelper.Clear();
1350 } while (bIterationFromRecursion || bResumeIteration);
1354 bool ScFormulaCell::IsIterCell() const { return bIsIterCell; }
1355 sal_uInt16 ScFormulaCell::GetSeenInIteration() const { return nSeenInIteration; }
1357 void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
1359 class RecursionCounter
1361 ScRecursionHelper& rRec;
1362 bool bStackedInIteration;
1363 public:
1364 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r)
1366 bStackedInIteration = rRec.IsDoingIteration();
1367 if (bStackedInIteration)
1368 rRec.GetRecursionInIterationStack().push( p);
1369 rRec.IncRecursionCount();
1371 ~RecursionCounter()
1373 rRec.DecRecursionCount();
1374 if (bStackedInIteration)
1375 rRec.GetRecursionInIterationStack().pop();
1377 } aRecursionCounter( pDocument->GetRecursionHelper(), this);
1378 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
1379 if( !pCode->GetCodeLen() && !pCode->GetCodeError() )
1381 // #i11719# no RPN and no error and no token code but result string present
1382 // => interpretation of this cell during name-compilation and unknown names
1383 // => can't exchange underlying code array in CompileTokenArray() /
1384 // Compile() because interpreter's token iterator would crash or pCode
1385 // would be deleted twice if this cell was interpreted during
1386 // compilation.
1387 // This should only be a temporary condition and, since we set an
1388 // error, if ran into it again we'd bump into the dirty-clearing
1389 // condition further down.
1390 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1392 pCode->SetCodeError( errNoCode );
1393 // This is worth an assertion; if encountered in daily work
1394 // documents we might need another solution. Or just confirm correctness.
1395 OSL_FAIL( "ScFormulaCell::Interpret: no RPN, no error, no token, but hybrid formula string" );
1396 return;
1398 CompileTokenArray();
1401 if( pCode->GetCodeLen() && pDocument )
1403 class StackCleaner
1405 ScDocument* pDoc;
1406 ScInterpreter* pInt;
1407 public:
1408 StackCleaner( ScDocument* pD, ScInterpreter* pI )
1409 : pDoc(pD), pInt(pI)
1411 ~StackCleaner()
1413 delete pInt;
1414 pDoc->DecInterpretLevel();
1417 pDocument->IncInterpretLevel();
1418 ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode );
1419 StackCleaner aStackCleaner( pDocument, p);
1420 sal_uInt16 nOldErrCode = aResult.GetResultError();
1421 if ( nSeenInIteration == 0 )
1422 { // Only the first time
1423 // With bChanged=false, if a newly compiled cell has a result of
1424 // 0.0, no change is detected and the cell will not be repainted.
1425 // bChanged = false;
1426 aResult.SetResultError( 0 );
1429 switch ( aResult.GetResultError() )
1431 case errCircularReference : // will be determined again if so
1432 aResult.SetResultError( 0 );
1433 break;
1436 bool bOldRunning = bRunning;
1437 bRunning = true;
1438 p->Interpret();
1439 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
1441 if (nSeenInIteration > 0)
1442 --nSeenInIteration; // retry when iteration is resumed
1443 return;
1445 bRunning = bOldRunning;
1447 // #i102616# For single-sheet saving consider only content changes, not format type,
1448 // because format type isn't set on loading (might be changed later)
1449 bool bContentChanged = false;
1451 // Do not create a HyperLink() cell if the formula results in an error.
1452 if( p->GetError() && pCode->IsHyperLink())
1453 pCode->SetHyperLink(false);
1455 if( p->GetError() && p->GetError() != errCircularReference)
1457 ResetDirty();
1458 bChanged = true;
1460 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1462 bool bIsValue = aResult.IsValue(); // the previous type
1463 // Did it converge?
1464 if ((bIsValue && p->GetResultType() == svDouble && fabs(
1465 p->GetNumResult() - aResult.GetDouble()) <=
1466 pDocument->GetDocOptions().GetIterEps()) ||
1467 (!bIsValue && p->GetResultType() == svString &&
1468 p->GetStringResult() == aResult.GetString()))
1470 // A convergence in the first iteration doesn't necessarily
1471 // mean that it's done, it may be as not all related cells
1472 // of a circle changed their values yet. If the set really
1473 // converges it will do so also during the next iteration. This
1474 // fixes situations like of #i44115#. If this wasn't wanted an
1475 // initial "uncalculated" value would be needed for all cells
1476 // of a circular dependency => graph needed before calculation.
1477 if (nSeenInIteration > 1 ||
1478 pDocument->GetDocOptions().GetIterCount() == 1)
1480 ResetDirty();
1485 // New error code?
1486 if( p->GetError() != nOldErrCode )
1488 bChanged = true;
1489 // bContentChanged only has to be set if the file content would be changed
1490 if ( aResult.GetCellResultType() != svUnknown )
1491 bContentChanged = true;
1494 if( mbNeedsNumberFormat )
1496 nFormatType = p->GetRetFormatType();
1497 sal_Int32 nFormatIndex = p->GetRetFormatIndex();
1499 // don't set text format as hard format
1500 if(nFormatType == NUMBERFORMAT_TEXT)
1501 nFormatIndex = 0;
1502 else if((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1503 nFormatIndex = ScGlobal::GetStandardFormat(*pDocument->GetFormatTable(),
1504 nFormatIndex, nFormatType);
1506 // set number format explicitly
1507 pDocument->SetNumberFormat( aPos, nFormatIndex );
1509 bChanged = true;
1510 mbNeedsNumberFormat = false;
1513 // In case of changes just obtain the result, no temporary and
1514 // comparison needed anymore.
1515 if (bChanged)
1517 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
1518 // Also handle special cases of initial results after loading.
1519 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1521 ScFormulaResult aNewResult( p->GetResultToken().get());
1522 StackVar eOld = aResult.GetCellResultType();
1523 StackVar eNew = aNewResult.GetCellResultType();
1524 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
1526 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
1527 // -> no change
1529 else
1531 if ( eOld == svHybridCell || eOld == svHybridValueCell ) // string result from SetFormulaResultString?
1532 eOld = svString; // ScHybridCellToken has a valid GetString method
1534 // #i106045# use approxEqual to compare with stored value
1535 bContentChanged = (eOld != eNew ||
1536 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
1537 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1541 aResult.SetToken( p->GetResultToken().get() );
1543 else
1545 ScFormulaResult aNewResult( p->GetResultToken().get());
1546 StackVar eOld = aResult.GetCellResultType();
1547 StackVar eNew = aNewResult.GetCellResultType();
1548 bChanged = (eOld != eNew ||
1549 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
1550 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
1552 // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged)
1553 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
1555 if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) ||
1556 ( (eOld == svHybridCell || eOld == svHybridValueCell) && eNew == svString && aResult.GetString() == aNewResult.GetString() ) ||
1557 ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) )
1559 // no change, see above
1561 else
1562 bContentChanged = true;
1565 aResult.Assign( aNewResult);
1568 // Precision as shown?
1569 if ( aResult.IsValue() && !p->GetError()
1570 && pDocument->GetDocOptions().IsCalcAsShown()
1571 && nFormatType != NUMBERFORMAT_DATE
1572 && nFormatType != NUMBERFORMAT_TIME
1573 && nFormatType != NUMBERFORMAT_DATETIME )
1575 sal_uLong nFormat = pDocument->GetNumberFormat( aPos );
1576 aResult.SetDouble( pDocument->RoundValueAsShown(
1577 aResult.GetDouble(), nFormat));
1579 if (eTailParam == SCITP_NORMAL)
1581 ResetDirty();
1583 if( aResult.GetMatrix() )
1585 // If the formula wasn't entered as a matrix formula, live on with
1586 // the upper left corner and let reference counting delete the matrix.
1587 if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() )
1588 aResult.SetToken( aResult.GetCellResultToken().get());
1590 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1592 // Coded double error may occur via filter import.
1593 sal_uInt16 nErr = GetDoubleErrorValue( aResult.GetDouble());
1594 aResult.SetResultError( nErr);
1595 bChanged = bContentChanged = true;
1598 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
1600 // pass bIgnoreLock=true, because even if called from pending row height update,
1601 // a changed result must still reset the stream flag
1602 pDocument->SetStreamValid(aPos.Tab(), false, true);
1604 if ( !pCode->IsRecalcModeAlways() )
1605 pDocument->RemoveFromFormulaTree( this );
1607 // FORCED cells also immediately tested for validity (start macro possibly)
1609 if ( pCode->IsRecalcModeForced() )
1611 sal_uLong nValidation = ((const SfxUInt32Item*) pDocument->GetAttr(
1612 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue();
1613 if ( nValidation )
1615 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
1616 ScRefCellValue aTmpCell(this);
1617 if ( pData && !pData->IsDataValid(aTmpCell, aPos))
1618 pData->DoCalcError( this );
1622 // Reschedule slows the whole thing down considerably, thus only execute on percent change
1623 ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent(
1624 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
1626 switch (p->GetVolatileType())
1628 case ScInterpreter::VOLATILE:
1629 // Volatile via built-in volatile functions. No actions needed.
1630 break;
1631 case ScInterpreter::VOLATILE_MACRO:
1632 // The formula contains a volatile macro.
1633 pCode->SetExclusiveRecalcModeAlways();
1634 pDocument->PutInFormulaTree(this);
1635 StartListeningTo(pDocument);
1636 break;
1637 case ScInterpreter::NOT_VOLATILE:
1638 if (pCode->IsRecalcModeAlways())
1640 // The formula was previously volatile, but no more.
1641 EndListeningTo(pDocument);
1642 pCode->SetExclusiveRecalcModeNormal();
1644 else
1646 // non-volatile formula. End listening to the area in case
1647 // it's listening due to macro module change.
1648 pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
1650 pDocument->RemoveFromFormulaTree(this);
1651 break;
1652 default:
1656 else
1658 // Cells with compiler errors should not be marked dirty forever
1659 OSL_ENSURE( pCode->GetCodeError(), "no RPN code und no errors ?!?!" );
1660 ResetDirty();
1664 void ScFormulaCell::SetCompile( bool bVal )
1666 bCompile = bVal;
1669 ScDocument* ScFormulaCell::GetDocument() const
1671 return pDocument;
1674 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows, bool bDirtyFlag )
1676 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
1677 if (pMat)
1678 pMat->SetMatColsRows( nCols, nRows );
1679 else if (nCols || nRows)
1681 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
1682 // Setting the new token actually forces an empty result at this top
1683 // left cell, so have that recalculated.
1684 SetDirty( bDirtyFlag );
1689 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
1691 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
1692 if (pMat)
1693 pMat->GetMatColsRows( nCols, nRows);
1694 else
1696 nCols = 0;
1697 nRows = 0;
1701 void ScFormulaCell::SetInChangeTrack( bool bVal )
1703 bInChangeTrack = bVal;
1706 bool ScFormulaCell::IsInChangeTrack() const
1708 return bInChangeTrack;
1711 void ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint)
1713 if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() )
1715 const ScHint* p = PTR_CAST( ScHint, &rHint );
1716 sal_uLong nHint = (p ? p->GetId() : 0);
1717 if (nHint & (SC_HINT_DATACHANGED | SC_HINT_TABLEOPDIRTY))
1719 bool bForceTrack = false;
1720 if ( nHint & SC_HINT_TABLEOPDIRTY )
1722 bForceTrack = !bTableOpDirty;
1723 if ( !bTableOpDirty )
1725 pDocument->AddTableOpFormulaCell( this );
1726 bTableOpDirty = true;
1729 else
1731 bForceTrack = !bDirty;
1732 SetDirtyVar();
1734 // Don't remove from FormulaTree to put in FormulaTrack to
1735 // put in FormulaTree again and again, only if necessary.
1736 // Any other means except RECALCMODE_ALWAYS by which a cell could
1737 // be in FormulaTree if it would notify other cells through
1738 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
1739 // Yes. The new TableOpDirty made it necessary to have a
1740 // forced mode where formulas may still be in FormulaTree from
1741 // TableOpDirty but have to notify dependents for normal dirty.
1742 if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
1743 || pCode->IsRecalcModeAlways())
1744 && !pDocument->IsInFormulaTrack( this ) )
1745 pDocument->AppendToFormulaTrack( this );
1750 void ScFormulaCell::SetDirty( bool bDirtyFlag )
1752 if ( !IsInChangeTrack() )
1754 if ( pDocument->GetHardRecalcState() )
1755 SetDirtyVar();
1756 else
1758 // Multiple Formulas avoid tracking in Load and Copy compileAll
1759 // by Scenario and Copy Block From Clip.
1760 // If unconditional required Formula tracking is set before SetDirty
1761 // bDirty = false, eg in CompileTokenArray
1762 if ( !bDirty || mbPostponedDirty || !pDocument->IsInFormulaTree( this ) )
1764 if( bDirtyFlag )
1765 SetDirtyVar();
1766 pDocument->AppendToFormulaTrack( this );
1767 pDocument->TrackFormulas();
1771 if (pDocument->IsStreamValid(aPos.Tab()))
1772 pDocument->SetStreamValid(aPos.Tab(), false);
1776 void ScFormulaCell::SetDirtyVar()
1778 bDirty = true;
1779 mbPostponedDirty = false;
1780 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
1781 mxGroup->meCalcState = sc::GroupCalcEnabled;
1783 // mark the sheet of this cell to be calculated
1784 //#FIXME do we need to revert this remnant of old fake vba events? pDocument->AddCalculateTable( aPos.Tab() );
1787 void ScFormulaCell::SetDirtyAfterLoad()
1789 bDirty = true;
1790 if ( !pDocument->GetHardRecalcState() )
1791 pDocument->PutInFormulaTree( this );
1794 void ScFormulaCell::ResetTableOpDirtyVar()
1796 bTableOpDirty = false;
1799 void ScFormulaCell::SetTableOpDirty()
1801 if ( !IsInChangeTrack() )
1803 if ( pDocument->GetHardRecalcState() )
1804 bTableOpDirty = true;
1805 else
1807 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
1809 if ( !bTableOpDirty )
1811 pDocument->AddTableOpFormulaCell( this );
1812 bTableOpDirty = true;
1814 pDocument->AppendToFormulaTrack( this );
1815 pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY );
1822 bool ScFormulaCell::IsDirtyOrInTableOpDirty() const
1824 return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp());
1827 void ScFormulaCell::SetResultDouble( double n )
1829 aResult.SetDouble( n);
1832 void ScFormulaCell::SetResultToken( const formula::FormulaToken* pToken )
1834 aResult.SetToken(pToken);
1837 svl::SharedString ScFormulaCell::GetResultString() const
1839 return aResult.GetString();
1842 void ScFormulaCell::SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, formula::FormulaToken* pUL )
1844 aResult.SetMatrix(nCols, nRows, pMat, pUL);
1847 void ScFormulaCell::SetErrCode( sal_uInt16 n )
1849 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
1850 * used whether it is solely for transport of a simple result error and get
1851 * rid of that abuse. */
1852 pCode->SetCodeError( n );
1853 // Hard set errors are transported as result type value per convention,
1854 // e.g. via clipboard. ScFormulaResult::IsValue() and
1855 // ScFormulaResult::GetDouble() handle that.
1856 aResult.SetResultError( n );
1859 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
1861 if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL )
1862 SetDirtyVar();
1863 if ( nBits & RECALCMODE_ONLOAD_ONCE )
1864 { // OnLoadOnce nur zum Dirty setzen nach Filter-Import
1865 nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL;
1867 pCode->AddRecalcMode( nBits );
1870 void ScFormulaCell::SetHybridDouble( double n )
1872 aResult.SetHybridDouble( n);
1875 void ScFormulaCell::SetHybridString( const OUString& r )
1877 aResult.SetHybridString( r);
1880 void ScFormulaCell::SetHybridFormula( const OUString& r,
1881 const formula::FormulaGrammar::Grammar eGrammar )
1883 aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
1886 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
1887 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
1889 OUString aCellString;
1891 Color* pColor;
1893 // Cell Text uses the Cell format while the URL uses
1894 // the default format for the type.
1895 sal_uLong nCellFormat = pDocument->GetNumberFormat( aPos );
1896 SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
1898 sal_uLong nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, NUMBERFORMAT_NUMBER);
1900 if ( IsValue() )
1902 double fValue = GetValue();
1903 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
1905 else
1907 aCellString = GetString().getString();
1908 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
1910 ScConstMatrixRef xMat( aResult.GetMatrix());
1911 if (xMat)
1913 // determine if the matrix result is a string or value.
1914 if (!xMat->IsValue(0, 1))
1915 rURL = xMat->GetString(0, 1).getString();
1916 else
1917 pFormatter->GetOutputString(
1918 xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
1921 if(rURL.isEmpty())
1923 if(IsValue())
1924 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
1925 else
1926 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
1930 bool ScFormulaCell::IsMultilineResult()
1932 if (!IsValue())
1933 return aResult.IsMultiline();
1934 return false;
1937 void ScFormulaCell::MaybeInterpret()
1939 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcOpenCLKernelCompilationScheduled)
1940 return;
1942 if (!IsDirtyOrInTableOpDirty())
1943 return;
1945 if (pDocument->GetAutoCalc() || (cMatrixFlag != MM_NONE))
1946 Interpret();
1949 bool ScFormulaCell::IsHyperLinkCell() const
1951 return pCode && pCode->IsHyperLink();
1954 EditTextObject* ScFormulaCell::CreateURLObject()
1956 OUString aCellText;
1957 OUString aURL;
1958 GetURLResult( aURL, aCellText );
1960 return ScEditUtil::CreateURLObjectFromURL( *pDocument, aURL, aCellText );
1963 bool ScFormulaCell::IsEmpty()
1965 MaybeInterpret();
1966 return aResult.GetCellResultType() == formula::svEmptyCell;
1969 bool ScFormulaCell::IsEmptyDisplayedAsString()
1971 MaybeInterpret();
1972 return aResult.IsEmptyDisplayedAsString();
1975 bool ScFormulaCell::IsValue()
1977 MaybeInterpret();
1978 return aResult.IsValue();
1981 bool ScFormulaCell::IsValueNoError()
1983 MaybeInterpret();
1984 if (pCode->GetCodeError())
1985 return false;
1987 return aResult.IsValueNoError();
1990 bool ScFormulaCell::IsHybridValueCell()
1992 return aResult.GetType() == formula::svHybridValueCell;
1995 double ScFormulaCell::GetValue()
1997 MaybeInterpret();
1998 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
1999 !aResult.GetResultError())
2000 return aResult.GetDouble();
2001 return 0.0;
2004 svl::SharedString ScFormulaCell::GetString()
2006 MaybeInterpret();
2007 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
2008 !aResult.GetResultError())
2009 return aResult.GetString();
2011 return svl::SharedString::getEmptyString();
2014 const ScMatrix* ScFormulaCell::GetMatrix()
2016 if ( pDocument->GetAutoCalc() )
2018 if( IsDirtyOrInTableOpDirty()
2019 // Was stored !bDirty but an accompanying matrix cell was bDirty?
2020 || (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix()))
2021 Interpret();
2023 return aResult.GetMatrix().get();
2026 bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
2028 switch ( cMatrixFlag )
2030 case MM_FORMULA :
2031 rPos = aPos;
2032 return true;
2033 case MM_REFERENCE :
2035 pCode->Reset();
2036 ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2037 if( t )
2039 ScSingleRefData& rRef = t->GetSingleRef();
2040 ScAddress aAbs = rRef.toAbs(aPos);
2041 if (ValidAddress(aAbs))
2043 rPos = aAbs;
2044 return true;
2048 break;
2050 return false;
2055 Edge-Values:
2058 4 16
2061 inside: 1
2062 outside: 0
2063 (reserved: open: 32)
2066 sal_uInt16 ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) const
2068 switch ( cMatrixFlag )
2070 case MM_FORMULA :
2071 case MM_REFERENCE :
2073 static SCCOL nC;
2074 static SCROW nR;
2075 ScAddress aOrg;
2076 if ( !GetMatrixOrigin( aOrg ) )
2077 return 0; // bad luck..
2078 if ( aOrg != rOrgPos )
2079 { // First time or a different matrix than last time.
2080 rOrgPos = aOrg;
2081 const ScFormulaCell* pFCell;
2082 if ( cMatrixFlag == MM_REFERENCE )
2083 pFCell = pDocument->GetFormulaCell(aOrg);
2084 else
2085 pFCell = this; // this MM_FORMULA
2086 // There's only one this, don't compare pFCell==this.
2087 if (pFCell && pFCell->cMatrixFlag == MM_FORMULA)
2089 pFCell->GetMatColsRows( nC, nR );
2090 if ( nC == 0 || nR == 0 )
2092 // No ScMatrixFormulaCellToken available yet, calculate new.
2093 nC = 1;
2094 nR = 1;
2095 ScAddress aTmpOrg;
2096 ScFormulaCell* pCell;
2097 ScAddress aAdr( aOrg );
2098 aAdr.IncCol();
2099 bool bCont = true;
2102 pCell = pDocument->GetFormulaCell(aAdr);
2103 if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
2104 pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
2106 nC++;
2107 aAdr.IncCol();
2109 else
2110 bCont = false;
2111 } while ( bCont );
2112 aAdr = aOrg;
2113 aAdr.IncRow();
2114 bCont = true;
2117 pCell = pDocument->GetFormulaCell(aAdr);
2118 if (pCell && pCell->cMatrixFlag == MM_REFERENCE &&
2119 pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
2121 nR++;
2122 aAdr.IncRow();
2124 else
2125 bCont = false;
2126 } while ( bCont );
2128 const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2131 else
2133 #if OSL_DEBUG_LEVEL > 0
2134 OStringBuffer aMsg("broken Matrix, no MatFormula at origin, Pos: ");
2135 OUString aTmp(aPos.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument));
2136 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
2137 aMsg.append(", MatOrg: ");
2138 aTmp = aOrg.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument);
2139 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_ASCII_US));
2140 OSL_FAIL(aMsg.getStr());
2141 #endif
2142 return 0; // bad luck ...
2145 // here we are, healthy and clean, somewhere in between
2146 SCsCOL dC = aPos.Col() - aOrg.Col();
2147 SCsROW dR = aPos.Row() - aOrg.Row();
2148 sal_uInt16 nEdges = 0;
2149 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2151 if ( dC == 0 )
2152 nEdges |= sc::MatrixEdgeLeft; // left edge
2153 if ( dC+1 == nC )
2154 nEdges |= sc::MatrixEdgeRight; // right edge
2155 if ( dR == 0 )
2156 nEdges |= sc::MatrixEdgeTop; // top edge
2157 if ( dR+1 == nR )
2158 nEdges |= sc::MatrixEdgeBottom; // bottom edge
2159 if ( !nEdges )
2160 nEdges = sc::MatrixEdgeInside; // inside
2162 #if OSL_DEBUG_LEVEL > 0
2163 else
2165 OStringBuffer aMsg( "broken Matrix, Pos: " );
2166 OUString aTmp(aPos.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument));
2167 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
2168 aMsg.append(", MatOrg: ");
2169 aTmp = aOrg.Format(SCA_VALID_COL | SCA_VALID_ROW, pDocument);
2170 aMsg.append(OUStringToOString(aTmp, RTL_TEXTENCODING_UTF8 ));
2171 aMsg.append(", MatCols: ");
2172 aMsg.append(static_cast<sal_Int32>( nC ));
2173 aMsg.append(", MatRows: ");
2174 aMsg.append(static_cast<sal_Int32>( nR ));
2175 aMsg.append(", DiffCols: ");
2176 aMsg.append(static_cast<sal_Int32>( dC ));
2177 aMsg.append(", DiffRows: ");
2178 aMsg.append(static_cast<sal_Int32>( dR ));
2179 OSL_FAIL( aMsg.makeStringAndClear().getStr());
2181 #endif
2182 return nEdges;
2183 // break;
2185 default:
2186 return 0;
2190 sal_uInt16 ScFormulaCell::GetErrCode()
2192 MaybeInterpret();
2194 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2195 * and not also abused for signaling other error conditions we could bail
2196 * out even before attempting to interpret broken code. */
2197 sal_uInt16 nErr = pCode->GetCodeError();
2198 if (nErr)
2199 return nErr;
2200 return aResult.GetResultError();
2203 sal_uInt16 ScFormulaCell::GetRawError()
2205 sal_uInt16 nErr = pCode->GetCodeError();
2206 if (nErr)
2207 return nErr;
2208 return aResult.GetResultError();
2211 bool ScFormulaCell::GetErrorOrValue( sal_uInt16& rErr, double& rVal )
2213 MaybeInterpret();
2215 rErr = pCode->GetCodeError();
2216 if (rErr)
2217 return true;
2219 return aResult.GetErrorOrDouble(rErr, rVal);
2222 bool ScFormulaCell::GetErrorOrString( sal_uInt16& rErr, svl::SharedString& rStr )
2224 MaybeInterpret();
2226 rErr = pCode->GetCodeError();
2227 if (rErr)
2228 return true;
2230 return aResult.GetErrorOrString(rErr, rStr);
2233 sc::FormulaResultValue ScFormulaCell::GetResult()
2235 MaybeInterpret();
2237 sal_uInt16 nErr = pCode->GetCodeError();
2238 if (nErr)
2239 return sc::FormulaResultValue(nErr);
2241 return aResult.GetResult();
2244 bool ScFormulaCell::HasOneReference( ScRange& r ) const
2246 pCode->Reset();
2247 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2248 if( p && !pCode->GetNextReferenceRPN() ) // only one!
2250 SingleDoubleRefProvider aProv( *p );
2251 r.aStart = aProv.Ref1.toAbs(aPos);
2252 r.aEnd = aProv.Ref2.toAbs(aPos);
2253 return true;
2255 else
2256 return false;
2259 bool
2260 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
2262 /* If there appears just one reference in the formula, it's the same
2263 as HasOneReference(). If there are more of them, they can denote
2264 one range if they are (sole) arguments of one function.
2265 Union of these references must form one range and their
2266 intersection must be empty set.
2269 // Detect the simple case of exactly one reference in advance without all
2270 // overhead.
2271 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
2272 // work again, where the function does not have only references.
2273 if (HasOneReference( rRange))
2274 return true;
2276 pCode->Reset();
2277 // Get first reference, if any
2278 ScToken* const pFirstReference(
2279 dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN()));
2280 if (pFirstReference)
2282 // Collect all consecutive references, starting by the one
2283 // already found
2284 std::deque<ScToken*> aReferences;
2285 aReferences.push_back(pFirstReference);
2286 FormulaToken* pToken(pCode->NextRPN());
2287 FormulaToken* pFunction(0);
2288 while (pToken)
2290 if (lcl_isReference(*pToken))
2292 aReferences.push_back(dynamic_cast<ScToken*>(pToken));
2293 pToken = pCode->NextRPN();
2295 else
2297 if (pToken->IsFunction())
2299 pFunction = pToken;
2301 break;
2304 if (pFunction && !pCode->GetNextReferenceRPN()
2305 && (pFunction->GetParamCount() == aReferences.size()))
2307 return lcl_refListFormsOneRange(aPos, aReferences, rRange);
2310 return false;
2313 bool ScFormulaCell::HasRelNameReference() const
2315 pCode->Reset();
2316 ScToken* t;
2317 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
2319 if ( t->GetSingleRef().IsRelName() ||
2320 (t->GetType() == formula::svDoubleRef &&
2321 t->GetDoubleRef().Ref2.IsRelName()) )
2322 return true;
2324 return false;
2327 bool ScFormulaCell::HasColRowName() const
2329 pCode->Reset();
2330 return (pCode->GetNextColRowName() != NULL);
2333 bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext& rCxt )
2335 if (rCxt.meMode != URM_INSDEL)
2336 // Just in case...
2337 return false;
2339 if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
2340 // No movement.
2341 return false;
2343 if (!rCxt.maRange.In(aPos))
2344 return false;
2346 // This formula cell itself is being shifted during cell range
2347 // insertion or deletion. Update its position.
2348 aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
2350 return true;
2353 namespace {
2356 * Check if we need to re-compile column or row names.
2358 bool checkCompileColRowName(
2359 const sc::RefUpdateContext& rCxt, ScDocument& rDoc, ScTokenArray& rCode,
2360 const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
2362 switch (rCxt.meMode)
2364 case URM_INSDEL:
2366 if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
2367 return false;
2369 ScToken* t;
2370 ScRangePairList* pColList = rDoc.GetColNameRanges();
2371 ScRangePairList* pRowList = rDoc.GetRowNameRanges();
2372 rCode.Reset();
2373 while ((t = static_cast<ScToken*>(rCode.GetNextColRowName())) != NULL)
2375 ScSingleRefData& rRef = t->GetSingleRef();
2376 if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
2377 { // ColName
2378 ScAddress aAdr = rRef.toAbs(aPos);
2379 ScRangePair* pR = pColList->Find( aAdr );
2380 if ( pR )
2381 { // defined
2382 if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
2383 return true;
2385 else
2386 { // on the fly
2387 if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
2388 return true;
2391 if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
2392 { // RowName
2393 ScAddress aAdr = rRef.toAbs(aPos);
2394 ScRangePair* pR = pRowList->Find( aAdr );
2395 if ( pR )
2396 { // defined
2397 if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
2398 return true;
2400 else
2401 { // on the fly
2402 if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
2403 return true;
2408 break;
2409 case URM_MOVE:
2410 { // Recomplie for Move/D&D when ColRowName was moved or this Cell
2411 // points to one and was moved.
2412 bool bMoved = (aPos != aOldPos);
2413 if (bMoved)
2414 return true;
2416 rCode.Reset();
2417 const ScToken* t = static_cast<const ScToken*>(rCode.GetNextColRowName());
2418 for (; t; t = static_cast<const ScToken*>(rCode.GetNextColRowName()))
2420 const ScSingleRefData& rRef = t->GetSingleRef();
2421 ScAddress aAbs = rRef.toAbs(aPos);
2422 if (ValidAddress(aAbs))
2424 if (rCxt.maRange.In(aAbs))
2425 return true;
2429 break;
2430 case URM_COPY:
2431 return bValChanged;
2432 default:
2436 return false;
2439 void setOldCodeToUndo(
2440 ScDocument* pUndoDoc, const ScAddress& aUndoPos, ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, sal_uInt8 cMatrixFlag)
2442 // Copy the cell to aUndoPos, which is its current position in the document,
2443 // so this works when UpdateReference is called before moving the cells
2444 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
2445 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
2447 // If there is already a formula cell in the undo document, don't overwrite it,
2448 // the first (oldest) is the important cell.
2449 if (pUndoDoc->GetCellType(aUndoPos) == CELLTYPE_FORMULA)
2450 return;
2452 ScFormulaCell* pFCell =
2453 new ScFormulaCell(
2454 pUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(), eTempGrammar, cMatrixFlag);
2456 pFCell->SetResultToken(NULL); // to recognize it as changed later (Cut/Paste!)
2457 pUndoDoc->SetFormulaCell(aUndoPos, pFCell);
2462 bool ScFormulaCell::UpdateReferenceOnShift(
2463 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
2465 if (rCxt.meMode != URM_INSDEL)
2466 // Just in case...
2467 return false;
2469 bool bCellStateChanged = false;
2470 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
2471 if ( pUndoCellPos )
2472 aUndoPos = *pUndoCellPos;
2473 ScAddress aOldPos( aPos );
2474 bCellStateChanged = UpdatePosOnShift(rCxt);
2476 // Check presence of any references or column row names.
2477 pCode->Reset();
2478 bool bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
2479 bool bHasColRowNames = false;
2480 if (!bHasRefs)
2482 pCode->Reset();
2483 bHasColRowNames = (pCode->GetNextColRowName() != NULL);
2484 bHasRefs = bHasRefs || bHasColRowNames;
2486 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
2488 if (!bHasRefs && !bOnRefMove)
2489 // This formula cell contains no references, nor needs recalculating
2490 // on reference update. Bail out.
2491 return bCellStateChanged;
2493 boost::scoped_ptr<ScTokenArray> pOldCode;
2494 if (pUndoDoc)
2495 pOldCode.reset(pCode->Clone());
2497 bool bValChanged = false;
2498 bool bRefModified = false;
2499 bool bRefSizeChanged = false;
2500 bool bRecompile = bCompile;
2502 if (bHasRefs)
2504 // Update cell or range references.
2505 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
2506 bRefModified = aRes.mbReferenceModified;
2507 bValChanged = aRes.mbValueChanged;
2508 if (aRes.mbNameModified)
2509 bRecompile = true;
2512 if (bValChanged || bRefModified)
2513 bCellStateChanged = true;
2515 if (bOnRefMove)
2516 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
2517 bOnRefMove = (bValChanged || (aPos != aOldPos));
2519 bool bHasRelName = false;
2520 bool bNewListening = false;
2521 bool bInDeleteUndo = false;
2523 if (bHasRefs)
2525 // Upon Insert ColRowNames have to be recompiled in case the
2526 // insertion occurs right in front of the range.
2527 if (bHasColRowNames && !bRecompile)
2528 bRecompile = checkCompileColRowName(rCxt, *pDocument, *pCode, aOldPos, aPos, bValChanged);
2530 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
2531 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
2533 // RelNameRefs are always moved
2534 bHasRelName = HasRelNameReference();
2535 // Reference changed and new listening needed?
2536 // Except in Insert/Delete without specialties.
2537 bNewListening = (bRefModified || bRecompile
2538 || (bValChanged && (bInDeleteUndo || bRefSizeChanged)) || bHasRelName);
2540 if ( bNewListening )
2541 EndListeningTo(pDocument, pOldCode.get(), aOldPos);
2544 // NeedDirty for changes except for Copy and Move/Insert without RelNames
2545 bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
2547 if (pUndoDoc && (bValChanged || bOnRefMove))
2548 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
2550 bCompile |= bRecompile;
2551 if (bCompile)
2553 CompileTokenArray( bNewListening ); // no Listening
2554 bNeedDirty = true;
2557 if ( !bInDeleteUndo )
2558 { // In ChangeTrack Delete-Reject listeners are established in
2559 // InsertCol/InsertRow
2560 if ( bNewListening )
2562 // Inserts/Deletes re-establish listeners after all
2563 // UpdateReference calls.
2564 // All replaced shared formula listeners have to be
2565 // established after an Insert or Delete. Do nothing here.
2566 SetNeedsListening( true);
2570 if (bNeedDirty && !bHasRelName)
2571 { // Cut off references, invalid or similar?
2572 // Postpone SetDirty() until all listeners have been re-established in
2573 // Inserts/Deletes.
2574 mbPostponedDirty = true;
2577 return bCellStateChanged;
2580 bool ScFormulaCell::UpdateReferenceOnMove(
2581 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
2583 if (rCxt.meMode != URM_MOVE)
2584 return false;
2586 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
2587 if ( pUndoCellPos )
2588 aUndoPos = *pUndoCellPos;
2589 ScAddress aOldPos( aPos );
2591 if (rCxt.maRange.In(aPos))
2593 // The cell is being moved or copied to a new position. I guess the
2594 // position has been updated prior to this call? Determine
2595 // its original position before the move which will be used to adjust
2596 // relative references later.
2597 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
2600 // Check presence of any references or column row names.
2601 pCode->Reset();
2602 bool bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
2603 bool bHasColRowNames = false;
2604 if (!bHasRefs)
2606 pCode->Reset();
2607 bHasColRowNames = (pCode->GetNextColRowName() != NULL);
2608 bHasRefs = bHasRefs || bHasColRowNames;
2610 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
2612 if (!bHasRefs && !bOnRefMove)
2613 // This formula cell contains no references, nor needs recalculating
2614 // on reference update. Bail out.
2615 return false;
2617 bool bCellStateChanged = false;
2618 boost::scoped_ptr<ScTokenArray> pOldCode;
2619 if (pUndoDoc)
2620 pOldCode.reset(pCode->Clone());
2622 bool bValChanged = false;
2623 bool bRefModified = false;
2624 bool bRefSizeChanged = false;
2626 if (bHasRefs)
2628 // Update cell or range references.
2629 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
2630 bRefModified = aRes.mbReferenceModified;
2631 bValChanged = aRes.mbValueChanged;
2634 if (bValChanged || bRefModified)
2635 bCellStateChanged = true;
2637 if (bOnRefMove)
2638 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
2639 bOnRefMove = (bValChanged || (aPos != aOldPos));
2641 bool bColRowNameCompile = false;
2642 bool bHasRelName = false;
2643 bool bNewListening = false;
2644 bool bInDeleteUndo = false;
2646 if (bHasRefs)
2648 // Upon Insert ColRowNames have to be recompiled in case the
2649 // insertion occurs right in front of the range.
2650 if (bHasColRowNames)
2651 bColRowNameCompile = checkCompileColRowName(rCxt, *pDocument, *pCode, aOldPos, aPos, bValChanged);
2653 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
2654 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
2656 // RelNameRefs are always moved
2657 bHasRelName = HasRelNameReference();
2658 // Reference changed and new listening needed?
2659 // Except in Insert/Delete without specialties.
2660 bNewListening = (bRefModified || bColRowNameCompile
2661 || bValChanged || bHasRelName)
2662 // #i36299# Don't duplicate action during cut&paste / drag&drop
2663 // on a cell in the range moved, start/end listeners is done
2664 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
2665 && !(pDocument->IsInsertingFromOtherDoc() && rCxt.maRange.In(aPos));
2667 if ( bNewListening )
2668 EndListeningTo(pDocument, pOldCode.get(), aOldPos);
2671 bool bNeedDirty = false;
2672 // NeedDirty for changes except for Copy and Move/Insert without RelNames
2673 if ( bRefModified || bColRowNameCompile ||
2674 (bValChanged && bHasRelName && (bHasRelName || bInDeleteUndo || bRefSizeChanged)) || bOnRefMove)
2675 bNeedDirty = true;
2677 if (pUndoDoc && (bValChanged || bOnRefMove))
2678 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
2680 bValChanged = false;
2682 if ( ( bCompile = (bCompile || bValChanged || bRefModified || bColRowNameCompile) ) != 0 )
2684 CompileTokenArray( bNewListening ); // no Listening
2685 bNeedDirty = true;
2688 if ( !bInDeleteUndo )
2689 { // In ChangeTrack Delete-Reject listeners are established in
2690 // InsertCol/InsertRow
2691 if ( bNewListening )
2693 StartListeningTo( pDocument );
2697 if (bNeedDirty)
2698 { // Cut off references, invalid or similar?
2699 sc::AutoCalcSwitch(*pDocument, false);
2700 SetDirty();
2703 return bCellStateChanged;
2706 bool ScFormulaCell::UpdateReferenceOnCopy(
2707 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
2709 if (rCxt.meMode != URM_COPY)
2710 return false;
2712 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
2713 if ( pUndoCellPos )
2714 aUndoPos = *pUndoCellPos;
2715 ScAddress aOldPos( aPos );
2717 if (rCxt.maRange.In(aPos))
2719 // The cell is being moved or copied to a new position. I guess the
2720 // position has been updated prior to this call? Determine
2721 // its original position before the move which will be used to adjust
2722 // relative references later.
2723 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
2726 // Check presence of any references or column row names.
2727 pCode->Reset();
2728 bool bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
2729 pCode->Reset();
2730 bool bHasColRowNames = (pCode->GetNextColRowName() != NULL);
2731 bHasRefs = bHasRefs || bHasColRowNames;
2732 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
2734 if (!bHasRefs && !bOnRefMove)
2735 // This formula cell contains no references, nor needs recalculating
2736 // on reference update. Bail out.
2737 return false;
2739 boost::scoped_ptr<ScTokenArray> pOldCode;
2740 if (pUndoDoc)
2741 pOldCode.reset(pCode->Clone());
2743 if (bOnRefMove)
2744 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
2745 bOnRefMove = (aPos != aOldPos);
2747 bool bNeedDirty = bOnRefMove;
2749 if (pUndoDoc && bOnRefMove)
2750 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
2752 if (bCompile)
2754 CompileTokenArray(false); // no Listening
2755 bNeedDirty = true;
2758 if (bNeedDirty)
2759 { // Cut off references, invalid or similar?
2760 sc::AutoCalcSwitch(*pDocument, false);
2761 SetDirty();
2764 return false;
2767 bool ScFormulaCell::UpdateReference(
2768 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
2770 if (pDocument->IsClipOrUndo())
2771 return false;
2773 if (mxGroup && mxGroup->mpTopCell != this)
2775 // This is not a top cell of a formula group. Don't update references.
2777 switch (rCxt.meMode)
2779 case URM_INSDEL:
2780 return UpdatePosOnShift(rCxt);
2781 break;
2782 default:
2785 return false;
2788 switch (rCxt.meMode)
2790 case URM_INSDEL:
2791 return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
2792 case URM_MOVE:
2793 return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
2794 case URM_COPY:
2795 return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
2796 default:
2800 return false;
2803 void ScFormulaCell::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
2805 // Adjust tokens only when it's not grouped or grouped top cell.
2806 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
2807 bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
2808 pCode->Reset();
2809 if (pDocument->IsClipOrUndo() || !pCode->GetNextReferenceRPN())
2811 if (bPosChanged)
2812 aPos.IncTab(rCxt.mnSheets);
2814 return;
2817 EndListeningTo( pDocument );
2818 ScAddress aOldPos = aPos;
2819 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
2820 if (bPosChanged)
2821 aPos.IncTab(rCxt.mnSheets);
2823 if (!bAdjustCode)
2824 return;
2826 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aOldPos);
2827 if (aRes.mbNameModified)
2828 // Re-compile after new sheet(s) have been inserted.
2829 bCompile = true;
2831 // no StartListeningTo because the new sheets have not been inserted yet.
2834 bool ScFormulaCell::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
2836 // Adjust tokens only when it's not grouped or grouped top cell.
2837 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
2838 bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
2839 pCode->Reset();
2840 if (pDocument->IsClipOrUndo() || !pCode->GetNextReferenceRPN())
2842 if (bPosChanged)
2843 aPos.IncTab(-1*rCxt.mnSheets);
2844 return false;
2847 EndListeningTo( pDocument );
2848 // IncTab _after_ EndListeningTo und _before_ Compiler UpdateDeleteTab!
2849 ScAddress aOldPos = aPos;
2850 if (bPosChanged)
2851 aPos.IncTab(-1*rCxt.mnSheets);
2853 if (!bAdjustCode)
2854 return false;
2856 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
2857 if (aRes.mbNameModified)
2858 // Re-compile after sheet(s) have been deleted.
2859 bCompile = true;
2861 return aRes.mbReferenceModified;
2864 void ScFormulaCell::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
2866 // Adjust tokens only when it's not grouped or grouped top cell.
2867 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
2869 pCode->Reset();
2870 if (!pCode->GetNextReferenceRPN() || pDocument->IsClipOrUndo())
2872 aPos.SetTab(nTabNo);
2873 return;
2876 EndListeningTo(pDocument);
2877 ScAddress aOldPos = aPos;
2878 // SetTab _after_ EndListeningTo und _before_ Compiler UpdateMoveTab !
2879 aPos.SetTab(nTabNo);
2881 // no StartListeningTo because pTab[nTab] not yet correct!
2883 if (!bAdjustCode)
2884 return;
2886 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
2887 if (aRes.mbNameModified)
2888 // Re-compile after sheet(s) have been deleted.
2889 bCompile = true;
2892 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
2894 if (pDocument->IsClipOrUndo())
2895 return;
2897 pCode->Reset();
2898 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2899 while (p)
2901 ScSingleRefData& rRef1 = p->GetSingleRef();
2902 if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
2903 rRef1.IncTab(1);
2904 if (p->GetType() == formula::svDoubleRef)
2906 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
2907 if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
2908 rRef2.IncTab(1);
2910 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2914 bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
2916 if (pDocument->IsClipOrUndo())
2917 return false;
2919 bool bRet = false;
2920 pCode->Reset();
2921 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2922 while (p)
2924 ScSingleRefData& rRef1 = p->GetSingleRef();
2925 if (!rRef1.IsTabRel())
2927 if (nTable != rRef1.Tab())
2928 bRet = true;
2929 else if (nTable != aPos.Tab())
2930 rRef1.SetAbsTab(aPos.Tab());
2932 if (p->GetType() == formula::svDoubleRef)
2934 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
2935 if (!rRef2.IsTabRel())
2937 if(nTable != rRef2.Tab())
2938 bRet = true;
2939 else if (nTable != aPos.Tab())
2940 rRef2.SetAbsTab(aPos.Tab());
2943 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
2945 return bRet;
2948 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
2950 if ( bForceIfNameInUse && !bCompile )
2951 bCompile = pCode->HasNameOrColRowName();
2952 if ( bCompile )
2953 pCode->SetCodeError( 0 ); // make sure it will really be compiled
2954 CompileTokenArray();
2957 // Reference transposition is only called in Clipboard Document
2958 void ScFormulaCell::TransposeReference()
2960 bool bFound = false;
2961 pCode->Reset();
2962 ScToken* t;
2963 while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
2965 ScSingleRefData& rRef1 = t->GetSingleRef();
2966 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
2968 bool bDouble = (t->GetType() == formula::svDoubleRef);
2969 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1);
2970 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
2972 SCCOLROW nTemp;
2974 nTemp = rRef1.Col();
2975 rRef1.SetRelCol(rRef1.Row());
2976 rRef1.SetRelRow(nTemp);
2978 if ( bDouble )
2980 nTemp = rRef2.Col();
2981 rRef2.SetRelCol(rRef2.Row());
2982 rRef2.SetRelRow(nTemp);
2985 bFound = true;
2990 if (bFound)
2991 bCompile = true;
2994 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
2995 ScDocument* pUndoDoc )
2997 EndListeningTo( pDocument );
2999 ScAddress aOldPos = aPos;
3000 bool bPosChanged = false; // Whether this cell has been moved
3002 ScRange aDestRange( rDest, ScAddress(
3003 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3004 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3005 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3006 if ( aDestRange.In( aOldPos ) )
3008 // Count back Positions
3009 SCsCOL nRelPosX = aOldPos.Col();
3010 SCsROW nRelPosY = aOldPos.Row();
3011 SCsTAB nRelPosZ = aOldPos.Tab();
3012 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
3013 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3014 bPosChanged = true;
3017 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
3018 bool bRefChanged = false;
3019 ScToken* t;
3021 ScRangeData* pShared = NULL;
3022 pCode->Reset();
3023 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
3025 if( t->GetOpCode() == ocName )
3027 ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
3028 if (pName)
3030 if (pName->IsModified())
3031 bRefChanged = true;
3034 else if( t->GetType() != svIndex )
3036 SingleDoubleRefModifier aMod(*t);
3037 ScComplexRefData& rRef = aMod.Ref();
3038 ScRange aAbs = rRef.toAbs(aOldPos);
3039 bool bMod = (ScRefUpdate::UpdateTranspose(pDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3040 if (bMod)
3042 rRef.SetRange(aAbs, aPos); // based on the new anchor position.
3043 bRefChanged = true;
3048 if (pShared) // Exchange Shared Formula with real Formula
3050 pDocument->RemoveFromFormulaTree( this ); // update formula count
3051 delete pCode;
3052 pCode = new ScTokenArray( *pShared->GetCode() );
3053 bRefChanged = true;
3054 pCode->Reset();
3055 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
3057 if( t->GetType() != svIndex )
3059 SingleDoubleRefModifier aMod(*t);
3060 ScComplexRefData& rRef = aMod.Ref();
3061 ScRange aAbs = rRef.toAbs(aOldPos);
3062 bool bMod = (ScRefUpdate::UpdateTranspose(pDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3063 if (bMod)
3064 rRef.SetRange(aAbs, aPos); // based on the new anchor position.
3069 if (bRefChanged)
3071 if (pUndoDoc)
3073 ScFormulaCell* pFCell = new ScFormulaCell(
3074 pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(), eTempGrammar, cMatrixFlag);
3076 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
3077 pUndoDoc->SetFormulaCell(aPos, pFCell);
3080 bCompile = true;
3081 CompileTokenArray(); // also call StartListeningTo
3082 SetDirty();
3084 else
3085 StartListeningTo( pDocument ); // Listener as previous
3087 delete pOld;
3090 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3092 EndListeningTo( pDocument );
3094 bool bRefChanged = false;
3095 ScToken* t;
3096 ScRangeData* pShared = NULL;
3098 pCode->Reset();
3099 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
3101 if( t->GetOpCode() == ocName )
3103 ScRangeData* pName = pDocument->GetRangeName()->findByIndex( t->GetIndex() );
3104 if (pName)
3106 if (pName->IsModified())
3107 bRefChanged = true;
3110 else if( t->GetType() != svIndex )
3112 SingleDoubleRefModifier aMod(*t);
3113 ScComplexRefData& rRef = aMod.Ref();
3114 ScRange aAbs = rRef.toAbs(aPos);
3115 bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3116 if (bMod)
3118 rRef.SetRange(aAbs, aPos);
3119 bRefChanged = true;
3124 if (pShared) // Exchange Shared Formula with real Formula
3126 pDocument->RemoveFromFormulaTree( this ); // Update formula count
3127 delete pCode;
3128 pCode = new ScTokenArray( *pShared->GetCode() );
3129 bRefChanged = true;
3130 pCode->Reset();
3131 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
3133 if( t->GetType() != svIndex )
3135 SingleDoubleRefModifier aMod(*t);
3136 ScComplexRefData& rRef = aMod.Ref();
3137 ScRange aAbs = rRef.toAbs(aPos);
3138 bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3139 if (bMod)
3140 rRef.SetRange(aAbs, aPos);
3145 if (bRefChanged)
3147 bCompile = true;
3148 CompileTokenArray(); // Also call StartListeningTo
3149 SetDirty();
3151 else
3152 StartListeningTo( pDocument ); // Listener as previous
3155 static void lcl_FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
3157 for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
3159 if (p->GetOpCode() == ocName)
3161 sal_uInt16 nTokenIndex = p->GetIndex();
3162 rIndexes.insert( nTokenIndex );
3164 ScRangeData* pSubName = pNames->findByIndex(p->GetIndex());
3165 if (pSubName)
3166 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
3171 void ScFormulaCell::FindRangeNamesInUse(std::set<sal_uInt16>& rIndexes) const
3173 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
3176 bool ScFormulaCell::IsSubTotal() const
3178 return bSubTotal;
3181 bool ScFormulaCell::IsChanged() const
3183 return bChanged;
3186 void ScFormulaCell::SetChanged(bool b)
3188 bChanged = b;
3191 sal_uInt8 ScFormulaCell::GetMatrixFlag() const
3193 return cMatrixFlag;
3196 ScTokenArray* ScFormulaCell::GetCode()
3198 return pCode;
3201 const ScTokenArray* ScFormulaCell::GetCode() const
3203 return pCode;
3206 bool ScFormulaCell::IsRunning() const
3208 return bRunning;
3211 void ScFormulaCell::SetRunning( bool bVal )
3213 bRunning = bVal;
3216 void ScFormulaCell::CompileDBFormula()
3218 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
3220 if ( p->GetOpCode() == ocDBArea
3221 || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) )
3223 bCompile = true;
3224 CompileTokenArray();
3225 SetDirty();
3226 break;
3231 void ScFormulaCell::CompileDBFormula( bool bCreateFormulaString )
3233 // Two phases must be called after each other
3234 // 1. Formula String with old generated names
3235 // 2. Formula String with new generated names
3236 if ( bCreateFormulaString )
3238 bool bRecompile = false;
3239 pCode->Reset();
3240 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
3242 switch ( p->GetOpCode() )
3244 case ocBad: // DB Area eventually goes bad
3245 case ocColRowName: // in case of the same names
3246 case ocDBArea: // DB Area
3247 bRecompile = true;
3248 break;
3249 case ocName:
3250 if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
3251 bRecompile = true; // DB Area
3252 break;
3253 default:
3254 ; // nothing
3257 if ( bRecompile )
3259 OUString aFormula;
3260 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
3261 if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
3263 if ( aFormula[ aFormula.getLength()-1 ] == '}' )
3264 aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
3265 if ( aFormula[0] == '{' )
3266 aFormula = aFormula.copy( 1 );
3268 EndListeningTo( pDocument );
3269 pDocument->RemoveFromFormulaTree( this );
3270 pCode->Clear();
3271 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
3274 else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
3276 Compile( aResult.GetHybridFormula(), false, eTempGrammar );
3277 aResult.SetToken( NULL);
3278 SetDirty();
3282 void ScFormulaCell::CompileNameFormula( bool bCreateFormulaString )
3284 // Two phases must be called after each other
3285 // 1. Formula String with old generated names
3286 // 2. Formula String with new generated names
3287 if ( bCreateFormulaString )
3289 bool bRecompile = false;
3290 pCode->Reset();
3291 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
3293 switch ( p->GetOpCode() )
3295 case ocBad: // in case RangeName goes bad
3296 case ocColRowName: // in case the names are the same
3297 bRecompile = true;
3298 break;
3299 default:
3300 if ( p->GetType() == svIndex )
3301 bRecompile = true; // RangeName
3304 if ( bRecompile )
3306 OUString aFormula;
3307 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
3308 if ( GetMatrixFlag() != MM_NONE && !aFormula.isEmpty() )
3310 if ( aFormula[ aFormula.getLength()-1 ] == '}' )
3311 aFormula = aFormula.copy( 0, aFormula.getLength()-1 );
3312 if ( aFormula[0] == '{' )
3313 aFormula = aFormula.copy( 1 );
3315 EndListeningTo( pDocument );
3316 pDocument->RemoveFromFormulaTree( this );
3317 pCode->Clear();
3318 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
3321 else if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
3323 Compile( aResult.GetHybridFormula(), false, eTempGrammar );
3324 aResult.SetToken( NULL);
3325 SetDirty();
3329 void ScFormulaCell::CompileColRowNameFormula()
3331 pCode->Reset();
3332 for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
3334 if ( p->GetOpCode() == ocColRowName )
3336 bCompile = true;
3337 CompileTokenArray();
3338 SetDirty();
3339 break;
3344 ScFormulaCell* ScFormulaCell::GetPrevious() const { return pPrevious; }
3345 ScFormulaCell* ScFormulaCell::GetNext() const { return pNext; }
3346 void ScFormulaCell::SetPrevious( ScFormulaCell* pF ) { pPrevious = pF; }
3347 void ScFormulaCell::SetNext( ScFormulaCell* pF ) { pNext = pF; }
3348 ScFormulaCell* ScFormulaCell::GetPreviousTrack() const { return pPreviousTrack; }
3349 ScFormulaCell* ScFormulaCell::GetNextTrack() const { return pNextTrack; }
3350 void ScFormulaCell::SetPreviousTrack( ScFormulaCell* pF ) { pPreviousTrack = pF; }
3351 void ScFormulaCell::SetNextTrack( ScFormulaCell* pF ) { pNextTrack = pF; }
3353 ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant )
3355 if (mxGroup)
3357 // You can't create a new group if the cell is already a part of a group.
3358 // Is this a sign of some inconsistent or incorrect data structures? Or normal?
3359 SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
3360 return ScFormulaCellGroupRef();
3363 mxGroup.reset(new ScFormulaCellGroup);
3364 mxGroup->mpTopCell = this;
3365 mxGroup->mbInvariant = bInvariant;
3366 mxGroup->mnLength = nLen;
3367 mxGroup->mpCode = pCode; // Move this to the shared location.
3368 if (mxGroup->sxCompilationThread.is())
3369 mxGroup->scheduleCompilation();
3370 return mxGroup;
3373 ScFormulaCellGroupRef ScFormulaCell::GetCellGroup()
3375 return mxGroup;
3378 void ScFormulaCell::SetCellGroup( const ScFormulaCellGroupRef &xRef )
3380 if (!xRef)
3382 // Make this cell a non-grouped cell.
3383 if (mxGroup)
3384 pCode = mxGroup->mpCode->Clone();
3386 mxGroup = xRef;
3387 return;
3390 // Group object has shared token array.
3391 if (!mxGroup)
3392 // Currently not shared. Delete the existing token array first.
3393 delete pCode;
3395 mxGroup = xRef;
3396 pCode = mxGroup->mpCode;
3399 ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( ScFormulaCell& rOther ) const
3401 // no Matrix formulae yet.
3402 if ( GetMatrixFlag() != MM_NONE )
3403 return NotEqual;
3405 // are these formule at all similar ?
3406 if ( GetHash() != rOther.GetHash() )
3407 return NotEqual;
3409 FormulaToken **pThis = pCode->GetCode();
3410 sal_uInt16 nThisLen = pCode->GetCodeLen();
3411 FormulaToken **pOther = rOther.pCode->GetCode();
3412 sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
3414 if ( !pThis || !pOther )
3416 // Error: no compiled code for cells !"
3417 return NotEqual;
3420 if ( nThisLen != nOtherLen )
3421 return NotEqual;
3423 bool bInvariant = true;
3425 // check we are basically the same function
3426 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
3428 ScToken *pThisTok = static_cast<ScToken*>( pThis[i] );
3429 ScToken *pOtherTok = static_cast<ScToken*>( pOther[i] );
3431 if ( pThisTok->GetType() != pOtherTok->GetType() ||
3432 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
3433 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
3435 // Incompatible type, op-code or param counts.
3436 return NotEqual;
3439 switch (pThisTok->GetType())
3441 case formula::svMatrix:
3442 case formula::svExternalSingleRef:
3443 case formula::svExternalDoubleRef:
3444 // Ignoring matrix and external references for now.
3445 return NotEqual;
3447 case formula::svSingleRef:
3449 // Single cell reference.
3450 const ScSingleRefData& rRef = pThisTok->GetSingleRef();
3451 if (rRef != pOtherTok->GetSingleRef())
3452 return NotEqual;
3454 if (rRef.IsRowRel())
3455 bInvariant = false;
3457 break;
3458 case formula::svDoubleRef:
3460 // Range reference.
3461 const ScSingleRefData& rRef1 = pThisTok->GetSingleRef();
3462 const ScSingleRefData& rRef2 = pThisTok->GetSingleRef2();
3463 if (rRef1 != pOtherTok->GetSingleRef())
3464 return NotEqual;
3466 if (rRef2 != pOtherTok->GetSingleRef2())
3467 return NotEqual;
3469 if (rRef1.IsRowRel())
3470 bInvariant = false;
3472 if (rRef2.IsRowRel())
3473 bInvariant = false;
3475 break;
3476 case formula::svDouble:
3478 if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
3479 return NotEqual;
3481 break;
3482 case formula::svString:
3484 if(pThisTok->GetString() != pOtherTok->GetString())
3485 return NotEqual;
3487 break;
3488 case formula::svIndex:
3490 if(pThisTok->GetIndex() != pOtherTok->GetIndex())
3491 return NotEqual;
3493 break;
3494 case formula::svByte:
3496 if(pThisTok->GetByte() != pOtherTok->GetByte())
3497 return NotEqual;
3499 break;
3500 default:
3505 return bInvariant ? EqualInvariant : EqualRelativeRef;
3508 bool ScFormulaCell::InterpretFormulaGroup()
3510 if (!ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
3511 return false;
3513 if (!mxGroup || !pCode)
3514 return false;
3516 if (mxGroup->meCalcState == sc::GroupCalcDisabled)
3517 return false;
3519 switch (pCode->GetVectorState())
3521 case FormulaVectorEnabled:
3522 case FormulaVectorCheckReference:
3523 // Good.
3524 break;
3525 case FormulaVectorDisabled:
3526 case FormulaVectorUnknown:
3527 default:
3528 // Not good.
3529 return false;
3532 // TODO : Disable invariant formula group interpretation for now in order
3533 // to get implicit intersection to work.
3534 if (mxGroup->mbInvariant && false)
3535 return InterpretInvariantFormulaGroup();
3537 if (mxGroup->meCalcState == sc::GroupCalcEnabled)
3539 ScTokenArray aCode;
3540 ScAddress aTopPos = aPos;
3541 aTopPos.SetRow(mxGroup->mpTopCell->aPos.Row());
3542 ScGroupTokenConverter aConverter(aCode, *pDocument, *this, mxGroup->mpTopCell->aPos);
3543 if (!aConverter.convert(*pCode))
3545 SAL_INFO("sc.opencl", "conversion of group " << this << " failed, disabling");
3546 mxGroup->meCalcState = sc::GroupCalcDisabled;
3547 return false;
3549 mxGroup->meCalcState = sc::GroupCalcRunning;
3550 if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, mxGroup->mpTopCell->aPos, mxGroup, aCode))
3552 SAL_INFO("sc.opencl", "interpreting group " << mxGroup << " (state " << mxGroup->meCalcState << ") failed, disabling");
3553 mxGroup->meCalcState = sc::GroupCalcDisabled;
3554 return false;
3556 mxGroup->meCalcState = sc::GroupCalcEnabled;
3558 else
3560 ScTokenArray aDummy;
3561 if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, mxGroup->mpTopCell->aPos, mxGroup, aDummy))
3563 SAL_INFO("sc.opencl", "interpreting group " << mxGroup << " (state " << mxGroup->meCalcState << ") failed, disabling");
3564 mxGroup->meCalcState = sc::GroupCalcDisabled;
3565 return false;
3569 return true;
3572 bool ScFormulaCell::InterpretInvariantFormulaGroup()
3574 if (pCode->GetVectorState() == FormulaVectorCheckReference)
3576 // An invariant group should only have absolute row references, and no
3577 // external references are allowed.
3579 ScTokenArray aCode;
3580 pCode->Reset();
3581 for (const formula::FormulaToken* p = pCode->First(); p; p = pCode->Next())
3583 const ScToken* pToken = static_cast<const ScToken*>(p);
3584 switch (pToken->GetType())
3586 case svSingleRef:
3588 ScSingleRefData aRef = pToken->GetSingleRef();
3589 ScAddress aRefPos = aRef.toAbs(aPos);
3590 formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefPos);
3591 if (!pNewToken)
3592 return false;
3594 aCode.AddToken(*pNewToken);
3596 break;
3597 case svDoubleRef:
3599 ScComplexRefData aRef = pToken->GetDoubleRef();
3600 ScRange aRefRange = aRef.toAbs(aPos);
3601 formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefRange);
3602 if (!pNewToken)
3603 return false;
3605 aCode.AddToken(*pNewToken);
3607 break;
3608 default:
3609 aCode.AddToken(*pToken);
3613 ScCompiler aComp(pDocument, aPos, aCode);
3614 aComp.SetGrammar(pDocument->GetGrammar());
3615 aComp.CompileTokenArray(); // Create RPN token array.
3616 ScInterpreter aInterpreter(this, pDocument, aPos, aCode);
3617 aInterpreter.Interpret();
3618 aResult.SetToken(aInterpreter.GetResultToken().get());
3620 else
3622 // Formula contains no references.
3623 ScInterpreter aInterpreter(this, pDocument, aPos, *pCode);
3624 aInterpreter.Interpret();
3625 aResult.SetToken(aInterpreter.GetResultToken().get());
3628 for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ )
3630 ScAddress aTmpPos = aPos;
3631 aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i);
3632 ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
3633 assert( pCell != NULL );
3635 // FIXME: this set of horrors is unclear to me ... certainly
3636 // the above GetCell is profoundly nasty & slow ...
3638 // Ensure the cell truly has a result:
3639 pCell->aResult = aResult;
3640 pCell->ResetDirty();
3641 pCell->SetChanged(true);
3644 return true;
3647 namespace {
3649 void startListeningArea(
3650 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const ScToken& rToken)
3652 const ScSingleRefData& rRef1 = rToken.GetSingleRef();
3653 const ScSingleRefData& rRef2 = rToken.GetSingleRef2();
3654 ScAddress aCell1 = rRef1.toAbs(rPos);
3655 ScAddress aCell2 = rRef2.toAbs(rPos);
3656 if (aCell1.IsValid() && aCell2.IsValid())
3658 if (rToken.GetOpCode() == ocColRowNameAuto)
3659 { // automagically
3660 if ( rRef1.IsColRel() )
3661 { // ColName
3662 aCell2.SetRow(MAXROW);
3664 else
3665 { // RowName
3666 aCell2.SetCol(MAXCOL);
3669 rDoc.StartListeningArea(ScRange(aCell1, aCell2), pCell);
3675 void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
3677 if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
3678 return;
3680 pDoc->SetDetectiveDirty(true); // It has changed something
3682 ScTokenArray* pArr = GetCode();
3683 if( pArr->IsRecalcModeAlways() )
3685 pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, this);
3686 SetNeedsListening( false);
3687 return;
3690 pArr->Reset();
3691 ScToken* t;
3692 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
3694 switch (t->GetType())
3696 case svSingleRef:
3698 ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3699 if (aCell.IsValid())
3700 pDoc->StartListeningCell(aCell, this);
3702 break;
3703 case svDoubleRef:
3704 startListeningArea(this, *pDoc, aPos, *t);
3705 break;
3706 default:
3707 ; // nothing
3710 SetNeedsListening( false);
3713 void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
3715 ScDocument& rDoc = rCxt.getDoc();
3717 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
3718 return;
3720 rDoc.SetDetectiveDirty(true); // It has changed something
3722 ScTokenArray* pArr = GetCode();
3723 if( pArr->IsRecalcModeAlways() )
3725 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, this);
3726 SetNeedsListening( false);
3727 return;
3730 pArr->Reset();
3731 ScToken* t;
3732 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
3734 switch (t->GetType())
3736 case svSingleRef:
3738 ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3739 if (aCell.IsValid())
3740 rDoc.StartListeningCell(rCxt, aCell, *this);
3742 break;
3743 case svDoubleRef:
3744 startListeningArea(this, rDoc, aPos, *t);
3745 break;
3746 default:
3747 ; // nothing
3750 SetNeedsListening( false);
3753 namespace {
3755 void endListeningArea(
3756 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const ScToken& rToken)
3758 const ScSingleRefData& rRef1 = rToken.GetSingleRef();
3759 const ScSingleRefData& rRef2 = rToken.GetSingleRef2();
3760 ScAddress aCell1 = rRef1.toAbs(rPos);
3761 ScAddress aCell2 = rRef2.toAbs(rPos);
3762 if (aCell1.IsValid() && aCell2.IsValid())
3764 if (rToken.GetOpCode() == ocColRowNameAuto)
3765 { // automagically
3766 if ( rRef1.IsColRel() )
3767 { // ColName
3768 aCell2.SetRow(MAXROW);
3770 else
3771 { // RowName
3772 aCell2.SetCol(MAXCOL);
3776 rDoc.EndListeningArea(ScRange(aCell1, aCell2), pCell);
3782 void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
3783 ScAddress aCellPos )
3785 if (pDoc->IsClipOrUndo() || IsInChangeTrack())
3786 return;
3788 pDoc->SetDetectiveDirty(true); // It has changed something
3790 if ( GetCode()->IsRecalcModeAlways() )
3792 pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, this );
3793 return;
3796 if (!pArr)
3798 pArr = GetCode();
3799 aCellPos = aPos;
3801 pArr->Reset();
3802 ScToken* t;
3803 while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
3805 switch (t->GetType())
3807 case svSingleRef:
3809 ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3810 if (aCell.IsValid())
3811 pDoc->EndListeningCell(aCell, this);
3813 break;
3814 case svDoubleRef:
3815 endListeningArea(this, *pDoc, aCellPos, *t);
3816 break;
3817 default:
3818 ; // nothing
3823 void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
3825 if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
3826 return;
3828 ScDocument& rDoc = rCxt.getDoc();
3829 rDoc.SetDetectiveDirty(true); // It has changed something
3831 if (pCode->IsRecalcModeAlways())
3833 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, this);
3834 return;
3837 pCode->Reset();
3838 ScToken* t;
3839 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
3841 switch (t->GetType())
3843 case svSingleRef:
3845 ScAddress aCell = t->GetSingleRef().toAbs(aPos);
3846 if (aCell.IsValid())
3847 rDoc.EndListeningCell(rCxt, aCell, *this);
3849 break;
3850 case svDoubleRef:
3851 endListeningArea(this, rDoc, aPos, *t);
3852 break;
3853 default:
3854 ; // nothing
3859 bool ScFormulaCell::IsShared() const
3861 return mxGroup.get() != NULL;
3864 bool ScFormulaCell::IsSharedTop() const
3866 if (!mxGroup)
3867 return false;
3869 return mxGroup->mpTopCell == this;
3872 SCROW ScFormulaCell::GetSharedTopRow() const
3874 return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1;
3877 SCROW ScFormulaCell::GetSharedLength() const
3879 return mxGroup ? mxGroup->mnLength : 0;
3882 ScTokenArray* ScFormulaCell::GetSharedCode()
3884 return mxGroup ? mxGroup->mpCode : NULL;
3887 const ScTokenArray* ScFormulaCell::GetSharedCode() const
3889 return mxGroup ? mxGroup->mpCode : NULL;
3892 bool ScFormulaCell::IsPostponedDirty() const
3894 return mbPostponedDirty;
3897 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */