Reorder ScFormulaCell members
[LibreOffice.git] / sc / source / core / data / formulacell.cxx
blob7b0f9e47b3fed338dd3cb0aa436980a249f07303
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <config_features.h>
22 #include <sal/config.h>
23 #include <sal/log.hxx>
25 #include <cassert>
26 #include <cstdlib>
28 #include <formulacell.hxx>
29 #include <grouptokenconverter.hxx>
31 #include <compiler.hxx>
32 #include <document.hxx>
33 #include <cellvalue.hxx>
34 #include <interpre.hxx>
35 #include <macromgr.hxx>
36 #include <refupdat.hxx>
37 #include <recursionhelper.hxx>
38 #include <docoptio.hxx>
39 #include <rangenam.hxx>
40 #include <rangelst.hxx>
41 #include <dbdata.hxx>
42 #include <progress.hxx>
43 #include <scmatrix.hxx>
44 #include <rechead.hxx>
45 #include <scitems.hxx>
46 #include <validat.hxx>
47 #include <editutil.hxx>
48 #include <chgtrack.hxx>
49 #include <tokenarray.hxx>
51 #include <comphelper/threadpool.hxx>
52 #include <editeng/editobj.hxx>
53 #include <tools/cpuid.hxx>
54 #include <formula/errorcodes.hxx>
55 #include <svl/intitem.hxx>
56 #include <o3tl/make_unique.hxx>
57 #include <formulagroup.hxx>
58 #include <listenercontext.hxx>
59 #include <types.hxx>
60 #include <scopetools.hxx>
61 #include <refupdatecontext.hxx>
62 #include <tokenstringcontext.hxx>
63 #include <refhint.hxx>
64 #include <listenerquery.hxx>
65 #include <listenerqueryids.hxx>
66 #include <grouparealistener.hxx>
67 #include <formulalogger.hxx>
68 #include <com/sun/star/sheet/FormulaLanguage.hpp>
70 #if HAVE_FEATURE_OPENCL
71 #include <opencl/openclwrapper.hxx>
72 #endif
74 #include <memory>
75 #include <map>
76 #include <vector>
78 using namespace formula;
80 #define DEBUG_CALCULATION 0
81 #if DEBUG_CALCULATION
82 static bool bDebugCalculationActive = false; // Set to true for global active init,
83 static ScAddress aDebugCalculationTriggerAddress(1,2,0); // or on cell Sheet1.B3, whatever you like
85 struct DebugCalculationEntry
87 ScAddress maPos;
88 OUString maResult;
89 const ScDocument* mpDoc;
90 sal_uInt32 mnGroup;
91 sal_uInt16 mnRecursion;
93 DebugCalculationEntry( const ScAddress& rPos, ScDocument* pDoc, sal_uInt32 nGroup ) :
94 maPos(rPos),
95 mpDoc(pDoc),
96 mnGroup(nGroup),
97 mnRecursion(pDoc->GetRecursionHelper().GetRecursionCount())
102 /** Debug/dump formula cell calculation chain.
103 Either, somewhere set aDC.mbActive=true, or
104 aDC.maTrigger=ScAddress(col,row,tab) of interest from where to start.
105 This does not work for deep recursion > MAXRECURSION, the results are
106 somewhat.. funny.. ;)
108 static struct DebugCalculation
110 std::vector< DebugCalculationEntry > mvPos;
111 std::vector< DebugCalculationEntry > mvResults;
112 ScAddress maTrigger;
113 sal_uInt32 mnGroup;
114 bool mbActive;
115 bool mbSwitchOff;
116 bool mbPrint;
117 bool mbPrintResults;
119 DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false),
120 mbPrint(true), mbPrintResults(false) {}
122 /** Print chain in encountered dependency order. */
123 void print() const
125 for (auto const& it : mvPos)
127 OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, it.mpDoc) +
128 " [" + OUString::number( it.mnRecursion) + "," + OUString::number( it.mnGroup) + "]");
129 fprintf( stderr, "%s -> ", aStr.toUtf8().getStr());
131 fprintf( stderr, "%s", "END\n");
134 /** Print chain results. */
135 void printResults() const
137 for (auto const& it : mvResults)
139 OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, it.mpDoc));
140 aStr += " (" + it.maResult + ")";
141 fprintf( stderr, "%s, ", aStr.toUtf8().getStr());
143 fprintf( stderr, "%s", "END\n");
146 void storeResult( const svl::SharedString& rStr )
148 if (mbActive && !mvPos.empty())
149 mvPos.back().maResult = "\"" + rStr.getString() + "\"";
152 void storeResult( const double& fVal )
154 if (mbActive && !mvPos.empty())
155 mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.', true);
158 void storeResultError( FormulaError nErr )
160 if (mbActive && !mvPos.empty())
161 mvPos.back().maResult = "Err:" + OUString::number( int( nErr ));
164 void enterGroup()
166 ++mnGroup;
169 void leaveGroup()
171 --mnGroup;
174 } aDC;
176 struct DebugCalculationStacker
178 DebugCalculationStacker( const ScAddress& rPos, ScDocument* pDoc )
180 if (!aDC.mbActive && rPos == aDC.maTrigger)
181 aDC.mbActive = aDC.mbSwitchOff = true;
182 if (aDC.mbActive)
184 aDC.mvPos.push_back( DebugCalculationEntry( rPos, pDoc, aDC.mnGroup));
185 aDC.mbPrint = true;
189 ~DebugCalculationStacker()
191 if (aDC.mbActive)
193 if (!aDC.mvPos.empty())
195 if (aDC.mbPrint)
197 aDC.print();
198 aDC.mbPrint = false;
200 if (aDC.mbPrintResults)
202 // Store results until final result is available, reversing order.
203 aDC.mvResults.push_back( aDC.mvPos.back());
205 aDC.mvPos.pop_back();
206 if (aDC.mbPrintResults && aDC.mvPos.empty())
208 aDC.printResults();
209 std::vector< DebugCalculationEntry >().swap( aDC.mvResults);
211 if (aDC.mbSwitchOff && aDC.mvPos.empty())
212 aDC.mbActive = false;
217 #endif
219 namespace {
221 // More or less arbitrary, of course all recursions must fit into available
222 // stack space (which is what on all systems we don't know yet?). Choosing a
223 // lower value may be better than trying a much higher value that also isn't
224 // sufficient but temporarily leads to high memory consumption. On the other
225 // hand, if the value fits all recursions, execution is quicker as no resumes
226 // are necessary. Could be made a configurable option.
227 // Allow for a year's calendar (366).
228 const sal_uInt16 MAXRECURSION = 400;
230 typedef SCCOLROW(*DimensionSelector)(const ScAddress&, const ScSingleRefData&);
232 SCCOLROW lcl_GetCol(const ScAddress& rPos, const ScSingleRefData& rData)
234 return rData.toAbs(rPos).Col();
237 SCCOLROW lcl_GetRow(const ScAddress& rPos, const ScSingleRefData& rData)
239 return rData.toAbs(rPos).Row();
242 SCCOLROW lcl_GetTab(const ScAddress& rPos, const ScSingleRefData& rData)
244 return rData.toAbs(rPos).Tab();
247 /** Check if both references span the same range in selected dimension.
249 bool
250 lcl_checkRangeDimension(
251 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
252 const DimensionSelector aWhich)
254 return aWhich(rPos, rRef1.Ref1) == aWhich(rPos, rRef2.Ref1) &&
255 aWhich(rPos, rRef1.Ref2) == aWhich(rPos, rRef2.Ref2);
258 bool
259 lcl_checkRangeDimensions(
260 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
261 bool& bCol, bool& bRow, bool& bTab)
263 const bool bSameCols(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetCol));
264 const bool bSameRows(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetRow));
265 const bool bSameTabs(lcl_checkRangeDimension(rPos, rRef1, rRef2, lcl_GetTab));
267 // Test if exactly two dimensions are equal
268 if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
270 bCol = !bSameCols;
271 bRow = !bSameRows;
272 bTab = !bSameTabs;
273 return true;
275 return false;
278 /** Check if references in given reference list can possibly
279 form a range. To do that, two of their dimensions must be the same.
281 bool
282 lcl_checkRangeDimensions(
283 const ScAddress& rPos,
284 const std::vector<formula::FormulaToken*>::const_iterator& rBegin,
285 const std::vector<formula::FormulaToken*>::const_iterator& rEnd,
286 bool& bCol, bool& bRow, bool& bTab)
288 std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin);
289 ++aCur;
290 const SingleDoubleRefProvider aRef(**rBegin);
291 bool bOk(false);
293 const SingleDoubleRefProvider aRefCur(**aCur);
294 bOk = lcl_checkRangeDimensions(rPos, aRef, aRefCur, bCol, bRow, bTab);
296 while (bOk && aCur != rEnd)
298 const SingleDoubleRefProvider aRefCur(**aCur);
299 bool bColTmp(false);
300 bool bRowTmp(false);
301 bool bTabTmp(false);
302 bOk = lcl_checkRangeDimensions(rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
303 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
304 ++aCur;
307 return bOk && aCur == rEnd;
310 class LessByReference
312 ScAddress const maPos;
313 DimensionSelector const maFunc;
314 public:
315 LessByReference(const ScAddress& rPos, const DimensionSelector& rFunc) :
316 maPos(rPos), maFunc(rFunc) {}
318 bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
320 const SingleDoubleRefProvider aRef1(*pRef1);
321 const SingleDoubleRefProvider aRef2(*pRef2);
322 return maFunc(maPos, aRef1.Ref1) < maFunc(maPos, aRef2.Ref1);
327 * Returns true if range denoted by token p2 starts immediately after range
328 * denoted by token p1. Dimension, in which the comparison takes place, is
329 * given by maFunc.
331 class AdjacentByReference
333 ScAddress const maPos;
334 DimensionSelector const maFunc;
335 public:
336 AdjacentByReference(const ScAddress& rPos, DimensionSelector aFunc) :
337 maPos(rPos), maFunc(aFunc) {}
339 bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
341 const SingleDoubleRefProvider aRef1(*p1);
342 const SingleDoubleRefProvider aRef2(*p2);
343 return maFunc(maPos, aRef2.Ref1) - maFunc(maPos, aRef1.Ref2) == 1;
347 bool
348 lcl_checkIfAdjacent(
349 const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
351 auto aBegin(rReferences.cbegin());
352 auto aEnd(rReferences.cend());
353 auto aBegin1(aBegin);
354 ++aBegin1;
355 --aEnd;
356 return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rPos, aWhich));
359 void
360 lcl_fillRangeFromRefList(
361 const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange)
363 const ScSingleRefData aStart(
364 SingleDoubleRefProvider(*rReferences.front()).Ref1);
365 rRange.aStart = aStart.toAbs(aPos);
366 const ScSingleRefData aEnd(
367 SingleDoubleRefProvider(*rReferences.back()).Ref2);
368 rRange.aEnd = aEnd.toAbs(aPos);
371 bool
372 lcl_refListFormsOneRange(
373 const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
374 ScRange& rRange)
376 if (rReferences.size() == 1)
378 lcl_fillRangeFromRefList(rPos, rReferences, rRange);
379 return true;
382 bool bCell(false);
383 bool bRow(false);
384 bool bTab(false);
385 if (lcl_checkRangeDimensions(rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
387 DimensionSelector aWhich;
388 if (bCell)
390 aWhich = lcl_GetCol;
392 else if (bRow)
394 aWhich = lcl_GetRow;
396 else if (bTab)
398 aWhich = lcl_GetTab;
400 else
402 OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
403 aWhich = lcl_GetRow; // initialize to avoid warning
406 // Sort the references by start of range
407 std::sort(rReferences.begin(), rReferences.end(), LessByReference(rPos, aWhich));
408 if (lcl_checkIfAdjacent(rPos, rReferences, aWhich))
410 lcl_fillRangeFromRefList(rPos, rReferences, rRange);
411 return true;
414 return false;
417 bool lcl_isReference(const FormulaToken& rToken)
419 return
420 rToken.GetType() == svSingleRef ||
421 rToken.GetType() == svDoubleRef;
424 void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc,
425 const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
427 ScRangeData* pRangeData = nullptr;
428 SCTAB nSheet = pToken->GetSheet();
429 sal_uInt16 nIndex = pToken->GetIndex();
430 if (!pOldDoc->CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true))
431 return; // nothing to do
433 if (!pRangeData)
435 // If this happened we have a real problem.
436 pToken->SetIndex(0);
437 OSL_FAIL("inserting the range name should not fail");
438 return;
441 pToken->SetIndex(nIndex);
442 pToken->SetSheet(nSheet);
445 void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
447 ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
448 if (!pOldDBCollection)
449 return;//strange error case, don't do anything
450 ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
451 ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
452 if (!pDBData)
453 return; //invalid index
454 OUString aDBName = pDBData->GetUpperName();
456 //search in new document
457 ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
458 if (!pNewDBCollection)
460 rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(&rNewDoc)));
461 pNewDBCollection = rNewDoc.GetDBCollection();
463 ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
464 ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
465 if (!pNewDBData)
467 pNewDBData = new ScDBData(*pDBData);
468 bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData));
469 assert(ins); (void)ins;
471 pToken->SetIndex(pNewDBData->GetIndex());
474 struct AreaListenerKey
476 ScRange const maRange;
477 bool const mbStartFixed;
478 bool const mbEndFixed;
480 AreaListenerKey( const ScRange& rRange, bool bStartFixed, bool bEndFixed ) :
481 maRange(rRange), mbStartFixed(bStartFixed), mbEndFixed(bEndFixed) {}
483 bool operator < ( const AreaListenerKey& r ) const
485 if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
486 return maRange.aStart.Tab() < r.maRange.aStart.Tab();
487 if (maRange.aStart.Col() != r.maRange.aStart.Col())
488 return maRange.aStart.Col() < r.maRange.aStart.Col();
489 if (maRange.aStart.Row() != r.maRange.aStart.Row())
490 return maRange.aStart.Row() < r.maRange.aStart.Row();
491 if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
492 return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
493 if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
494 return maRange.aEnd.Col() < r.maRange.aEnd.Col();
495 if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
496 return maRange.aEnd.Row() < r.maRange.aEnd.Row();
497 if (mbStartFixed != r.mbStartFixed)
498 return r.mbStartFixed;
499 if (mbEndFixed != r.mbEndFixed)
500 return r.mbEndFixed;
502 return false;
506 typedef std::map<AreaListenerKey, std::unique_ptr<sc::FormulaGroupAreaListener>> AreaListenersType;
510 struct ScFormulaCellGroup::Impl
512 AreaListenersType m_AreaListeners;
515 ScFormulaCellGroup::ScFormulaCellGroup() :
516 mpImpl(new Impl),
517 mnRefCount(0),
518 mpCode(nullptr),
519 mpTopCell(nullptr),
520 mnLength(0),
521 mnWeight(0),
522 mnFormatType(SvNumFormatType::NUMBER),
523 mbInvariant(false),
524 mbSubTotal(false),
525 mbPartOfCycle(false),
526 meCalcState(sc::GroupCalcEnabled)
530 ScFormulaCellGroup::~ScFormulaCellGroup()
532 delete mpCode;
535 void ScFormulaCellGroup::setCode( const ScTokenArray& rCode )
537 delete mpCode;
538 mpCode = rCode.Clone();
539 mbInvariant = mpCode->IsInvariant();
540 mpCode->GenHash();
543 void ScFormulaCellGroup::setCode( ScTokenArray* pCode )
545 delete mpCode;
546 mpCode = pCode; // takes ownership of the token array.
547 mpCode->Finalize(); // Reduce memory usage if needed.
548 mbInvariant = mpCode->IsInvariant();
549 mpCode->GenHash();
552 void ScFormulaCellGroup::compileCode(
553 ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
555 if (!mpCode)
556 return;
558 if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
560 bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
561 ScCompiler aComp(&rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
562 mbSubTotal = aComp.CompileTokenArray();
563 mnFormatType = aComp.GetNumFormatType();
565 else
567 mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
571 sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener(
572 ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
574 AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);
576 AreaListenersType::iterator it = mpImpl->m_AreaListeners.lower_bound(aKey);
577 if (it == mpImpl->m_AreaListeners.end() || mpImpl->m_AreaListeners.key_comp()(aKey, it->first))
579 // Insert a new one.
580 it = mpImpl->m_AreaListeners.insert(
581 it, std::make_pair(aKey, o3tl::make_unique<sc::FormulaGroupAreaListener>(
582 rRange, *(*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed)));
585 return it->second.get();
588 void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc )
590 AreaListenersType::iterator it = mpImpl->m_AreaListeners.begin(), itEnd = mpImpl->m_AreaListeners.end();
591 for (; it != itEnd; ++it)
593 sc::FormulaGroupAreaListener *const pListener = it->second.get();
594 ScRange aListenRange = pListener->getListeningRange();
595 // This "always listen" special range is never grouped.
596 bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
597 rDoc.EndListeningArea(aListenRange, bGroupListening, pListener);
600 mpImpl->m_AreaListeners.clear();
603 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos ) :
604 bDirty(false),
605 bTableOpDirty(false),
606 bChanged(false),
607 bRunning(false),
608 bCompile(false),
609 bSubTotal(false),
610 bIsIterCell(false),
611 bInChangeTrack(false),
612 bNeedListening(false),
613 mbNeedsNumberFormat(false),
614 mbAllowNumberFormatChange(false),
615 mbPostponedDirty(false),
616 mbIsExtRef(false),
617 mbSeenInPath(false),
618 cMatrixFlag(ScMatrixMode::NONE),
619 nSeenInIteration(0),
620 nFormatType(SvNumFormatType::NUMBER),
621 eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
622 pCode(new ScTokenArray),
623 pDocument(pDoc),
624 pPrevious(nullptr),
625 pNext(nullptr),
626 pPreviousTrack(nullptr),
627 pNextTrack(nullptr),
628 aPos(rPos)
632 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
633 const OUString& rFormula,
634 const FormulaGrammar::Grammar eGrammar,
635 ScMatrixMode cMatInd ) :
636 bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
637 bTableOpDirty( false ),
638 bChanged( false ),
639 bRunning( false ),
640 bCompile( false ),
641 bSubTotal( false ),
642 bIsIterCell( false ),
643 bInChangeTrack( false ),
644 bNeedListening( false ),
645 mbNeedsNumberFormat( false ),
646 mbAllowNumberFormatChange(false),
647 mbPostponedDirty(false),
648 mbIsExtRef(false),
649 mbSeenInPath(false),
650 cMatrixFlag ( cMatInd ),
651 nSeenInIteration(0),
652 nFormatType ( SvNumFormatType::NUMBER ),
653 eTempGrammar( eGrammar),
654 pCode( nullptr ),
655 pDocument( pDoc ),
656 pPrevious(nullptr),
657 pNext(nullptr),
658 pPreviousTrack(nullptr),
659 pNextTrack(nullptr),
660 aPos(rPos)
662 Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
663 if (!pCode)
664 // We need to have a non-NULL token array instance at all times.
665 pCode = new ScTokenArray;
668 ScFormulaCell::ScFormulaCell(
669 ScDocument* pDoc, const ScAddress& rPos, ScTokenArray* pArray,
670 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
671 bDirty( true ),
672 bTableOpDirty( false ),
673 bChanged( false ),
674 bRunning( false ),
675 bCompile( false ),
676 bSubTotal( false ),
677 bIsIterCell( false ),
678 bInChangeTrack( false ),
679 bNeedListening( false ),
680 mbNeedsNumberFormat( false ),
681 mbAllowNumberFormatChange(false),
682 mbPostponedDirty(false),
683 mbIsExtRef(false),
684 mbSeenInPath(false),
685 cMatrixFlag ( cMatInd ),
686 nSeenInIteration(0),
687 nFormatType ( SvNumFormatType::NUMBER ),
688 eTempGrammar( eGrammar),
689 pCode(pArray),
690 pDocument( pDoc ),
691 pPrevious(nullptr),
692 pNext(nullptr),
693 pPreviousTrack(nullptr),
694 pNextTrack(nullptr),
695 aPos(rPos)
697 assert(pArray); // Never pass a NULL pointer here.
699 pCode->Finalize(); // Reduce memory usage if needed.
701 // Generate RPN token array.
702 if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
704 ScCompiler aComp( pDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE );
705 bSubTotal = aComp.CompileTokenArray();
706 nFormatType = aComp.GetNumFormatType();
708 else
710 if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
711 bSubTotal = true;
714 if (bSubTotal)
715 pDocument->AddSubTotalCell(this);
717 pCode->GenHash();
720 ScFormulaCell::ScFormulaCell(
721 ScDocument* pDoc, const ScAddress& rPos, const ScTokenArray& rArray,
722 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
723 bDirty( true ),
724 bTableOpDirty( false ),
725 bChanged( false ),
726 bRunning( false ),
727 bCompile( false ),
728 bSubTotal( false ),
729 bIsIterCell( false ),
730 bInChangeTrack( false ),
731 bNeedListening( false ),
732 mbNeedsNumberFormat( false ),
733 mbAllowNumberFormatChange(false),
734 mbPostponedDirty(false),
735 mbIsExtRef(false),
736 mbSeenInPath(false),
737 cMatrixFlag ( cMatInd ),
738 nSeenInIteration(0),
739 nFormatType ( SvNumFormatType::NUMBER ),
740 eTempGrammar( eGrammar),
741 pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array
742 pDocument( pDoc ),
743 pPrevious(nullptr),
744 pNext(nullptr),
745 pPreviousTrack(nullptr),
746 pNextTrack(nullptr),
747 aPos(rPos)
749 // RPN array generation
750 if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
752 ScCompiler aComp( pDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE );
753 bSubTotal = aComp.CompileTokenArray();
754 nFormatType = aComp.GetNumFormatType();
756 else
758 if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) )
759 bSubTotal = true;
762 if (bSubTotal)
763 pDocument->AddSubTotalCell(this);
765 pCode->GenHash();
768 ScFormulaCell::ScFormulaCell(
769 ScDocument* pDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
770 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) :
771 mxGroup(xGroup),
772 bDirty(true),
773 bTableOpDirty( false ),
774 bChanged( false ),
775 bRunning( false ),
776 bCompile( false ),
777 bSubTotal(xGroup->mbSubTotal),
778 bIsIterCell( false ),
779 bInChangeTrack( false ),
780 bNeedListening( false ),
781 mbNeedsNumberFormat( false ),
782 mbAllowNumberFormatChange(false),
783 mbPostponedDirty(false),
784 mbIsExtRef(false),
785 mbSeenInPath(false),
786 cMatrixFlag ( cInd ),
787 nSeenInIteration(0),
788 nFormatType(xGroup->mnFormatType),
789 eTempGrammar( eGrammar),
790 pCode(xGroup->mpCode ? xGroup->mpCode : new ScTokenArray),
791 pDocument( pDoc ),
792 pPrevious(nullptr),
793 pNext(nullptr),
794 pPreviousTrack(nullptr),
795 pNextTrack(nullptr),
796 aPos(rPos)
798 if (bSubTotal)
799 pDocument->AddSubTotalCell(this);
802 ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) :
803 SvtListener(),
804 bDirty( rCell.bDirty ),
805 bTableOpDirty( false ),
806 bChanged( rCell.bChanged ),
807 bRunning( false ),
808 bCompile( rCell.bCompile ),
809 bSubTotal( rCell.bSubTotal ),
810 bIsIterCell( false ),
811 bInChangeTrack( false ),
812 bNeedListening( false ),
813 mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ),
814 mbAllowNumberFormatChange(false),
815 mbPostponedDirty(false),
816 mbIsExtRef(false),
817 mbSeenInPath(false),
818 cMatrixFlag ( rCell.cMatrixFlag ),
819 nSeenInIteration(0),
820 nFormatType( rCell.nFormatType ),
821 aResult( rCell.aResult ),
822 eTempGrammar( rCell.eTempGrammar),
823 pDocument( &rDoc ),
824 pPrevious(nullptr),
825 pNext(nullptr),
826 pPreviousTrack(nullptr),
827 pNextTrack(nullptr),
828 aPos(rPos)
830 pCode = rCell.pCode->Clone();
832 // set back any errors and recompile
833 // not in the Clipboard - it must keep the received error flag
834 // Special Length=0: as bad cells are generated, then they are also retained
835 if ( pCode->GetCodeError() != FormulaError::NONE && !pDocument->IsClipboard() && pCode->GetLen() )
837 pCode->SetCodeError( FormulaError::NONE );
838 bCompile = true;
840 // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
841 bool bCompileLater = false;
842 bool bClipMode = rCell.pDocument->IsClipboard();
844 //update ScNameTokens
845 if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
847 if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
849 bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default);
850 formula::FormulaToken* pToken = nullptr;
851 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
852 while((pToken = aIter.GetNextName())!= nullptr)
854 OpCode eOpCode = pToken->GetOpCode();
855 if (eOpCode == ocName)
856 adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
857 else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
858 adjustDBRange(pToken, rDoc, rCell.pDocument);
862 bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
863 if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
865 pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
868 pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, bCopyBetweenDocs );
871 if (!pDocument->IsClipOrUndo())
873 if (&pDocument->GetSharedStringPool() != &rCell.pDocument->GetSharedStringPool())
874 pCode->ReinternStrings( pDocument->GetSharedStringPool());
875 pCode->AdjustReferenceOnCopy( aPos);
878 if( !bCompile )
879 { // Name references with references and ColRowNames
880 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
881 formula::FormulaToken* t;
882 while ( ( t = aIter.GetNextReferenceOrName() ) != nullptr && !bCompile )
884 if ( t->IsExternalRef() )
886 // External name, cell, and area references.
887 bCompile = true;
889 else if ( t->GetType() == svIndex )
891 const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
892 if( pRangeData )
894 if( pRangeData->HasReferences() )
895 bCompile = true;
897 else
898 bCompile = true; // invalid reference!
900 else if ( t->GetOpCode() == ocColRowName )
902 bCompile = true; // new lookup needed
903 bCompileLater = bClipMode;
907 if( bCompile )
909 if ( !bCompileLater && bClipMode )
911 // Merging ranges needs the actual positions after UpdateReference.
912 // ColRowNames and TableRefs need new lookup after positions are
913 // adjusted.
914 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
915 pCode->HasOpCode( ocTableRef);
917 if ( !bCompileLater )
919 // bNoListening, not at all if in Clipboard/Undo,
920 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
921 CompileTokenArray( true );
925 if( nCloneFlags & ScCloneFlags::StartListening )
926 StartListeningTo( &rDoc );
928 if (bSubTotal)
929 pDocument->AddSubTotalCell(this);
932 ScFormulaCell::~ScFormulaCell()
934 pDocument->RemoveFromFormulaTrack( this );
935 pDocument->RemoveFromFormulaTree( this );
936 pDocument->RemoveSubTotalCell(this);
937 if (pCode->HasOpCode(ocMacro))
938 pDocument->GetMacroManager()->RemoveDependentCell(this);
940 if (pDocument->HasExternalRefManager())
941 pDocument->GetExternalRefManager()->removeRefCell(this);
943 if (!mxGroup || !mxGroup->mpCode)
944 // Formula token is not shared.
945 delete pCode;
948 ScFormulaCell* ScFormulaCell::Clone() const
950 return new ScFormulaCell(*this, *pDocument, aPos);
953 ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const
955 return new ScFormulaCell(*this, *pDocument, rPos, ScCloneFlags::Default);
958 size_t ScFormulaCell::GetHash() const
960 return pCode->GetHash();
963 ScFormulaVectorState ScFormulaCell::GetVectorState() const
965 return pCode->GetVectorState();
968 void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
969 const FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext ) const
971 if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
973 rBuffer = ScGlobal::GetErrorString(pCode->GetCodeError());
974 return;
976 else if( cMatrixFlag == ScMatrixMode::Reference )
978 // Reference to another cell that contains a matrix formula.
979 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
980 formula::FormulaToken* p = aIter.GetNextReferenceRPN();
981 if( p )
983 /* FIXME: original GetFormula() code obtained
984 * pCell only if (!IsInChangeTrack()),
985 * GetEnglishFormula() omitted that test.
986 * Can we live without in all cases? */
987 ScFormulaCell* pCell = nullptr;
988 ScSingleRefData& rRef = *p->GetSingleRef();
989 ScAddress aAbs = rRef.toAbs(aPos);
990 if (ValidAddress(aAbs))
991 pCell = pDocument->GetFormulaCell(aAbs);
993 if (pCell)
995 pCell->GetFormula( rBuffer, eGrammar, pContext );
996 return;
998 else
1000 ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, false, false, pContext );
1001 aComp.CreateStringFromTokenArray( rBuffer );
1004 else
1006 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1009 else
1011 ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, false, false, pContext );
1012 aComp.CreateStringFromTokenArray( rBuffer );
1015 rBuffer.insert( 0, '=');
1016 if( cMatrixFlag != ScMatrixMode::NONE )
1018 rBuffer.insert( 0, '{');
1019 rBuffer.append( '}');
1023 void ScFormulaCell::GetFormula( OUString& rFormula, const FormulaGrammar::Grammar eGrammar,
1024 const ScInterpreterContext* pContext ) const
1026 OUStringBuffer rBuffer( rFormula );
1027 GetFormula( rBuffer, eGrammar, pContext );
1028 rFormula = rBuffer.makeStringAndClear();
1031 OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, const ScInterpreterContext* pContext ) const
1033 OUStringBuffer aBuf;
1034 if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
1036 ScTokenArray aCode;
1037 aCode.AddToken( FormulaErrorToken( pCode->GetCodeError()));
1038 ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
1039 aComp.CreateStringFromTokenArray(aBuf);
1040 return aBuf.makeStringAndClear();
1042 else if( cMatrixFlag == ScMatrixMode::Reference )
1044 // Reference to another cell that contains a matrix formula.
1045 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
1046 formula::FormulaToken* p = aIter.GetNextReferenceRPN();
1047 if( p )
1049 /* FIXME: original GetFormula() code obtained
1050 * pCell only if (!IsInChangeTrack()),
1051 * GetEnglishFormula() omitted that test.
1052 * Can we live without in all cases? */
1053 ScFormulaCell* pCell = nullptr;
1054 ScSingleRefData& rRef = *p->GetSingleRef();
1055 ScAddress aAbs = rRef.toAbs(aPos);
1056 if (ValidAddress(aAbs))
1057 pCell = pDocument->GetFormulaCell(aAbs);
1059 if (pCell)
1061 return pCell->GetFormula(rCxt);
1063 else
1065 ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1066 aComp.CreateStringFromTokenArray(aBuf);
1069 else
1071 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1074 else
1076 ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1077 aComp.CreateStringFromTokenArray(aBuf);
1080 aBuf.insert( 0, '=');
1081 if( cMatrixFlag != ScMatrixMode::NONE )
1083 aBuf.insert( 0, '{');
1084 aBuf.append( '}');
1087 return aBuf.makeStringAndClear();
1090 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows )
1092 MaybeInterpret();
1094 const ScMatrix* pMat = nullptr;
1095 if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell &&
1096 ((pMat = aResult.GetToken().get()->GetMatrix()) != nullptr))
1097 pMat->GetDimensions( rCols, rRows );
1098 else
1100 rCols = 0;
1101 rRows = 0;
1105 void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; }
1106 void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; }
1108 void ScFormulaCell::SetNeedsDirty( bool bVar )
1110 mbPostponedDirty = bVar;
1113 void ScFormulaCell::SetNeedNumberFormat( bool bVal )
1115 mbNeedsNumberFormat = mbAllowNumberFormatChange = bVal;
1118 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
1119 const FormulaGrammar::Grammar eGrammar )
1121 if ( pDocument->IsClipOrUndo() )
1122 return;
1123 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1124 if ( bWasInFormulaTree )
1125 pDocument->RemoveFromFormulaTree( this );
1126 // pCode may not deleted for queries, but must be empty
1127 if ( pCode )
1128 pCode->Clear();
1129 ScTokenArray* pCodeOld = pCode;
1130 ScCompiler aComp( pDocument, aPos, eGrammar);
1131 pCode = aComp.CompileString( rFormula );
1132 delete pCodeOld;
1133 if( pCode->GetCodeError() == FormulaError::NONE )
1135 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1136 { // not recursive CompileTokenArray/Compile/CompileTokenArray
1137 if ( rFormula[0] == '=' )
1138 pCode->AddBad( rFormula.copy(1) );
1139 else
1140 pCode->AddBad( rFormula );
1142 bCompile = true;
1143 CompileTokenArray( bNoListening );
1145 else
1146 bChanged = true;
1148 if ( bWasInFormulaTree )
1149 pDocument->PutInFormulaTree( this );
1152 void ScFormulaCell::Compile(
1153 sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1155 if ( pDocument->IsClipOrUndo() )
1156 return;
1157 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1158 if ( bWasInFormulaTree )
1159 pDocument->RemoveFromFormulaTree( this );
1160 // pCode may not deleted for queries, but must be empty
1161 if ( pCode )
1162 pCode->Clear();
1163 ScTokenArray* pCodeOld = pCode;
1164 ScCompiler aComp(rCxt, aPos);
1165 pCode = aComp.CompileString( rFormula );
1166 delete pCodeOld;
1167 if( pCode->GetCodeError() == FormulaError::NONE )
1169 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1170 { // not recursive CompileTokenArray/Compile/CompileTokenArray
1171 if ( rFormula[0] == '=' )
1172 pCode->AddBad( rFormula.copy(1) );
1173 else
1174 pCode->AddBad( rFormula );
1176 bCompile = true;
1177 CompileTokenArray(rCxt, bNoListening);
1179 else
1180 bChanged = true;
1182 if ( bWasInFormulaTree )
1183 pDocument->PutInFormulaTree( this );
1186 void ScFormulaCell::CompileTokenArray( bool bNoListening )
1188 // Not already compiled?
1189 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1191 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
1193 else if( bCompile && !pDocument->IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
1195 // RPN length may get changed
1196 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1197 if ( bWasInFormulaTree )
1198 pDocument->RemoveFromFormulaTree( this );
1200 // Loading from within filter? No listening yet!
1201 if( pDocument->IsInsertingFromOtherDoc() )
1202 bNoListening = true;
1204 if( !bNoListening && pCode->GetCodeLen() )
1205 EndListeningTo( pDocument );
1206 ScCompiler aComp(pDocument, aPos, *pCode, pDocument->GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
1207 bSubTotal = aComp.CompileTokenArray();
1208 if( pCode->GetCodeError() == FormulaError::NONE )
1210 nFormatType = aComp.GetNumFormatType();
1211 bChanged = true;
1212 aResult.SetToken( nullptr);
1213 bCompile = false;
1214 if ( !bNoListening )
1215 StartListeningTo( pDocument );
1217 if ( bWasInFormulaTree )
1218 pDocument->PutInFormulaTree( this );
1220 if (bSubTotal)
1221 pDocument->AddSubTotalCell(this);
1225 void ScFormulaCell::CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNoListening )
1227 // Not already compiled?
1228 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1230 rCxt.setGrammar(eTempGrammar);
1231 Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
1233 else if( bCompile && !pDocument->IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
1235 // RPN length may get changed
1236 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1237 if ( bWasInFormulaTree )
1238 pDocument->RemoveFromFormulaTree( this );
1240 // Loading from within filter? No listening yet!
1241 if( pDocument->IsInsertingFromOtherDoc() )
1242 bNoListening = true;
1244 if( !bNoListening && pCode->GetCodeLen() )
1245 EndListeningTo( pDocument );
1246 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1247 bSubTotal = aComp.CompileTokenArray();
1248 if( pCode->GetCodeError() == FormulaError::NONE )
1250 nFormatType = aComp.GetNumFormatType();
1251 bChanged = true;
1252 aResult.SetToken( nullptr);
1253 bCompile = false;
1254 if ( !bNoListening )
1255 StartListeningTo( pDocument );
1257 if ( bWasInFormulaTree )
1258 pDocument->PutInFormulaTree( this );
1260 if (bSubTotal)
1261 pDocument->AddSubTotalCell(this);
1265 void ScFormulaCell::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
1267 if ( cMatrixFlag == ScMatrixMode::Reference )
1268 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1269 // just establish listeners
1270 StartListeningTo( pDocument );
1271 return ;
1274 // Error constant formula cell stays as is.
1275 if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
1276 return;
1278 // Compilation changes RPN count, remove and reinsert to FormulaTree if it
1279 // was in to update its count.
1280 bool bWasInFormulaTree = pDocument->IsInFormulaTree( this);
1281 if (bWasInFormulaTree)
1282 pDocument->RemoveFromFormulaTree( this);
1283 rCxt.setGrammar(eTempGrammar);
1284 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1285 OUString aFormula, aFormulaNmsp;
1286 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1287 pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
1288 rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() );
1289 // pCode may not deleted for queries, but must be empty
1290 pCode->Clear();
1292 bool bDoCompile = true;
1294 if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
1296 ScAddress aPreviousCell( aPos );
1297 aPreviousCell.IncRow( -1 );
1298 ScFormulaCell *pPreviousCell = pDocument->GetFormulaCell( aPreviousCell );
1299 if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
1301 // Build formula string using the tokens from the previous cell,
1302 // but use the current cell position.
1303 ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
1304 OUStringBuffer aShouldBeBuf;
1305 aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
1307 // The initial '=' is optional in ODFF.
1308 const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
1309 OUString aShouldBe = aShouldBeBuf.makeStringAndClear();
1310 if (aFormula.getLength() == aShouldBe.getLength() + nLeadingEqual &&
1311 aFormula.match( aShouldBe, nLeadingEqual))
1313 // Put them in the same formula group.
1314 ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
1315 if (!xGroup) // Last cell is not grouped yet. Start a new group.
1316 xGroup = pPreviousCell->CreateCellGroup(1, false);
1317 ++xGroup->mnLength;
1318 SetCellGroup( xGroup );
1320 // Do setup here based on previous cell.
1322 nFormatType = pPreviousCell->nFormatType;
1323 bSubTotal = pPreviousCell->bSubTotal;
1324 bChanged = true;
1325 bCompile = false;
1327 if (bSubTotal)
1328 pDocument->AddSubTotalCell(this);
1330 bDoCompile = false;
1331 pCode = pPreviousCell->pCode;
1332 if (pPreviousCell->mbIsExtRef)
1333 pDocument->GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
1338 if (bDoCompile)
1340 ScTokenArray* pCodeOld = pCode;
1341 pCode = aComp.CompileString( aFormula, aFormulaNmsp );
1342 delete pCodeOld;
1344 if( pCode->GetCodeError() == FormulaError::NONE )
1346 if ( !pCode->GetLen() )
1348 if ( !aFormula.isEmpty() && aFormula[0] == '=' )
1349 pCode->AddBad( aFormula.copy( 1 ) );
1350 else
1351 pCode->AddBad( aFormula );
1353 bSubTotal = aComp.CompileTokenArray();
1354 if( pCode->GetCodeError() == FormulaError::NONE )
1356 nFormatType = aComp.GetNumFormatType();
1357 bChanged = true;
1358 bCompile = false;
1361 if (bSubTotal)
1362 pDocument->AddSubTotalCell(this);
1364 else
1365 bChanged = true;
1368 // After loading, it must be known if ocDde/ocWebservice is in any formula
1369 // (for external links warning, CompileXML is called at the end of loading XML file)
1370 pDocument->CheckLinkFormulaNeedingCheck(*pCode);
1372 //volatile cells must be added here for import
1373 if( !pCode->IsRecalcModeNormal() || pCode->IsRecalcModeForced())
1375 // During load, only those cells that are marked explicitly dirty get
1376 // recalculated. So we need to set it dirty here.
1377 SetDirtyVar();
1378 pDocument->AppendToFormulaTrack(this);
1379 // Do not call TrackFormulas() here, not all listeners may have been
1380 // established, postponed until ScDocument::CompileXML() finishes.
1382 else if (bWasInFormulaTree)
1383 pDocument->PutInFormulaTree(this);
1386 void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
1388 bool bNewCompiled = false;
1389 // If a Calc 1.0-doc is read, we have a result, but no token array
1390 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1392 rCxt.setGrammar(eTempGrammar);
1393 Compile(rCxt, aResult.GetHybridFormula(), true);
1394 aResult.SetToken( nullptr);
1395 bDirty = true;
1396 bNewCompiled = true;
1398 // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1399 if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1401 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1402 bSubTotal = aComp.CompileTokenArray();
1403 nFormatType = aComp.GetNumFormatType();
1404 bDirty = true;
1405 bCompile = false;
1406 bNewCompiled = true;
1408 if (bSubTotal)
1409 pDocument->AddSubTotalCell(this);
1412 // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1413 // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1414 // We iron this out here for all systems, such that we also have an Err503 here.
1415 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
1417 OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1418 aResult.SetResultError( FormulaError::IllegalFPOperation );
1419 bDirty = true;
1422 // DoubleRefs for binary operators were always a Matrix before version v5.0.
1423 // Now this is only the case when in an array formula, otherwise it's an implicit intersection
1424 if ( ScDocument::GetSrcVersion() < SC_MATRIX_DOUBLEREF &&
1425 GetMatrixFlag() == ScMatrixMode::NONE && pCode->HasMatrixDoubleRefOps() )
1427 cMatrixFlag = ScMatrixMode::Formula;
1428 SetMatColsRows( 1, 1);
1431 // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1432 // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
1433 if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
1435 if (bStartListening)
1436 StartListeningTo(pDocument);
1438 if( !pCode->IsRecalcModeNormal() )
1439 bDirty = true;
1441 if ( pCode->IsRecalcModeAlways() )
1442 { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1443 // for each F9
1444 bDirty = true;
1446 // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1449 bool ScFormulaCell::MarkUsedExternalReferences()
1451 return pCode && pDocument->MarkUsedExternalReferences(*pCode, aPos);
1454 namespace {
1455 class RecursionCounter
1457 ScRecursionHelper& rRec;
1458 bool bStackedInIteration;
1459 #ifdef DBG_UTIL
1460 const ScFormulaCell* cell;
1461 #endif
1462 public:
1463 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
1464 : rRec(r)
1465 #ifdef DBG_UTIL
1466 , cell(p)
1467 #endif
1469 bStackedInIteration = rRec.IsDoingIteration();
1470 if (bStackedInIteration)
1471 rRec.GetRecursionInIterationStack().push( p);
1472 rRec.IncRecursionCount();
1474 ~RecursionCounter()
1476 rRec.DecRecursionCount();
1477 if (bStackedInIteration)
1479 #ifdef DBG_UTIL
1480 assert(rRec.GetRecursionInIterationStack().top() == cell);
1481 #endif
1482 rRec.GetRecursionInIterationStack().pop();
1488 void ScFormulaCell::Interpret()
1490 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
1492 ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
1494 if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel())
1496 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
1497 return;
1500 #if DEBUG_CALCULATION
1501 static bool bDebugCalculationInit = true;
1502 if (bDebugCalculationInit)
1504 aDC.maTrigger = aDebugCalculationTriggerAddress;
1505 aDC.mbPrintResults = true;
1506 bDebugCalculationInit = false;
1508 DebugCalculationStacker aDebugEntry( aPos, pDocument);
1509 #endif
1511 if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
1512 return; // no double/triple processing
1514 //FIXME:
1515 // If the call originates from a Reschedule in DdeLink update, leave dirty
1516 // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1517 if ( pDocument->IsInDdeLinkUpdate() )
1518 return;
1520 if (bRunning)
1522 if (!pDocument->GetDocOptions().IsIter())
1524 aResult.SetResultError( FormulaError::CircularReference );
1525 return;
1528 if (aResult.GetResultError() == FormulaError::CircularReference)
1529 aResult.SetResultError( FormulaError::NONE );
1531 // Start or add to iteration list.
1532 if (!rRecursionHelper.IsDoingIteration() ||
1533 !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
1534 rRecursionHelper.SetInIterationReturn( true);
1536 return;
1538 // no multiple interprets for GetErrCode, IsValue, GetValue and
1539 // different entry point recursions. Would also lead to premature
1540 // convergence in iterations.
1541 if (rRecursionHelper.GetIteration() && nSeenInIteration ==
1542 rRecursionHelper.GetIteration())
1543 return ;
1545 bool bOldRunning = bRunning;
1546 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1548 bRunning = true;
1549 rRecursionHelper.SetInRecursionReturn( true);
1551 else
1553 pDocument->IncInterpretLevel();
1555 #if DEBUG_CALCULATION
1556 aDC.enterGroup();
1557 #endif
1558 bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
1559 bool bGroupInterpreted = InterpretFormulaGroup();
1560 bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;
1562 #if DEBUG_CALCULATION
1563 aDC.leaveGroup();
1564 #endif
1565 if (!bGroupInterpreted)
1567 // Dependency calc inside InterpretFormulaGroup() failed due to
1568 // detection of a cycle and there are parent FG's in the cycle.
1569 // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
1570 if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
1572 pDocument->DecInterpretLevel();
1573 return;
1576 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
1577 InterpretTail( pDocument->GetNonThreadedContext(), SCITP_NORMAL);
1580 pDocument->DecInterpretLevel();
1583 // While leaving a recursion or iteration stack, insert its cells to the
1584 // recursion list in reverse order.
1585 if (rRecursionHelper.IsInReturn())
1587 if (rRecursionHelper.GetRecursionCount() > 0 ||
1588 !rRecursionHelper.IsDoingRecursion())
1589 rRecursionHelper.Insert( this, bOldRunning, aResult);
1590 bool bIterationFromRecursion = false;
1591 bool bResumeIteration = false;
1594 if ((rRecursionHelper.IsInIterationReturn() &&
1595 rRecursionHelper.GetRecursionCount() == 0 &&
1596 !rRecursionHelper.IsDoingIteration()) ||
1597 bIterationFromRecursion || bResumeIteration)
1599 bool & rDone = rRecursionHelper.GetConvergingReference();
1600 rDone = false;
1601 if (!bIterationFromRecursion && bResumeIteration)
1603 bResumeIteration = false;
1604 // Resuming iteration expands the range.
1605 ScFormulaRecursionList::const_iterator aOldStart(
1606 rRecursionHelper.GetLastIterationStart());
1607 rRecursionHelper.ResumeIteration();
1608 // Mark new cells being in iteration.
1609 for (ScFormulaRecursionList::const_iterator aIter(
1610 rRecursionHelper.GetIterationStart()); aIter !=
1611 aOldStart; ++aIter)
1613 ScFormulaCell* pIterCell = (*aIter).pCell;
1614 pIterCell->bIsIterCell = true;
1616 // Mark older cells dirty again, in case they converted
1617 // without accounting for all remaining cells in the circle
1618 // that weren't touched so far, e.g. conditional. Restore
1619 // backupped result.
1620 sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1621 for (ScFormulaRecursionList::const_iterator aIter(
1622 aOldStart); aIter !=
1623 rRecursionHelper.GetIterationEnd(); ++aIter)
1625 ScFormulaCell* pIterCell = (*aIter).pCell;
1626 if (pIterCell->nSeenInIteration == nIteration)
1628 if (!pIterCell->bDirty || aIter == aOldStart)
1630 pIterCell->aResult = (*aIter).aPreviousResult;
1632 --pIterCell->nSeenInIteration;
1634 pIterCell->bDirty = true;
1637 else
1639 bResumeIteration = false;
1640 // Close circle once. If 'this' is self-referencing only
1641 // (e.g. counter or self-adder) then it is already
1642 // implicitly closed.
1643 /* TODO: does this even make sense anymore? The last cell
1644 * added above with rRecursionHelper.Insert() should always
1645 * be 'this', shouldn't it? */
1646 ScFormulaCell* pLastCell = nullptr;
1647 if (rRecursionHelper.GetList().size() > 1 &&
1648 ((pLastCell = rRecursionHelper.GetList().back().pCell) != this))
1650 pDocument->IncInterpretLevel();
1651 pLastCell->InterpretTail(
1652 pDocument->GetNonThreadedContext(), SCITP_CLOSE_ITERATION_CIRCLE);
1653 pDocument->DecInterpretLevel();
1655 // Start at 1, init things.
1656 rRecursionHelper.StartIteration();
1657 // Mark all cells being in iteration. Reset results to
1658 // original values, formula cells have been interpreted
1659 // already, discard that step.
1660 for (ScFormulaRecursionList::const_iterator aIter(
1661 rRecursionHelper.GetIterationStart()); aIter !=
1662 rRecursionHelper.GetIterationEnd(); ++aIter)
1664 ScFormulaCell* pIterCell = (*aIter).pCell;
1665 pIterCell->aResult = (*aIter).aPreviousResult;
1666 pIterCell->bIsIterCell = true;
1669 bIterationFromRecursion = false;
1670 sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
1671 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1672 rRecursionHelper.IncIteration())
1674 rDone = false;
1675 bool bFirst = true;
1676 for ( ScFormulaRecursionList::iterator aIter(
1677 rRecursionHelper.GetIterationStart()); aIter !=
1678 rRecursionHelper.GetIterationEnd() &&
1679 !rRecursionHelper.IsInReturn(); ++aIter)
1681 ScFormulaCell* pIterCell = (*aIter).pCell;
1682 if (pIterCell->IsDirtyOrInTableOpDirty() &&
1683 rRecursionHelper.GetIteration() !=
1684 pIterCell->GetSeenInIteration())
1686 (*aIter).aPreviousResult = pIterCell->aResult;
1687 pDocument->IncInterpretLevel();
1688 pIterCell->InterpretTail( pDocument->GetNonThreadedContext(), SCITP_FROM_ITERATION);
1689 pDocument->DecInterpretLevel();
1691 if (bFirst)
1693 rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1694 bFirst = false;
1696 else if (rDone)
1698 rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1701 if (rRecursionHelper.IsInReturn())
1703 bResumeIteration = true;
1704 break; // for
1705 // Don't increment iteration.
1708 if (!bResumeIteration)
1710 if (rDone)
1712 for (ScFormulaRecursionList::const_iterator aIter(
1713 rRecursionHelper.GetIterationStart());
1714 aIter != rRecursionHelper.GetIterationEnd();
1715 ++aIter)
1717 ScFormulaCell* pIterCell = (*aIter).pCell;
1718 pIterCell->bIsIterCell = false;
1719 pIterCell->nSeenInIteration = 0;
1720 pIterCell->bRunning = (*aIter).bOldRunning;
1723 else
1725 for (ScFormulaRecursionList::const_iterator aIter(
1726 rRecursionHelper.GetIterationStart());
1727 aIter != rRecursionHelper.GetIterationEnd();
1728 ++aIter)
1730 ScFormulaCell* pIterCell = (*aIter).pCell;
1731 pIterCell->bIsIterCell = false;
1732 pIterCell->nSeenInIteration = 0;
1733 pIterCell->bRunning = (*aIter).bOldRunning;
1734 pIterCell->ResetDirty();
1735 // The difference to Excel is that Excel does not
1736 // produce an error for non-convergence thus a
1737 // delta of 0.001 still works to execute the
1738 // maximum number of iterations and display the
1739 // results no matter if the result anywhere reached
1740 // near delta, but also never indicates whether the
1741 // result actually makes sense in case of
1742 // non-counter context. Calc does check the delta
1743 // in every case. If we wanted to support what
1744 // Excel does then add another option "indicate
1745 // non-convergence error" (default on) and execute
1746 // the following block only if set.
1747 #if 1
1748 // If one cell didn't converge, all cells of this
1749 // circular dependency don't, no matter whether
1750 // single cells did.
1751 pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
1752 pIterCell->bChanged = true;
1753 #endif
1756 // End this iteration and remove entries.
1757 rRecursionHelper.EndIteration();
1758 bResumeIteration = rRecursionHelper.IsDoingIteration();
1761 if (rRecursionHelper.IsInRecursionReturn() &&
1762 rRecursionHelper.GetRecursionCount() == 0 &&
1763 !rRecursionHelper.IsDoingRecursion())
1765 bIterationFromRecursion = false;
1766 // Iterate over cells known so far, start with the last cell
1767 // encountered, inserting new cells if another recursion limit
1768 // is reached. Repeat until solved.
1769 rRecursionHelper.SetDoingRecursion( true);
1772 rRecursionHelper.SetInRecursionReturn( false);
1773 for (ScFormulaRecursionList::const_iterator aIter(
1774 rRecursionHelper.GetIterationStart());
1775 !rRecursionHelper.IsInReturn() && aIter !=
1776 rRecursionHelper.GetIterationEnd(); ++aIter)
1778 ScFormulaCell* pCell = (*aIter).pCell;
1779 if (pCell->IsDirtyOrInTableOpDirty())
1781 pDocument->IncInterpretLevel();
1782 pCell->InterpretTail( pDocument->GetNonThreadedContext(), SCITP_NORMAL);
1783 pDocument->DecInterpretLevel();
1784 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1785 pCell->bRunning = (*aIter).bOldRunning;
1788 } while (rRecursionHelper.IsInRecursionReturn());
1789 rRecursionHelper.SetDoingRecursion( false);
1790 if (rRecursionHelper.IsInIterationReturn())
1792 if (!bResumeIteration)
1793 bIterationFromRecursion = true;
1795 else if (bResumeIteration ||
1796 rRecursionHelper.IsDoingIteration())
1797 rRecursionHelper.GetList().erase(
1798 rRecursionHelper.GetIterationStart(),
1799 rRecursionHelper.GetLastIterationStart());
1800 else
1801 rRecursionHelper.Clear();
1803 } while (bIterationFromRecursion || bResumeIteration);
1806 #if DEBUG_CALCULATION
1807 FormulaError nErr = aResult.GetResultError();
1808 if (nErr != FormulaError::NONE)
1809 aDC.storeResultError( nErr);
1810 else if (aResult.IsValue())
1811 aDC.storeResult( aResult.GetDouble());
1812 else
1813 aDC.storeResult( aResult.GetString());
1814 #endif
1817 void ScFormulaCell::InterpretTail( ScInterpreterContext& rContext, ScInterpretTailParameter eTailParam )
1819 RecursionCounter aRecursionCounter( pDocument->GetRecursionHelper(), this);
1820 // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
1821 if(bIsIterCell)
1822 nSeenInIteration = pDocument->GetRecursionHelper().GetIteration();
1823 if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1825 // #i11719# no RPN and no error and no token code but result string present
1826 // => interpretation of this cell during name-compilation and unknown names
1827 // => can't exchange underlying code array in CompileTokenArray() /
1828 // Compile() because interpreter's token iterator would crash or pCode
1829 // would be deleted twice if this cell was interpreted during
1830 // compilation.
1831 // This should only be a temporary condition and, since we set an
1832 // error, if ran into it again we'd bump into the dirty-clearing
1833 // condition further down.
1834 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1836 pCode->SetCodeError( FormulaError::NoCode );
1837 // This is worth an assertion; if encountered in daily work
1838 // documents we might need another solution. Or just confirm correctness.
1839 return;
1841 CompileTokenArray();
1844 if( pCode->GetCodeLen() && pDocument )
1846 std::unique_ptr<ScInterpreter> pInterpreter(new ScInterpreter( this, pDocument, rContext, aPos, *pCode ));
1847 FormulaError nOldErrCode = aResult.GetResultError();
1848 if ( nSeenInIteration == 0 )
1849 { // Only the first time
1850 // With bChanged=false, if a newly compiled cell has a result of
1851 // 0.0, no change is detected and the cell will not be repainted.
1852 // bChanged = false;
1853 aResult.SetResultError( FormulaError::NONE );
1856 switch ( aResult.GetResultError() )
1858 case FormulaError::CircularReference : // will be determined again if so
1859 aResult.SetResultError( FormulaError::NONE );
1860 break;
1861 default: break;
1864 bool bOldRunning = bRunning;
1865 bRunning = true;
1866 pInterpreter->Interpret();
1867 if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE)
1869 if (nSeenInIteration > 0)
1870 --nSeenInIteration; // retry when iteration is resumed
1872 if ( aResult.GetType() == formula::svUnknown )
1873 aResult.SetToken( pInterpreter->GetResultToken().get() );
1875 return;
1877 bRunning = bOldRunning;
1879 // #i102616# For single-sheet saving consider only content changes, not format type,
1880 // because format type isn't set on loading (might be changed later)
1881 bool bContentChanged = false;
1883 // Do not create a HyperLink() cell if the formula results in an error.
1884 if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
1885 pCode->SetHyperLink(false);
1887 if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
1889 bChanged = true;
1891 if (pInterpreter->GetError() == FormulaError::RetryCircular)
1893 // Array formula matrix calculation corner case. Keep dirty
1894 // state, do not remove from formula tree or anything else, but
1895 // store FormulaError::CircularReference in case this cell does not get
1896 // recalculated.
1897 aResult.SetResultError( FormulaError::CircularReference);
1898 return;
1901 ResetDirty();
1904 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1906 bool bIsValue = aResult.IsValue(); // the previous type
1907 // Did it converge?
1908 if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
1909 pInterpreter->GetNumResult() - aResult.GetDouble()) <=
1910 pDocument->GetDocOptions().GetIterEps()) ||
1911 (!bIsValue && pInterpreter->GetResultType() == svString &&
1912 pInterpreter->GetStringResult() == aResult.GetString()))
1914 // A convergence in the first iteration doesn't necessarily
1915 // mean that it's done, it may be as not all related cells
1916 // of a circle changed their values yet. If the set really
1917 // converges it will do so also during the next iteration. This
1918 // fixes situations like of #i44115#. If this wasn't wanted an
1919 // initial "uncalculated" value would be needed for all cells
1920 // of a circular dependency => graph needed before calculation.
1921 if (nSeenInIteration > 1 ||
1922 pDocument->GetDocOptions().GetIterCount() == 1)
1924 ResetDirty();
1929 // New error code?
1930 if( pInterpreter->GetError() != nOldErrCode )
1932 bChanged = true;
1933 // bContentChanged only has to be set if the file content would be changed
1934 if ( aResult.GetCellResultType() != svUnknown )
1935 bContentChanged = true;
1938 ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
1940 // For IF() and other jumps or changed formatted source data the result
1941 // format may change for different runs, e.g. =IF(B1,B1) with first
1942 // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
1943 // displayed as TRUE. Do not force a general format though if
1944 // mbNeedsNumberFormat is set (because there was a general format..).
1945 // Note that nFormatType may be out of sync here if a format was
1946 // applied or cleared after the last run, but obtaining the current
1947 // format always just to check would be expensive. There may be
1948 // cases where the format should be changed but is not. If that turns
1949 // out to be a real problem then obtain the current format type after
1950 // the initial check when needed.
1951 bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
1952 !SvNumberFormatter::IsCompatible( nFormatType, pInterpreter->GetRetFormatType()));
1954 // We have some requirements additionally to IsCompatible().
1955 // * Do not apply a NumberFormat::LOGICAL if the result value is not
1956 // 1.0 or 0.0
1957 // * Do not override an already set numeric number format if the result
1958 // is of type NumberFormat::LOGICAL, it could be user applied.
1959 // On the other hand, for an empty jump path instead of FALSE an
1960 // unexpected for example 0% could be displayed. YMMV.
1961 // * Never override a non-standard number format that indicates user
1962 // applied.
1963 // * NumberFormat::TEXT does not force a change.
1964 if (bForceNumberFormat)
1966 sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1967 const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
1968 if (nRetType == SvNumFormatType::LOGICAL)
1970 double fVal;
1971 if ((fVal = aNewResult.GetDouble()) != 1.0 && fVal != 0.0)
1972 bForceNumberFormat = false;
1973 else
1975 nOldFormatIndex = pDocument->GetNumberFormat( rContext, aPos);
1976 nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
1977 switch (nFormatType)
1979 case SvNumFormatType::PERCENT:
1980 case SvNumFormatType::CURRENCY:
1981 case SvNumFormatType::SCIENTIFIC:
1982 case SvNumFormatType::FRACTION:
1983 bForceNumberFormat = false;
1984 break;
1985 case SvNumFormatType::NUMBER:
1986 if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
1987 bForceNumberFormat = false;
1988 break;
1989 default: break;
1993 else if (nRetType == SvNumFormatType::TEXT)
1995 bForceNumberFormat = false;
1997 if (bForceNumberFormat)
1999 if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
2001 nOldFormatIndex = pDocument->GetNumberFormat( rContext, aPos);
2002 nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2004 if (nOldFormatIndex !=
2005 ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
2006 bForceNumberFormat = false;
2010 if( mbNeedsNumberFormat || bForceNumberFormat )
2012 bool bSetFormat = true;
2013 const SvNumFormatType nOldFormatType = nFormatType;
2014 nFormatType = pInterpreter->GetRetFormatType();
2015 sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
2017 if (nFormatType == SvNumFormatType::TEXT)
2019 // Don't set text format as hard format.
2020 bSetFormat = false;
2022 else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
2024 // In a matrix range do not set an (inherited) logical format
2025 // as hard format if the value does not represent a strict TRUE
2026 // or FALSE value. But do set for a top left error value so
2027 // following matrix cells can inherit for non-error values.
2028 // This solves a problem with IF() expressions in array context
2029 // where incidentally the top left element results in logical
2030 // type but some others don't. It still doesn't solve the
2031 // reverse case though, where top left is not logical type but
2032 // some other elements should be. We'd need to transport type
2033 // or format information on arrays.
2034 StackVar eNewCellResultType = aNewResult.GetCellResultType();
2035 if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
2037 double fVal;
2038 if (eNewCellResultType != svDouble)
2040 bSetFormat = false;
2041 nFormatType = nOldFormatType; // that? or number?
2043 else if ((fVal = aNewResult.GetDouble()) != 1.0 && fVal != 0.0)
2045 bSetFormat = false;
2046 nFormatType = SvNumFormatType::NUMBER;
2051 if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)))
2052 nFormatIndex = ScGlobal::GetStandardFormat(*rContext.GetFormatTable(),
2053 nFormatIndex, nFormatType);
2055 // Do not replace a General format (which was the reason why
2056 // mbNeedsNumberFormat was set) with a General format.
2057 // 1. setting a format has quite some overhead in the
2058 // ScPatternAttr/ScAttrArray handling, even if identical.
2059 // 2. the General formats may be of different locales.
2060 // XXX if mbNeedsNumberFormat was set even if the current format
2061 // was not General then we'd have to obtain the current format here
2062 // and check at least the types.
2063 if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)))
2065 // set number format explicitly
2066 if (!pDocument->IsThreadedGroupCalcInProgress())
2067 pDocument->SetNumberFormat( aPos, nFormatIndex );
2068 else
2070 // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work
2071 // to the main thread. Since thread calculations operate on formula groups,
2072 // it's enough to store just the row.
2073 DelayedSetNumberFormat data = { aPos.Row(), nFormatIndex };
2074 rContext.maDelayedSetNumberFormat.push_back( data );
2076 bChanged = true;
2079 mbNeedsNumberFormat = false;
2082 // In case of changes just obtain the result, no temporary and
2083 // comparison needed anymore.
2084 if (bChanged)
2086 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
2087 // Also handle special cases of initial results after loading.
2088 if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
2090 StackVar eOld = aResult.GetCellResultType();
2091 StackVar eNew = aNewResult.GetCellResultType();
2092 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
2094 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
2095 // -> no change
2097 else
2099 if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
2100 eOld = svString; // ScHybridCellToken has a valid GetString method
2102 // #i106045# use approxEqual to compare with stored value
2103 bContentChanged = (eOld != eNew ||
2104 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
2105 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2109 aResult.SetToken( pInterpreter->GetResultToken().get() );
2111 else
2113 StackVar eOld = aResult.GetCellResultType();
2114 StackVar eNew = aNewResult.GetCellResultType();
2115 bChanged = (eOld != eNew ||
2116 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
2117 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2119 // #i102616# handle special cases of initial results after loading
2120 // (only if the sheet is still marked unchanged)
2121 if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) )
2123 if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
2124 ((eOld == svHybridCell) &&
2125 eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
2126 (eOld == svDouble && eNew == svDouble &&
2127 rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
2129 // no change, see above
2131 else
2132 bContentChanged = true;
2135 aResult.Assign( aNewResult);
2138 // Precision as shown?
2139 if ( aResult.IsValue() && pInterpreter->GetError() == FormulaError::NONE
2140 && pDocument->GetDocOptions().IsCalcAsShown()
2141 && nFormatType != SvNumFormatType::DATE
2142 && nFormatType != SvNumFormatType::TIME
2143 && nFormatType != SvNumFormatType::DATETIME )
2145 sal_uInt32 nFormat = pDocument->GetNumberFormat( rContext, aPos );
2146 aResult.SetDouble( pDocument->RoundValueAsShown(
2147 aResult.GetDouble(), nFormat, &rContext));
2149 if (eTailParam == SCITP_NORMAL)
2151 ResetDirty();
2153 if( aResult.GetMatrix() )
2155 // If the formula wasn't entered as a matrix formula, live on with
2156 // the upper left corner and let reference counting delete the matrix.
2157 if( cMatrixFlag != ScMatrixMode::Formula && !pCode->IsHyperLink() )
2158 aResult.SetToken( aResult.GetCellResultToken().get());
2160 if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) )
2162 // Coded double error may occur via filter import.
2163 FormulaError nErr = GetDoubleErrorValue( aResult.GetDouble());
2164 aResult.SetResultError( nErr);
2165 bChanged = bContentChanged = true;
2168 if (bContentChanged && pDocument->IsStreamValid(aPos.Tab()))
2170 // pass bIgnoreLock=true, because even if called from pending row height update,
2171 // a changed result must still reset the stream flag
2172 pDocument->SetStreamValid(aPos.Tab(), false, true);
2174 if ( !pDocument->IsThreadedGroupCalcInProgress() && !pCode->IsRecalcModeAlways() )
2175 pDocument->RemoveFromFormulaTree( this );
2177 // FORCED cells also immediately tested for validity (start macro possibly)
2179 if ( pCode->IsRecalcModeForced() )
2181 sal_uLong nValidation = pDocument->GetAttr(
2182 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
2183 if ( nValidation )
2185 const ScValidationData* pData = pDocument->GetValidationEntry( nValidation );
2186 ScRefCellValue aTmpCell(this);
2187 if ( pData && !pData->IsDataValid(aTmpCell, aPos))
2188 pData->DoCalcError( this );
2192 // Reschedule slows the whole thing down considerably, thus only execute on percent change
2193 if (!pDocument->IsThreadedGroupCalcInProgress())
2195 ScProgress *pProgress = ScProgress::GetInterpretProgress();
2196 if (pProgress && pProgress->Enabled())
2198 pProgress->SetStateCountDownOnPercent(
2199 pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE );
2202 switch (pInterpreter->GetVolatileType())
2204 case ScInterpreter::VOLATILE:
2205 // Volatile via built-in volatile functions. No actions needed.
2206 break;
2207 case ScInterpreter::VOLATILE_MACRO:
2208 // The formula contains a volatile macro.
2209 pCode->SetExclusiveRecalcModeAlways();
2210 pDocument->PutInFormulaTree(this);
2211 StartListeningTo(pDocument);
2212 break;
2213 case ScInterpreter::NOT_VOLATILE:
2214 if (pCode->IsRecalcModeAlways())
2216 // The formula was previously volatile, but no more.
2217 EndListeningTo(pDocument);
2218 pCode->SetExclusiveRecalcModeNormal();
2220 else
2222 // non-volatile formula. End listening to the area in case
2223 // it's listening due to macro module change.
2224 pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
2226 pDocument->RemoveFromFormulaTree(this);
2227 break;
2228 default:
2233 else
2235 // Cells with compiler errors should not be marked dirty forever
2236 OSL_ENSURE( pCode->GetCodeError() != FormulaError::NONE, "no RPN code and no errors ?!?!" );
2237 ResetDirty();
2241 void ScFormulaCell::HandleStuffAfterParallelCalculation()
2243 if( pCode->GetCodeLen() && pDocument )
2245 if ( !pCode->IsRecalcModeAlways() )
2246 pDocument->RemoveFromFormulaTree( this );
2248 std::unique_ptr<ScInterpreter> pInterpreter(new ScInterpreter( this, pDocument, pDocument->GetNonThreadedContext(), aPos, *pCode ));
2250 switch (pInterpreter->GetVolatileType())
2252 case ScInterpreter::VOLATILE_MACRO:
2253 // The formula contains a volatile macro.
2254 pCode->SetExclusiveRecalcModeAlways();
2255 pDocument->PutInFormulaTree(this);
2256 StartListeningTo(pDocument);
2257 break;
2258 case ScInterpreter::NOT_VOLATILE:
2259 if (pCode->IsRecalcModeAlways())
2261 // The formula was previously volatile, but no more.
2262 EndListeningTo(pDocument);
2263 pCode->SetExclusiveRecalcModeNormal();
2265 else
2267 // non-volatile formula. End listening to the area in case
2268 // it's listening due to macro module change.
2269 pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
2271 pDocument->RemoveFromFormulaTree(this);
2272 break;
2273 default:
2279 void ScFormulaCell::SetCompile( bool bVal )
2281 bCompile = bVal;
2284 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows )
2286 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst();
2287 if (pMat)
2288 pMat->SetMatColsRows( nCols, nRows );
2289 else if (nCols || nRows)
2291 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2292 // Setting the new token actually forces an empty result at this top
2293 // left cell, so have that recalculated.
2294 SetDirty();
2298 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2300 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken();
2301 if (pMat)
2302 pMat->GetMatColsRows( nCols, nRows);
2303 else
2305 nCols = 0;
2306 nRows = 0;
2310 void ScFormulaCell::SetInChangeTrack( bool bVal )
2312 bInChangeTrack = bVal;
2315 void ScFormulaCell::Notify( const SfxHint& rHint )
2317 if (pDocument->IsInDtorClear())
2318 return;
2320 const SfxHintId nHint = rHint.GetId();
2321 if (nHint == SfxHintId::ScReference)
2323 const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2325 switch (rRefHint.getType())
2327 case sc::RefHint::Moved:
2329 // One of the references has moved.
2331 const sc::RefMovedHint& rRefMoved = static_cast<const sc::RefMovedHint&>(rRefHint);
2332 if (!IsShared() || IsSharedTop())
2334 sc::RefUpdateResult aRes = pCode->MoveReference(aPos, rRefMoved.getContext());
2335 if (aRes.mbNameModified)
2337 // RPN token needs to be re-generated.
2338 bCompile = true;
2339 CompileTokenArray();
2340 SetDirtyVar();
2344 break;
2345 case sc::RefHint::ColumnReordered:
2347 const sc::RefColReorderHint& rRefColReorder =
2348 static_cast<const sc::RefColReorderHint&>(rRefHint);
2349 if (!IsShared() || IsSharedTop())
2350 pCode->MoveReferenceColReorder(
2351 aPos, rRefColReorder.getTab(),
2352 rRefColReorder.getStartRow(),
2353 rRefColReorder.getEndRow(),
2354 rRefColReorder.getColMap());
2356 break;
2357 case sc::RefHint::RowReordered:
2359 const sc::RefRowReorderHint& rRefRowReorder =
2360 static_cast<const sc::RefRowReorderHint&>(rRefHint);
2361 if (!IsShared() || IsSharedTop())
2362 pCode->MoveReferenceRowReorder(
2363 aPos, rRefRowReorder.getTab(),
2364 rRefRowReorder.getStartColumn(),
2365 rRefRowReorder.getEndColumn(),
2366 rRefRowReorder.getRowMap());
2368 break;
2369 case sc::RefHint::StartListening:
2371 StartListeningTo( pDocument);
2373 break;
2374 case sc::RefHint::StopListening:
2376 EndListeningTo( pDocument);
2378 break;
2379 default:
2383 return;
2386 if ( pDocument->GetHardRecalcState() == ScDocument::HardRecalcState::OFF )
2388 if (nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged))
2390 bool bForceTrack = false;
2391 if ( nHint == SfxHintId::ScTableOpDirty )
2393 bForceTrack = !bTableOpDirty;
2394 if ( !bTableOpDirty )
2396 pDocument->AddTableOpFormulaCell( this );
2397 bTableOpDirty = true;
2400 else
2402 bForceTrack = !bDirty;
2403 SetDirtyVar();
2405 // Don't remove from FormulaTree to put in FormulaTrack to
2406 // put in FormulaTree again and again, only if necessary.
2407 // Any other means except ScRecalcMode::ALWAYS by which a cell could
2408 // be in FormulaTree if it would notify other cells through
2409 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2410 // Yes. The new TableOpDirty made it necessary to have a
2411 // forced mode where formulas may still be in FormulaTree from
2412 // TableOpDirty but have to notify dependents for normal dirty.
2413 if ( (bForceTrack || !pDocument->IsInFormulaTree( this )
2414 || pCode->IsRecalcModeAlways())
2415 && !pDocument->IsInFormulaTrack( this ) )
2416 pDocument->AppendToFormulaTrack( this );
2421 void ScFormulaCell::Query( SvtListener::QueryBase& rQuery ) const
2423 switch (rQuery.getId())
2425 case SC_LISTENER_QUERY_FORMULA_GROUP_POS:
2427 sc::RefQueryFormulaGroup& rRefQuery =
2428 static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2429 if (IsShared())
2430 rRefQuery.add(aPos);
2432 break;
2433 default:
2438 void ScFormulaCell::SetDirty( bool bDirtyFlag )
2440 if (IsInChangeTrack())
2441 return;
2443 if ( pDocument->GetHardRecalcState() != ScDocument::HardRecalcState::OFF )
2445 SetDirtyVar();
2446 pDocument->SetStreamValid(aPos.Tab(), false);
2447 return;
2450 // Avoid multiple formula tracking in Load() and in CompileAll()
2451 // after CopyScenario() and CopyBlockFromClip().
2452 // If unconditional formula tracking is needed, set bDirty=false
2453 // before calling SetDirty(), for example in CompileTokenArray().
2454 if ( !bDirty || mbPostponedDirty || !pDocument->IsInFormulaTree( this ) )
2456 if( bDirtyFlag )
2457 SetDirtyVar();
2458 pDocument->AppendToFormulaTrack( this );
2460 // While loading a document listeners have not been established yet.
2461 // Tracking would remove this cell from the FormulaTrack and add it to
2462 // the FormulaTree, once in there it would be assumed that its
2463 // dependents already had been tracked and it would be skipped on a
2464 // subsequent notify. Postpone tracking until all listeners are set.
2465 if (!pDocument->IsImportingXML())
2466 pDocument->TrackFormulas();
2469 pDocument->SetStreamValid(aPos.Tab(), false);
2472 void ScFormulaCell::SetDirtyVar()
2474 bDirty = true;
2475 mbPostponedDirty = false;
2476 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2478 mxGroup->meCalcState = sc::GroupCalcEnabled;
2479 mxGroup->mbPartOfCycle = false;
2482 // mark the sheet of this cell to be calculated
2483 //#FIXME do we need to revert this remnant of old fake vba events? pDocument->AddCalculateTable( aPos.Tab() );
2486 void ScFormulaCell::SetDirtyAfterLoad()
2488 bDirty = true;
2489 if ( pDocument->GetHardRecalcState() == ScDocument::HardRecalcState::OFF )
2490 pDocument->PutInFormulaTree( this );
2493 void ScFormulaCell::ResetTableOpDirtyVar()
2495 bTableOpDirty = false;
2498 void ScFormulaCell::SetTableOpDirty()
2500 if ( !IsInChangeTrack() )
2502 if ( pDocument->GetHardRecalcState() != ScDocument::HardRecalcState::OFF )
2503 bTableOpDirty = true;
2504 else
2506 if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) )
2508 if ( !bTableOpDirty )
2510 pDocument->AddTableOpFormulaCell( this );
2511 bTableOpDirty = true;
2513 pDocument->AppendToFormulaTrack( this );
2514 pDocument->TrackFormulas( SfxHintId::ScTableOpDirty );
2520 void ScFormulaCell::SetResultDouble( double n )
2522 aResult.SetDouble(n);
2525 void ScFormulaCell::SetResultToken( const formula::FormulaToken* pToken )
2527 aResult.SetToken(pToken);
2530 svl::SharedString ScFormulaCell::GetResultString() const
2532 return aResult.GetString();
2535 bool ScFormulaCell::HasHybridStringResult() const
2537 return aResult.GetType() == formula::svHybridCell && !aResult.GetString().isEmpty();
2540 void ScFormulaCell::SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL )
2542 aResult.SetMatrix(nCols, nRows, pMat, pUL);
2545 void ScFormulaCell::SetErrCode( FormulaError n )
2547 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2548 * used whether it is solely for transport of a simple result error and get
2549 * rid of that abuse. */
2550 pCode->SetCodeError( n );
2551 // Hard set errors are transported as result type value per convention,
2552 // e.g. via clipboard. ScFormulaResult::IsValue() and
2553 // ScFormulaResult::GetDouble() handle that.
2554 aResult.SetResultError( n );
2557 void ScFormulaCell::SetResultError( FormulaError n )
2559 aResult.SetResultError( n );
2562 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits )
2564 if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
2565 SetDirtyVar();
2566 if ( nBits & ScRecalcMode::ONLOAD_ONCE )
2567 { // OnLoadOnce is used only to set Dirty after filter import.
2568 nBits = (nBits & ~ScRecalcMode::EMask) | ScRecalcMode::NORMAL;
2570 pCode->AddRecalcMode( nBits );
2573 void ScFormulaCell::SetHybridDouble( double n )
2575 aResult.SetHybridDouble( n);
2578 void ScFormulaCell::SetHybridString( const svl::SharedString& r )
2580 aResult.SetHybridString( r);
2583 void ScFormulaCell::SetHybridEmptyDisplayedAsString()
2585 aResult.SetHybridEmptyDisplayedAsString();
2588 void ScFormulaCell::SetHybridFormula( const OUString& r,
2589 const formula::FormulaGrammar::Grammar eGrammar )
2591 aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2594 const OUString& ScFormulaCell::GetHybridFormula() const
2596 return aResult.GetHybridFormula();
2599 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2600 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2602 OUString aCellString;
2604 Color* pColor;
2606 // Cell Text uses the Cell format while the URL uses
2607 // the default format for the type.
2608 const sal_uInt32 nCellFormat = pDocument->GetNumberFormat( aPos );
2609 SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
2611 const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
2613 if ( IsValue() )
2615 double fValue = GetValue();
2616 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2618 else
2620 aCellString = GetString().getString();
2621 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2623 ScConstMatrixRef xMat( aResult.GetMatrix());
2624 if (xMat)
2626 // determine if the matrix result is a string or value.
2627 if (!xMat->IsValue(0, 1))
2628 rURL = xMat->GetString(0, 1).getString();
2629 else
2630 pFormatter->GetOutputString(
2631 xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2634 if(rURL.isEmpty())
2636 if(IsValue())
2637 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2638 else
2639 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2643 bool ScFormulaCell::IsMultilineResult()
2645 if (!IsValue())
2646 return aResult.IsMultiline();
2647 return false;
2650 bool ScFormulaCell::IsHyperLinkCell() const
2652 return pCode && pCode->IsHyperLink();
2655 std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
2657 OUString aCellText;
2658 OUString aURL;
2659 GetURLResult( aURL, aCellText );
2661 return ScEditUtil::CreateURLObjectFromURL( *pDocument, aURL, aCellText );
2664 bool ScFormulaCell::IsEmpty()
2666 MaybeInterpret();
2667 return aResult.GetCellResultType() == formula::svEmptyCell;
2670 bool ScFormulaCell::IsEmptyDisplayedAsString()
2672 MaybeInterpret();
2673 return aResult.IsEmptyDisplayedAsString();
2676 bool ScFormulaCell::IsValue()
2678 MaybeInterpret();
2679 return aResult.IsValue();
2682 bool ScFormulaCell::IsValueNoError()
2684 MaybeInterpret();
2685 if (pCode->GetCodeError() != FormulaError::NONE)
2686 return false;
2688 return aResult.IsValueNoError();
2691 bool ScFormulaCell::IsValueNoError() const
2693 if (NeedsInterpret())
2694 // false if the cell is dirty & needs to be interpreted.
2695 return false;
2697 if (pCode->GetCodeError() != FormulaError::NONE)
2698 return false;
2700 return aResult.IsValueNoError();
2703 double ScFormulaCell::GetValue()
2705 MaybeInterpret();
2706 return GetRawValue();
2709 svl::SharedString ScFormulaCell::GetString()
2711 MaybeInterpret();
2712 return GetRawString();
2715 double ScFormulaCell::GetRawValue() const
2717 if ((pCode->GetCodeError() == FormulaError::NONE) &&
2718 aResult.GetResultError() == FormulaError::NONE)
2719 return aResult.GetDouble();
2720 return 0.0;
2723 svl::SharedString ScFormulaCell::GetRawString() const
2725 if ((pCode->GetCodeError() == FormulaError::NONE) &&
2726 aResult.GetResultError() == FormulaError::NONE)
2727 return aResult.GetString();
2729 return svl::SharedString::getEmptyString();
2732 const ScMatrix* ScFormulaCell::GetMatrix()
2734 if ( pDocument->GetAutoCalc() )
2736 if( IsDirtyOrInTableOpDirty()
2737 // Was stored !bDirty but an accompanying matrix cell was bDirty?
2738 || (!bDirty && cMatrixFlag == ScMatrixMode::Formula && !aResult.GetMatrix()))
2739 Interpret();
2741 return aResult.GetMatrix().get();
2744 bool ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
2746 switch ( cMatrixFlag )
2748 case ScMatrixMode::Formula :
2749 rPos = aPos;
2750 return true;
2751 case ScMatrixMode::Reference :
2753 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
2754 formula::FormulaToken* t = aIter.GetNextReferenceRPN();
2755 if( t )
2757 ScSingleRefData& rRef = *t->GetSingleRef();
2758 ScAddress aAbs = rRef.toAbs(aPos);
2759 if (ValidAddress(aAbs))
2761 rPos = aAbs;
2762 return true;
2766 break;
2767 default: break;
2769 return false;
2772 sc::MatrixEdge ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) const
2774 switch ( cMatrixFlag )
2776 case ScMatrixMode::Formula :
2777 case ScMatrixMode::Reference :
2779 static thread_local SCCOL nC;
2780 static thread_local SCROW nR;
2781 ScAddress aOrg;
2782 if ( !GetMatrixOrigin( aOrg ) )
2783 return sc::MatrixEdge::Nothing;
2784 if ( aOrg != rOrgPos )
2785 { // First time or a different matrix than last time.
2786 rOrgPos = aOrg;
2787 const ScFormulaCell* pFCell;
2788 if ( cMatrixFlag == ScMatrixMode::Reference )
2789 pFCell = pDocument->GetFormulaCell(aOrg);
2790 else
2791 pFCell = this; // this ScMatrixMode::Formula
2792 // There's only one this, don't compare pFCell==this.
2793 if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
2795 pFCell->GetMatColsRows( nC, nR );
2796 if ( nC == 0 || nR == 0 )
2798 // No ScMatrixFormulaCellToken available yet, calculate new.
2799 nC = 1;
2800 nR = 1;
2801 ScAddress aTmpOrg;
2802 ScFormulaCell* pCell;
2803 ScAddress aAdr( aOrg );
2804 aAdr.IncCol();
2805 bool bCont = true;
2808 pCell = pDocument->GetFormulaCell(aAdr);
2809 if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2810 pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
2812 nC++;
2813 aAdr.IncCol();
2815 else
2816 bCont = false;
2817 } while ( bCont );
2818 aAdr = aOrg;
2819 aAdr.IncRow();
2820 bCont = true;
2823 pCell = pDocument->GetFormulaCell(aAdr);
2824 if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2825 pCell->GetMatrixOrigin(aTmpOrg) && aTmpOrg == aOrg)
2827 nR++;
2828 aAdr.IncRow();
2830 else
2831 bCont = false;
2832 } while ( bCont );
2834 const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2837 else
2839 #if OSL_DEBUG_LEVEL > 0
2840 SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
2841 << aPos.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, pDocument)
2842 << ", MatOrg: "
2843 << aOrg.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, pDocument) );
2844 #endif
2845 return sc::MatrixEdge::Nothing;
2848 // here we are, healthy and clean, somewhere in between
2849 SCCOL dC = aPos.Col() - aOrg.Col();
2850 SCROW dR = aPos.Row() - aOrg.Row();
2851 sc::MatrixEdge nEdges = sc::MatrixEdge::Nothing;
2852 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2854 if ( dC == 0 )
2855 nEdges |= sc::MatrixEdge::Left;
2856 if ( dC+1 == nC )
2857 nEdges |= sc::MatrixEdge::Right;
2858 if ( dR == 0 )
2859 nEdges |= sc::MatrixEdge::Top;
2860 if ( dR+1 == nR )
2861 nEdges |= sc::MatrixEdge::Bottom;
2862 if ( nEdges == sc::MatrixEdge::Nothing )
2863 nEdges = sc::MatrixEdge::Inside;
2865 else
2867 SAL_WARN( "sc", "broken Matrix, Pos: "
2868 << aPos.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, pDocument)
2869 << ", MatOrg: "
2870 << aOrg.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, pDocument)
2871 << ", MatCols: " << static_cast<sal_Int32>( nC )
2872 << ", MatRows: " << static_cast<sal_Int32>( nR )
2873 << ", DiffCols: " << static_cast<sal_Int32>( dC )
2874 << ", DiffRows: " << static_cast<sal_Int32>( dR ));
2876 return nEdges;
2878 default:
2879 return sc::MatrixEdge::Nothing;
2883 FormulaError ScFormulaCell::GetErrCode()
2885 MaybeInterpret();
2887 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2888 * and not also abused for signaling other error conditions we could bail
2889 * out even before attempting to interpret broken code. */
2890 FormulaError nErr = pCode->GetCodeError();
2891 if (nErr != FormulaError::NONE)
2892 return nErr;
2893 return aResult.GetResultError();
2896 FormulaError ScFormulaCell::GetRawError()
2898 FormulaError nErr = pCode->GetCodeError();
2899 if (nErr != FormulaError::NONE)
2900 return nErr;
2901 return aResult.GetResultError();
2904 bool ScFormulaCell::GetErrorOrValue( FormulaError& rErr, double& rVal )
2906 MaybeInterpret();
2908 rErr = pCode->GetCodeError();
2909 if (rErr != FormulaError::NONE)
2910 return true;
2912 return aResult.GetErrorOrDouble(rErr, rVal);
2915 sc::FormulaResultValue ScFormulaCell::GetResult()
2917 MaybeInterpret();
2919 FormulaError nErr = pCode->GetCodeError();
2920 if (nErr != FormulaError::NONE)
2921 return sc::FormulaResultValue(nErr);
2923 return aResult.GetResult();
2926 sc::FormulaResultValue ScFormulaCell::GetResult() const
2928 FormulaError nErr = pCode->GetCodeError();
2929 if (nErr != FormulaError::NONE)
2930 return sc::FormulaResultValue(nErr);
2932 return aResult.GetResult();
2935 bool ScFormulaCell::HasOneReference( ScRange& r ) const
2937 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
2938 formula::FormulaToken* p = aIter.GetNextReferenceRPN();
2939 if( p && !aIter.GetNextReferenceRPN() ) // only one!
2941 SingleDoubleRefProvider aProv( *p );
2942 r.aStart = aProv.Ref1.toAbs(aPos);
2943 r.aEnd = aProv.Ref2.toAbs(aPos);
2944 return true;
2946 else
2947 return false;
2950 bool
2951 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
2953 /* If there appears just one reference in the formula, it's the same
2954 as HasOneReference(). If there are more of them, they can denote
2955 one range if they are (sole) arguments of one function.
2956 Union of these references must form one range and their
2957 intersection must be empty set.
2960 // Detect the simple case of exactly one reference in advance without all
2961 // overhead.
2962 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
2963 // work again, where the function does not have only references.
2964 if (HasOneReference( rRange))
2965 return true;
2967 // Get first reference, if any
2968 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
2969 formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
2970 if (pFirstReference)
2972 // Collect all consecutive references, starting by the one
2973 // already found
2974 std::vector<formula::FormulaToken*> aReferences;
2975 aReferences.push_back(pFirstReference);
2976 FormulaToken* pToken(aIter.NextRPN());
2977 FormulaToken* pFunction(nullptr);
2978 while (pToken)
2980 if (lcl_isReference(*pToken))
2982 aReferences.push_back(pToken);
2983 pToken = aIter.NextRPN();
2985 else
2987 if (pToken->IsFunction())
2989 pFunction = pToken;
2991 break;
2994 if (pFunction && !aIter.GetNextReferenceRPN()
2995 && (pFunction->GetParamCount() == aReferences.size()))
2997 return lcl_refListFormsOneRange(aPos, aReferences, rRange);
3000 return false;
3003 ScFormulaCell::RelNameRef ScFormulaCell::HasRelNameReference() const
3005 RelNameRef eRelNameRef = RelNameRef::NONE;
3006 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
3007 formula::FormulaToken* t;
3008 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
3010 switch (t->GetType())
3012 case formula::svSingleRef:
3013 if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
3014 eRelNameRef = RelNameRef::SINGLE;
3015 break;
3016 case formula::svDoubleRef:
3017 if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
3018 // May originate from individual cell names, in which case
3019 // it needs recompilation.
3020 return RelNameRef::DOUBLE;
3021 /* TODO: have an extra flag at ScComplexRefData if range was
3022 * extended? or too cumbersome? might narrow recompilation to
3023 * only needed cases.
3024 * */
3025 break;
3026 default:
3027 ; // nothing
3030 return eRelNameRef;
3033 bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext& rCxt )
3035 if (rCxt.meMode != URM_INSDEL)
3036 // Just in case...
3037 return false;
3039 if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
3040 // No movement.
3041 return false;
3043 if (!rCxt.maRange.In(aPos))
3044 return false;
3046 // This formula cell itself is being shifted during cell range
3047 // insertion or deletion. Update its position.
3048 ScAddress aErrorPos( ScAddress::UNINITIALIZED );
3049 if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos))
3051 assert(!"can't move ScFormulaCell");
3054 return true;
3057 namespace {
3060 * Check if we need to re-compile column or row names.
3062 bool checkCompileColRowName(
3063 const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
3064 const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
3066 switch (rCxt.meMode)
3068 case URM_INSDEL:
3070 if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
3071 return false;
3073 formula::FormulaTokenArrayPlainIterator aIter(rCode);
3074 formula::FormulaToken* t;
3075 ScRangePairList* pColList = rDoc.GetColNameRanges();
3076 ScRangePairList* pRowList = rDoc.GetRowNameRanges();
3077 while ((t = aIter.GetNextColRowName()) != nullptr)
3079 ScSingleRefData& rRef = *t->GetSingleRef();
3080 if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
3081 { // ColName
3082 ScAddress aAdr = rRef.toAbs(aPos);
3083 ScRangePair* pR = pColList->Find( aAdr );
3084 if ( pR )
3085 { // defined
3086 if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
3087 return true;
3089 else
3090 { // on the fly
3091 if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
3092 return true;
3095 if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
3096 { // RowName
3097 ScAddress aAdr = rRef.toAbs(aPos);
3098 ScRangePair* pR = pRowList->Find( aAdr );
3099 if ( pR )
3100 { // defined
3101 if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
3102 return true;
3104 else
3105 { // on the fly
3106 if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
3107 return true;
3112 break;
3113 case URM_MOVE:
3114 { // Recomplie for Move/D&D when ColRowName was moved or this Cell
3115 // points to one and was moved.
3116 bool bMoved = (aPos != aOldPos);
3117 if (bMoved)
3118 return true;
3120 formula::FormulaTokenArrayPlainIterator aIter(rCode);
3121 const formula::FormulaToken* t = aIter.GetNextColRowName();
3122 for (; t; t = aIter.GetNextColRowName())
3124 const ScSingleRefData& rRef = *t->GetSingleRef();
3125 ScAddress aAbs = rRef.toAbs(aPos);
3126 if (ValidAddress(aAbs))
3128 if (rCxt.maRange.In(aAbs))
3129 return true;
3133 break;
3134 case URM_COPY:
3135 return bValChanged;
3136 default:
3140 return false;
3143 void setOldCodeToUndo(
3144 ScDocument* pUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
3146 // Copy the cell to aUndoPos, which is its current position in the document,
3147 // so this works when UpdateReference is called before moving the cells
3148 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
3149 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
3151 // If there is already a formula cell in the undo document, don't overwrite it,
3152 // the first (oldest) is the important cell.
3153 if (pUndoDoc->GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3154 return;
3156 ScFormulaCell* pFCell =
3157 new ScFormulaCell(
3158 pUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(), eTempGrammar, cMatrixFlag);
3160 pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3161 pUndoDoc->SetFormulaCell(aUndoPos, pFCell);
3166 bool ScFormulaCell::UpdateReferenceOnShift(
3167 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3169 if (rCxt.meMode != URM_INSDEL)
3170 // Just in case...
3171 return false;
3173 bool bCellStateChanged = false;
3174 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3175 if ( pUndoCellPos )
3176 aUndoPos = *pUndoCellPos;
3177 ScAddress aOldPos( aPos );
3178 bCellStateChanged = UpdatePosOnShift(rCxt);
3180 // Check presence of any references or column row names.
3181 bool bHasRefs = pCode->HasReferences();
3182 bool bHasColRowNames = false;
3183 if (!bHasRefs)
3185 bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3186 bHasRefs = bHasRefs || bHasColRowNames;
3188 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3190 if (!bHasRefs && !bOnRefMove)
3191 // This formula cell contains no references, nor needs recalculating
3192 // on reference update. Bail out.
3193 return bCellStateChanged;
3195 std::unique_ptr<ScTokenArray> pOldCode;
3196 if (pUndoDoc)
3197 pOldCode.reset(pCode->Clone());
3199 bool bValChanged = false;
3200 bool bRefModified = false;
3201 bool bRecompile = bCompile;
3203 if (bHasRefs)
3205 // Update cell or range references.
3206 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
3207 bRefModified = aRes.mbReferenceModified;
3208 bValChanged = aRes.mbValueChanged;
3209 if (aRes.mbNameModified)
3210 bRecompile = true;
3213 if (bValChanged || bRefModified)
3214 bCellStateChanged = true;
3216 if (bOnRefMove)
3217 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3218 bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
3220 bool bNewListening = false;
3221 bool bInDeleteUndo = false;
3223 if (bHasRefs)
3225 // Upon Insert ColRowNames have to be recompiled in case the
3226 // insertion occurs right in front of the range.
3227 if (bHasColRowNames && !bRecompile)
3228 bRecompile = checkCompileColRowName(rCxt, *pDocument, *pCode, aOldPos, aPos, bValChanged);
3230 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
3231 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3233 // RelNameRefs are always moved
3234 bool bHasRelName = false;
3235 if (!bRecompile)
3237 RelNameRef eRelNameRef = HasRelNameReference();
3238 bHasRelName = (eRelNameRef != RelNameRef::NONE);
3239 bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
3241 // Reference changed and new listening needed?
3242 // Except in Insert/Delete without specialties.
3243 bNewListening = (bRefModified || bRecompile
3244 || (bValChanged && bInDeleteUndo) || bHasRelName);
3246 if ( bNewListening )
3247 EndListeningTo(pDocument, pOldCode.get(), aOldPos);
3250 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3251 bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
3253 if (pUndoDoc && (bValChanged || bOnRefMove))
3254 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3256 bCompile |= bRecompile;
3257 if (bCompile)
3259 CompileTokenArray( bNewListening ); // no Listening
3260 bNeedDirty = true;
3263 if ( !bInDeleteUndo )
3264 { // In ChangeTrack Delete-Reject listeners are established in
3265 // InsertCol/InsertRow
3266 if ( bNewListening )
3268 // Inserts/Deletes re-establish listeners after all
3269 // UpdateReference calls.
3270 // All replaced shared formula listeners have to be
3271 // established after an Insert or Delete. Do nothing here.
3272 SetNeedsListening( true);
3276 if (bNeedDirty)
3277 { // Cut off references, invalid or similar?
3278 // Postpone SetDirty() until all listeners have been re-established in
3279 // Inserts/Deletes.
3280 mbPostponedDirty = true;
3283 return bCellStateChanged;
3286 bool ScFormulaCell::UpdateReferenceOnMove(
3287 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3289 if (rCxt.meMode != URM_MOVE)
3290 return false;
3292 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3293 if ( pUndoCellPos )
3294 aUndoPos = *pUndoCellPos;
3295 ScAddress aOldPos( aPos );
3297 bool bCellInMoveTarget = rCxt.maRange.In(aPos);
3299 if ( bCellInMoveTarget )
3301 // The cell is being moved or copied to a new position. I guess the
3302 // position has been updated prior to this call? Determine
3303 // its original position before the move which will be used to adjust
3304 // relative references later.
3305 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3308 // Check presence of any references or column row names.
3309 bool bHasRefs = pCode->HasReferences();
3310 bool bHasColRowNames = false;
3311 if (!bHasRefs)
3313 bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3314 bHasRefs = bHasRefs || bHasColRowNames;
3316 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3318 if (!bHasRefs && !bOnRefMove)
3319 // This formula cell contains no references, nor needs recalculating
3320 // on reference update. Bail out.
3321 return false;
3323 bool bCellStateChanged = false;
3324 std::unique_ptr<ScTokenArray> pOldCode;
3325 if (pUndoDoc)
3326 pOldCode.reset(pCode->Clone());
3328 bool bValChanged = false;
3329 bool bRefModified = false;
3331 if (bHasRefs)
3333 // Update cell or range references.
3334 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3335 bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3336 bValChanged = aRes.mbValueChanged;
3337 if (aRes.mbNameModified)
3338 // Re-compile to get the RPN token regenerated to reflect updated names.
3339 bCompile = true;
3342 if (bValChanged || bRefModified)
3343 bCellStateChanged = true;
3345 if (bOnRefMove)
3346 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3347 bOnRefMove = (bValChanged || (aPos != aOldPos));
3349 bool bColRowNameCompile = false;
3350 bool bHasRelName = false;
3351 bool bNewListening = false;
3352 bool bInDeleteUndo = false;
3354 if (bHasRefs)
3356 // Upon Insert ColRowNames have to be recompiled in case the
3357 // insertion occurs right in front of the range.
3358 if (bHasColRowNames)
3359 bColRowNameCompile = checkCompileColRowName(rCxt, *pDocument, *pCode, aOldPos, aPos, bValChanged);
3361 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
3362 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3364 // RelNameRefs are always moved
3365 RelNameRef eRelNameRef = HasRelNameReference();
3366 bHasRelName = (eRelNameRef != RelNameRef::NONE);
3367 bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
3368 // Reference changed and new listening needed?
3369 // Except in Insert/Delete without specialties.
3370 bNewListening = (bRefModified || bColRowNameCompile
3371 || bValChanged || bHasRelName)
3372 // #i36299# Don't duplicate action during cut&paste / drag&drop
3373 // on a cell in the range moved, start/end listeners is done
3374 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3375 && !(pDocument->IsInsertingFromOtherDoc() && rCxt.maRange.In(aPos));
3377 if ( bNewListening )
3378 EndListeningTo(pDocument, pOldCode.get(), aOldPos);
3381 bool bNeedDirty = false;
3382 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3383 if ( bRefModified || bColRowNameCompile ||
3384 (bValChanged && bHasRelName ) || bOnRefMove)
3385 bNeedDirty = true;
3387 if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
3388 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3390 bValChanged = false;
3392 bCompile = (bCompile || bValChanged || bColRowNameCompile);
3393 if ( bCompile )
3395 CompileTokenArray( bNewListening ); // no Listening
3396 bNeedDirty = true;
3399 if ( !bInDeleteUndo )
3400 { // In ChangeTrack Delete-Reject listeners are established in
3401 // InsertCol/InsertRow
3402 if ( bNewListening )
3404 StartListeningTo( pDocument );
3408 if (bNeedDirty)
3409 { // Cut off references, invalid or similar?
3410 sc::AutoCalcSwitch aACSwitch(*pDocument, false);
3411 SetDirty();
3414 return bCellStateChanged;
3417 bool ScFormulaCell::UpdateReferenceOnCopy(
3418 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3420 if (rCxt.meMode != URM_COPY)
3421 return false;
3423 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3424 if ( pUndoCellPos )
3425 aUndoPos = *pUndoCellPos;
3426 ScAddress aOldPos( aPos );
3428 if (rCxt.maRange.In(aPos))
3430 // The cell is being moved or copied to a new position. I guess the
3431 // position has been updated prior to this call? Determine
3432 // its original position before the move which will be used to adjust
3433 // relative references later.
3434 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3437 // Check presence of any references or column row names.
3438 bool bHasRefs = pCode->HasReferences();
3439 bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3440 bHasRefs = bHasRefs || bHasColRowNames;
3441 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3443 if (!bHasRefs && !bOnRefMove)
3444 // This formula cell contains no references, nor needs recalculating
3445 // on reference update. Bail out.
3446 return false;
3448 std::unique_ptr<ScTokenArray> pOldCode;
3449 if (pUndoDoc)
3450 pOldCode.reset(pCode->Clone());
3452 if (bOnRefMove)
3453 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3454 bOnRefMove = (aPos != aOldPos);
3456 bool bNeedDirty = bOnRefMove;
3458 if (pUndoDoc && bOnRefMove)
3459 setOldCodeToUndo(pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3461 if (bCompile)
3463 CompileTokenArray(); // no Listening
3464 bNeedDirty = true;
3467 if (bNeedDirty)
3468 { // Cut off references, invalid or similar?
3469 sc::AutoCalcSwitch aACSwitch(*pDocument, false);
3470 SetDirty();
3473 return false;
3476 bool ScFormulaCell::UpdateReference(
3477 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3479 if (pDocument->IsClipOrUndo())
3480 return false;
3482 if (mxGroup && mxGroup->mpTopCell != this)
3484 // This is not a top cell of a formula group. Don't update references.
3486 switch (rCxt.meMode)
3488 case URM_INSDEL:
3489 return UpdatePosOnShift(rCxt);
3490 break;
3491 default:
3494 return false;
3497 switch (rCxt.meMode)
3499 case URM_INSDEL:
3500 return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3501 case URM_MOVE:
3502 return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3503 case URM_COPY:
3504 return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3505 default:
3509 return false;
3512 void ScFormulaCell::UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt )
3514 // Adjust tokens only when it's not grouped or grouped top cell.
3515 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3516 bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3517 if (pDocument->IsClipOrUndo() || !pCode->HasReferences())
3519 if (bPosChanged)
3520 aPos.IncTab(rCxt.mnSheets);
3522 return;
3525 EndListeningTo( pDocument );
3526 ScAddress aOldPos = aPos;
3527 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3528 if (bPosChanged)
3529 aPos.IncTab(rCxt.mnSheets);
3531 if (!bAdjustCode)
3532 return;
3534 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aOldPos);
3535 if (aRes.mbNameModified)
3536 // Re-compile after new sheet(s) have been inserted.
3537 bCompile = true;
3539 // no StartListeningTo because the new sheets have not been inserted yet.
3542 void ScFormulaCell::UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt )
3544 // Adjust tokens only when it's not grouped or grouped top cell.
3545 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3546 bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3547 if (pDocument->IsClipOrUndo() || !pCode->HasReferences())
3549 if (bPosChanged)
3550 aPos.IncTab(-1*rCxt.mnSheets);
3551 return;
3554 EndListeningTo( pDocument );
3555 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3556 ScAddress aOldPos = aPos;
3557 if (bPosChanged)
3558 aPos.IncTab(-1*rCxt.mnSheets);
3560 if (!bAdjustCode)
3561 return;
3563 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
3564 if (aRes.mbNameModified)
3565 // Re-compile after sheet(s) have been deleted.
3566 bCompile = true;
3569 void ScFormulaCell::UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
3571 // Adjust tokens only when it's not grouped or grouped top cell.
3572 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3574 if (!pCode->HasReferences() || pDocument->IsClipOrUndo())
3576 aPos.SetTab(nTabNo);
3577 return;
3580 EndListeningTo(pDocument);
3581 ScAddress aOldPos = aPos;
3582 // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3583 aPos.SetTab(nTabNo);
3585 // no StartListeningTo because pTab[nTab] not yet correct!
3587 if (!bAdjustCode)
3588 return;
3590 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
3591 if (aRes.mbNameModified)
3592 // Re-compile after sheet(s) have been deleted.
3593 bCompile = true;
3596 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
3598 if (pDocument->IsClipOrUndo())
3599 return;
3601 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3602 if (!bAdjustCode)
3603 return;
3605 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
3606 formula::FormulaToken* p = aIter.GetNextReferenceRPN();
3607 while (p)
3609 ScSingleRefData& rRef1 = *p->GetSingleRef();
3610 if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3611 rRef1.IncTab(1);
3612 if (p->GetType() == formula::svDoubleRef)
3614 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3615 if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3616 rRef2.IncTab(1);
3618 p = aIter.GetNextReferenceRPN();
3622 bool ScFormulaCell::TestTabRefAbs(SCTAB nTable)
3624 if (pDocument->IsClipOrUndo())
3625 return false;
3627 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3628 if (!bAdjustCode)
3629 return false;
3631 bool bRet = false;
3632 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
3633 formula::FormulaToken* p = aIter.GetNextReferenceRPN();
3634 while (p)
3636 ScSingleRefData& rRef1 = *p->GetSingleRef();
3637 if (!rRef1.IsTabRel())
3639 if (nTable != rRef1.Tab())
3640 bRet = true;
3641 else if (nTable != aPos.Tab())
3642 rRef1.SetAbsTab(aPos.Tab());
3644 if (p->GetType() == formula::svDoubleRef)
3646 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3647 if (!rRef2.IsTabRel())
3649 if(nTable != rRef2.Tab())
3650 bRet = true;
3651 else if (nTable != aPos.Tab())
3652 rRef2.SetAbsTab(aPos.Tab());
3655 p = aIter.GetNextReferenceRPN();
3657 return bRet;
3660 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3662 if ( bForceIfNameInUse && !bCompile )
3663 bCompile = pCode->HasNameOrColRowName();
3664 if ( bCompile )
3665 pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3666 CompileTokenArray();
3669 // Reference transposition is only called in Clipboard Document
3670 void ScFormulaCell::TransposeReference()
3672 bool bFound = false;
3673 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
3674 formula::FormulaToken* t;
3675 while ( ( t = aIter.GetNextReference() ) != nullptr )
3677 ScSingleRefData& rRef1 = *t->GetSingleRef();
3678 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3680 bool bDouble = (t->GetType() == formula::svDoubleRef);
3681 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3682 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3684 SCCOLROW nTemp;
3686 nTemp = rRef1.Col();
3687 rRef1.SetRelCol(rRef1.Row());
3688 rRef1.SetRelRow(nTemp);
3690 if ( bDouble )
3692 nTemp = rRef2.Col();
3693 rRef2.SetRelCol(rRef2.Row());
3694 rRef2.SetRelRow(nTemp);
3697 bFound = true;
3702 if (bFound)
3703 bCompile = true;
3706 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3707 ScDocument* pUndoDoc )
3709 EndListeningTo( pDocument );
3711 ScAddress aOldPos = aPos;
3712 bool bPosChanged = false; // Whether this cell has been moved
3714 ScRange aDestRange( rDest, ScAddress(
3715 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3716 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3717 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3718 if ( aDestRange.In( aOldPos ) )
3720 // Count back Positions
3721 SCCOL nRelPosX = aOldPos.Col();
3722 SCROW nRelPosY = aOldPos.Row();
3723 SCTAB nRelPosZ = aOldPos.Tab();
3724 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
3725 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3726 bPosChanged = true;
3729 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : nullptr;
3730 bool bRefChanged = false;
3732 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
3733 formula::FormulaToken* t;
3734 while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3736 if( t->GetOpCode() == ocName )
3738 const ScRangeData* pName = pDocument->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
3739 if (pName && pName->IsModified())
3740 bRefChanged = true;
3742 else if( t->GetType() != svIndex )
3744 SingleDoubleRefModifier aMod(*t);
3745 ScComplexRefData& rRef = aMod.Ref();
3746 ScRange aAbs = rRef.toAbs(aOldPos);
3747 bool bMod = (ScRefUpdate::UpdateTranspose(pDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3748 if (bMod)
3750 rRef.SetRange(aAbs, aPos); // based on the new anchor position.
3751 bRefChanged = true;
3756 if (bRefChanged)
3758 if (pUndoDoc)
3760 ScFormulaCell* pFCell = new ScFormulaCell(
3761 pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(), eTempGrammar, cMatrixFlag);
3763 pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3764 pUndoDoc->SetFormulaCell(aPos, pFCell);
3767 bCompile = true;
3768 CompileTokenArray(); // also call StartListeningTo
3769 SetDirty();
3771 else
3772 StartListeningTo( pDocument ); // Listener as previous
3774 delete pOld;
3777 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3779 EndListeningTo( pDocument );
3781 bool bRefChanged = false;
3783 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
3784 formula::FormulaToken* t;
3786 while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3788 if( t->GetOpCode() == ocName )
3790 const ScRangeData* pName = pDocument->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
3791 if (pName && pName->IsModified())
3792 bRefChanged = true;
3794 else if( t->GetType() != svIndex )
3796 SingleDoubleRefModifier aMod(*t);
3797 ScComplexRefData& rRef = aMod.Ref();
3798 ScRange aAbs = rRef.toAbs(aPos);
3799 bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3800 if (bMod)
3802 rRef.SetRange(aAbs, aPos);
3803 bRefChanged = true;
3808 if (bRefChanged)
3810 bCompile = true;
3811 CompileTokenArray(); // Also call StartListeningTo
3812 SetDirty();
3814 else
3815 StartListeningTo( pDocument ); // Listener as previous
3818 // See also ScDocument::FindRangeNamesReferencingSheet()
3819 static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument* pDoc,
3820 int nRecursion)
3822 FormulaTokenArrayPlainIterator aIter(*pCode);
3823 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3825 if (p->GetOpCode() == ocName)
3827 sal_uInt16 nTokenIndex = p->GetIndex();
3828 SCTAB nTab = p->GetSheet();
3829 rIndexes.setUpdatedName( nTab, nTokenIndex);
3831 if (nRecursion < 126) // whatever.. 42*3
3833 ScRangeData* pSubName = pDoc->FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3834 if (pSubName)
3835 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pDoc, nRecursion+1);
3841 void ScFormulaCell::FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes) const
3843 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument, 0);
3846 void ScFormulaCell::SetChanged(bool b)
3848 bChanged = b;
3851 void ScFormulaCell::SetCode( ScTokenArray* pNew )
3853 assert(!mxGroup); // Don't call this if it's shared.
3854 delete pCode;
3855 pCode = pNew; // takes ownership.
3858 void ScFormulaCell::SetRunning( bool bVal )
3860 bRunning = bVal;
3863 void ScFormulaCell::CompileDBFormula( sc::CompileFormulaContext& rCxt )
3865 FormulaTokenArrayPlainIterator aIter(*pCode);
3866 for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3868 OpCode eOp = p->GetOpCode();
3869 if ( eOp == ocDBArea || eOp == ocTableRef )
3871 bCompile = true;
3872 CompileTokenArray(rCxt);
3873 SetDirty();
3874 break;
3879 void ScFormulaCell::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
3881 FormulaTokenArrayPlainIterator aIter(*pCode);
3882 for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3884 if ( p->GetOpCode() == ocColRowName )
3886 bCompile = true;
3887 CompileTokenArray(rCxt);
3888 SetDirty();
3889 break;
3894 void ScFormulaCell::SetPrevious( ScFormulaCell* pF ) { pPrevious = pF; }
3895 void ScFormulaCell::SetNext( ScFormulaCell* pF ) { pNext = pF; }
3896 void ScFormulaCell::SetPreviousTrack( ScFormulaCell* pF ) { pPreviousTrack = pF; }
3897 void ScFormulaCell::SetNextTrack( ScFormulaCell* pF ) { pNextTrack = pF; }
3899 ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant )
3901 if (mxGroup)
3903 // You can't create a new group if the cell is already a part of a group.
3904 // Is this a sign of some inconsistent or incorrect data structures? Or normal?
3905 SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
3906 return ScFormulaCellGroupRef();
3909 mxGroup.reset(new ScFormulaCellGroup);
3910 mxGroup->mpTopCell = this;
3911 mxGroup->mbInvariant = bInvariant;
3912 mxGroup->mnLength = nLen;
3913 mxGroup->mpCode = pCode; // Move this to the shared location.
3914 return mxGroup;
3917 void ScFormulaCell::SetCellGroup( const ScFormulaCellGroupRef &xRef )
3919 if (!xRef)
3921 // Make this cell a non-grouped cell.
3922 if (mxGroup)
3923 pCode = mxGroup->mpCode->Clone();
3925 mxGroup = xRef;
3926 return;
3929 // Group object has shared token array.
3930 if (!mxGroup)
3931 // Currently not shared. Delete the existing token array first.
3932 delete pCode;
3934 mxGroup = xRef;
3935 pCode = mxGroup->mpCode;
3936 mxGroup->mnWeight = 0; // invalidate
3939 ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( const ScFormulaCell& rOther ) const
3941 // no Matrix formulae yet.
3942 if ( GetMatrixFlag() != ScMatrixMode::NONE )
3943 return NotEqual;
3945 // are these formulas at all similar ?
3946 if ( GetHash() != rOther.GetHash() )
3947 return NotEqual;
3949 if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
3950 return NotEqual;
3952 FormulaToken **pThis = pCode->GetCode();
3953 sal_uInt16 nThisLen = pCode->GetCodeLen();
3954 FormulaToken **pOther = rOther.pCode->GetCode();
3955 sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
3957 if ( !pThis || !pOther )
3959 // Error: no compiled code for cells !"
3960 return NotEqual;
3963 if ( nThisLen != nOtherLen )
3964 return NotEqual;
3966 // No tokens can be an error cell so check error code, otherwise we could
3967 // end up with a series of equal error values instead of individual error
3968 // values. Also if for any reason different errors are set even if all
3969 // tokens are equal, the cells are not equal.
3970 if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
3971 return NotEqual;
3973 bool bInvariant = true;
3975 // check we are basically the same function
3976 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
3978 formula::FormulaToken *pThisTok = pThis[i];
3979 formula::FormulaToken *pOtherTok = pOther[i];
3981 if ( pThisTok->GetType() != pOtherTok->GetType() ||
3982 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
3983 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
3985 // Incompatible type, op-code or param counts.
3986 return NotEqual;
3989 switch (pThisTok->GetType())
3991 case formula::svMatrix:
3992 case formula::svExternalSingleRef:
3993 case formula::svExternalDoubleRef:
3994 // Ignoring matrix and external references for now.
3995 return NotEqual;
3997 case formula::svSingleRef:
3999 // Single cell reference.
4000 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4001 if (rRef != *pOtherTok->GetSingleRef())
4002 return NotEqual;
4004 if (rRef.IsRowRel())
4005 bInvariant = false;
4007 break;
4008 case formula::svDoubleRef:
4010 // Range reference.
4011 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4012 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4013 if (rRef1 != *pOtherTok->GetSingleRef())
4014 return NotEqual;
4016 if (rRef2 != *pOtherTok->GetSingleRef2())
4017 return NotEqual;
4019 if (rRef1.IsRowRel())
4020 bInvariant = false;
4022 if (rRef2.IsRowRel())
4023 bInvariant = false;
4025 break;
4026 case formula::svDouble:
4028 if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4029 return NotEqual;
4031 break;
4032 case formula::svString:
4034 if(pThisTok->GetString() != pOtherTok->GetString())
4035 return NotEqual;
4037 break;
4038 case formula::svIndex:
4040 if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4041 return NotEqual;
4043 break;
4044 case formula::svByte:
4046 if(pThisTok->GetByte() != pOtherTok->GetByte())
4047 return NotEqual;
4049 break;
4050 case formula::svExternal:
4052 if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4053 return NotEqual;
4055 if (pThisTok->GetByte() != pOtherTok->GetByte())
4056 return NotEqual;
4058 break;
4059 case formula::svError:
4061 if (pThisTok->GetError() != pOtherTok->GetError())
4062 return NotEqual;
4064 break;
4065 default:
4070 // If still the same, check lexical names as different names may result in
4071 // identical RPN code.
4073 pThis = pCode->GetArray();
4074 nThisLen = pCode->GetLen();
4075 pOther = rOther.pCode->GetArray();
4076 nOtherLen = rOther.pCode->GetLen();
4078 if ( !pThis || !pOther )
4080 // Error: no code for cells !"
4081 return NotEqual;
4084 if ( nThisLen != nOtherLen )
4085 return NotEqual;
4087 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4089 formula::FormulaToken *pThisTok = pThis[i];
4090 formula::FormulaToken *pOtherTok = pOther[i];
4092 if ( pThisTok->GetType() != pOtherTok->GetType() ||
4093 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4094 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4096 // Incompatible type, op-code or param counts.
4097 return NotEqual;
4100 switch (pThisTok->GetType())
4102 // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4103 // resulting in identical RPN references that could lead to creating
4104 // a formula group from formulas that should not be merged into a group,
4105 // so check also the formula itself.
4106 case formula::svSingleRef:
4108 // Single cell reference.
4109 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4110 if (rRef != *pOtherTok->GetSingleRef())
4111 return NotEqual;
4113 if (rRef.IsRowRel())
4114 bInvariant = false;
4116 break;
4117 case formula::svDoubleRef:
4119 // Range reference.
4120 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4121 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4122 if (rRef1 != *pOtherTok->GetSingleRef())
4123 return NotEqual;
4125 if (rRef2 != *pOtherTok->GetSingleRef2())
4126 return NotEqual;
4128 if (rRef1.IsRowRel())
4129 bInvariant = false;
4131 if (rRef2.IsRowRel())
4132 bInvariant = false;
4134 break;
4135 // All index tokens are names. Different categories already had
4136 // different OpCode values.
4137 case formula::svIndex:
4139 if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4140 return NotEqual;
4141 switch (pThisTok->GetOpCode())
4143 case ocTableRef:
4144 // nothing, sheet value assumed as -1, silence
4145 // ScTableRefToken::GetSheet() SAL_WARN about
4146 // unhandled
4148 break;
4149 default: // ocName, ocDBArea
4150 if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4151 return NotEqual;
4154 break;
4155 default:
4160 return bInvariant ? EqualInvariant : EqualRelativeRef;
4163 namespace {
4165 // Split N into optimally equal-sized pieces, each not larger than K.
4166 // Return value P is number of pieces. A returns the number of pieces
4167 // one larger than N/P, 0..P-1.
4169 int splitup(int N, int K, int& A)
4171 assert(N > 0);
4172 assert(K > 0);
4174 A = 0;
4176 if (N <= K)
4177 return 1;
4179 const int ideal_num_parts = N / K;
4180 if (ideal_num_parts * K == N)
4181 return ideal_num_parts;
4183 const int num_parts = ideal_num_parts + 1;
4184 const int nominal_part_size = N / num_parts;
4186 A = N - num_parts * nominal_part_size;
4188 return num_parts;
4191 } // anonymous namespace
4193 struct ScDependantsCalculator
4195 ScDocument& mrDoc;
4196 const ScTokenArray& mrCode;
4197 const ScFormulaCellGroupRef& mxGroup;
4198 const SCROW mnLen;
4199 const ScAddress& mrPos;
4201 ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell, const ScAddress& rPos) :
4202 mrDoc(rDoc),
4203 mrCode(rCode),
4204 mxGroup(rCell.GetCellGroup()),
4205 mnLen(mxGroup->mnLength),
4206 mrPos(rPos)
4210 // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4212 // I think what this function does is to check whether the relative row reference nRelRow points
4213 // to a row that is inside the range of rows covered by the formula group.
4215 bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4217 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4218 return false;
4220 SCROW nEndRow = mrPos.Row() + mnLen - 1;
4222 if (nRelRow <= 0)
4224 SCROW nTest = nEndRow;
4225 nTest += nRelRow;
4226 if (nTest >= mrPos.Row())
4227 return true;
4229 else
4231 SCROW nTest = mrPos.Row(); // top row.
4232 nTest += nRelRow;
4233 if (nTest <= nEndRow)
4234 return true;
4237 return false;
4240 // FIXME: another copy-paste
4242 // And this correspondingly checks whether an absolute row is inside the range of rows covered
4243 // by the formula group.
4245 bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4247 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4248 return false;
4250 SCROW nEndRow = mrPos.Row() + mnLen - 1;
4252 if (rRefPos.Row() < mrPos.Row())
4253 return false;
4255 if (rRefPos.Row() > nEndRow)
4256 return false;
4258 return true;
4261 // Checks if the doubleref engulfs all of formula group cells
4262 // Note : does not check if there is a partial overlap, that can be done by calling
4263 // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4264 bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4266 if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4267 || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4269 return false;
4272 SCROW nStartRow = mrPos.Row();
4273 SCROW nEndRow = nStartRow + mnLen - 1;
4274 SCROW nRefStartRow = rAbs.aStart.Row();
4275 SCROW nRefEndRow = rAbs.aEnd.Row();
4277 if (bIsRef1RowRel && bIsRef2RowRel &&
4278 ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4279 ((nRefStartRow + mnLen - 1) <= nStartRow &&
4280 (nRefEndRow + mnLen - 1) >= nEndRow)))
4281 return true;
4283 if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4284 (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4285 return true;
4287 if (!bIsRef2RowRel &&
4288 nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4289 return true;
4291 return false;
4294 // FIXME: another copy-paste
4295 SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4297 SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4298 nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4299 if (nLastRow < (nRow + nRowLen - 1))
4301 // This can end up negative! Was that the original intent, or
4302 // is it accidental? Was it not like that originally but the
4303 // surrounding conditions changed?
4304 nRowLen = nLastRow - nRow + 1;
4305 // Anyway, let's assume it doesn't make sense to return a
4306 // negative or zero value here.
4307 if (nRowLen <= 0)
4308 nRowLen = 1;
4310 else if (nLastRow == 0)
4311 // Column is empty.
4312 nRowLen = 1;
4314 return nRowLen;
4317 bool DoIt()
4319 // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4321 ScRangeList aRangeList;
4322 bool bHasSelfReferences = false;
4323 for (auto p: mrCode.RPNTokens())
4325 switch (p->GetType())
4327 case svSingleRef:
4329 ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4330 if( aRef.IsDeleted())
4331 return false;
4332 ScAddress aRefPos = aRef.toAbs(mrPos);
4334 if (!mrDoc.TableExists(aRefPos.Tab()))
4335 return false; // or true?
4337 if (aRef.IsRowRel())
4339 if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4341 bHasSelfReferences = true;
4342 continue;
4345 // Trim data array length to actual data range.
4346 SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row(), mnLen);
4348 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab(),
4349 aRefPos.Col(), aRefPos.Row() + nTrimLen - 1, aRefPos.Tab()));
4351 else
4353 if (isSelfReferenceAbsolute(aRefPos))
4355 bHasSelfReferences = true;
4356 continue;
4359 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4362 break;
4363 case svDoubleRef:
4365 ScComplexRefData aRef = *p->GetDoubleRef();
4366 if( aRef.IsDeleted())
4367 return false;
4368 ScRange aAbs = aRef.toAbs(mrPos);
4370 // Multiple sheet
4371 if (aRef.Ref1.Tab() != aRef.Ref2.Tab())
4372 return false;
4374 bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4375 // Check for self reference.
4376 if (bIsRef1RowRel)
4378 if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4380 bHasSelfReferences = true;
4381 continue;
4384 else if (isSelfReferenceAbsolute(aAbs.aStart))
4386 bHasSelfReferences = true;
4387 continue;
4390 bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4391 if (bIsRef2RowRel)
4393 if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4395 bHasSelfReferences = true;
4396 continue;
4399 else if (isSelfReferenceAbsolute(aAbs.aEnd))
4401 bHasSelfReferences = true;
4402 continue;
4405 if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4407 bHasSelfReferences = true;
4408 continue;
4411 // Row reference is relative.
4412 bool bAbsLast = !aRef.Ref2.IsRowRel();
4413 ScAddress aRefPos = aAbs.aStart;
4414 SCROW nRefRowSize = aAbs.aEnd.Row() - aAbs.aStart.Row() + 1;
4415 SCROW nArrayLength = nRefRowSize;
4416 if (!bAbsLast)
4418 // range end position is relative. Extend the array length.
4419 SCROW nLastRefRowOffset = aAbs.aEnd.Row() - mrPos.Row();
4420 SCROW nLastRefRow = mrPos.Row() + mnLen - 1 + nLastRefRowOffset;
4421 SCROW nNewLength = nLastRefRow - aAbs.aStart.Row() + 1;
4422 if (nNewLength > nArrayLength)
4423 nArrayLength = nNewLength;
4426 // Trim trailing empty rows.
4427 nArrayLength = trimLength(aRefPos.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), aRefPos.Row(), nArrayLength);
4429 aRangeList.Join(ScRange(aAbs.aStart.Col(), aRefPos.Row(), aRefPos.Tab(),
4430 aAbs.aEnd.Col(), aRefPos.Row() + nArrayLength - 1, aRefPos.Tab()));
4432 break;
4433 default:
4434 break;
4438 // Compute dependencies irrespective of the presence of any self references.
4439 // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4440 // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4441 for (size_t i = 0; i < aRangeList.size(); ++i)
4443 const ScRange & rRange = aRangeList[i];
4444 assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4445 for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4447 if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, rRange.aStart.Row(), rRange.aStart.Tab()),
4448 rRange.aEnd.Row() - rRange.aStart.Row() + 1, mxGroup))
4449 return false;
4452 return !bHasSelfReferences;
4456 bool ScFormulaCell::InterpretFormulaGroup()
4458 if (!mxGroup || !pCode)
4459 return false;
4461 auto aScope = sc::FormulaLogger::get().enterGroup(*pDocument, *this);
4462 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
4464 if (mxGroup->mbPartOfCycle)
4466 aScope.addMessage("This formula-group is part of a cycle");
4467 return false;
4470 if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4472 aScope.addMessage("group calc disabled");
4473 return false;
4476 // To temporarily use threading for sc unit tests regardless of the size of the formula group,
4477 // add the condition !std::getenv("LO_TESTNAME") below (with &&)
4478 if (GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize)
4480 mxGroup->meCalcState = sc::GroupCalcDisabled;
4481 aScope.addGroupSizeThresholdMessage(*this);
4482 return false;
4485 if (cMatrixFlag != ScMatrixMode::NONE)
4487 mxGroup->meCalcState = sc::GroupCalcDisabled;
4488 aScope.addMessage("matrix skipped");
4489 return false;
4492 // Guard against endless recursion of Interpret() calls, for this to work
4493 // ScFormulaCell::InterpretFormulaGroup() must never be called through
4494 // anything else than ScFormulaCell::Interpret(), same as
4495 // ScFormulaCell::InterpretTail()
4496 RecursionCounter aRecursionCounter( rRecursionHelper, this);
4498 bool bDependencyComputed = false;
4499 bool bDependencyCheckFailed = false;
4501 // Preference order: First try OpenCL, then threading.
4502 if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4503 return true;
4505 if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed))
4506 return true;
4508 return false;
4511 bool ScFormulaCell::CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope)
4513 ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
4514 // iterate over code in the formula ...
4515 // ensure all input is pre-calculated -
4516 // to avoid writing during the calculation
4518 bool bOKToParallelize = false;
4520 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4521 if (mxGroup->mbPartOfCycle)
4523 mxGroup->meCalcState = sc::GroupCalcDisabled;
4524 rScope.addMessage("found circular formula-group dependencies");
4525 return false;
4528 ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4529 ScDependantsCalculator aCalculator(*pDocument, *pCode, *this, mxGroup->mpTopCell->aPos);
4530 bOKToParallelize = aCalculator.DoIt();
4533 if (rRecursionHelper.IsInRecursionReturn())
4535 mxGroup->meCalcState = sc::GroupCalcDisabled;
4536 rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4537 return false;
4540 if (mxGroup->mbPartOfCycle)
4542 mxGroup->meCalcState = sc::GroupCalcDisabled;
4543 rScope.addMessage("found circular formula-group dependencies");
4544 return false;
4547 if (!bOKToParallelize)
4549 mxGroup->meCalcState = sc::GroupCalcDisabled;
4550 rScope.addMessage("could not do new dependencies calculation thing");
4551 return false;
4554 return true;
4557 // To be called only from InterpretFormulaGroup().
4558 bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope& aScope,
4559 bool& bDependencyComputed,
4560 bool& bDependencyCheckFailed)
4562 static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4563 if (!bDependencyCheckFailed && !bThreadingProhibited &&
4564 pCode->IsEnabledForThreading() &&
4565 ScCalcConfig::isThreadingEnabled())
4567 if(!bDependencyComputed && !CheckComputeDependencies(aScope))
4569 bDependencyComputed = true;
4570 bDependencyCheckFailed = true;
4571 return false;
4574 bDependencyComputed = true;
4576 const static bool bHyperThreadingActive = tools::cpuid::hasHyperThreading();
4578 // Then do the threaded calculation
4580 class Executor : public comphelper::ThreadTask
4582 private:
4583 const unsigned mnThisThread;
4584 const unsigned mnThreadsTotal;
4585 ScDocument* mpDocument;
4586 ScInterpreterContext* mpContext;
4587 const ScAddress& mrTopPos;
4588 SCROW const mnLength;
4590 public:
4591 Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4592 unsigned nThisThread,
4593 unsigned nThreadsTotal,
4594 ScDocument* pDocument2,
4595 ScInterpreterContext* pContext,
4596 const ScAddress& rTopPos,
4597 SCROW nLength) :
4598 comphelper::ThreadTask(rTag),
4599 mnThisThread(nThisThread),
4600 mnThreadsTotal(nThreadsTotal),
4601 mpDocument(pDocument2),
4602 mpContext(pContext),
4603 mrTopPos(rTopPos),
4604 mnLength(nLength)
4608 virtual void doWork() override
4610 auto aNonThreadedData = mpDocument->CalculateInColumnInThread(*mpContext, mrTopPos, mnLength, mnThisThread, mnThreadsTotal);
4611 aNonThreadedData.MergeBackIntoNonThreadedData(mpDocument->maNonThreaded);
4616 SvNumberFormatter* pNonThreadedFormatter = pDocument->GetNonThreadedContext().GetFormatTable();
4618 comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool());
4619 sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4621 if ( bHyperThreadingActive && nThreadCount >= 2 )
4622 nThreadCount /= 2;
4624 SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4626 assert(!pDocument->IsThreadedGroupCalcInProgress());
4627 pDocument->SetThreadedGroupCalcInProgress(true);
4629 ScMutationDisable aGuard(pDocument, ScMutationGuardFlags::CORE);
4631 // Start nThreadCount new threads
4632 std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
4633 std::vector<ScInterpreterContext*> contexts(nThreadCount);
4634 for (int i = 0; i < nThreadCount; ++i)
4636 contexts[i] = new ScInterpreterContext(*pDocument, pNonThreadedFormatter);
4637 pDocument->SetupFromNonThreadedContext(*contexts[i], i);
4638 rThreadPool.pushTask(o3tl::make_unique<Executor>(aTag, i, nThreadCount, pDocument, contexts[i], mxGroup->mpTopCell->aPos, mxGroup->mnLength));
4641 SAL_INFO("sc.threaded", "Joining threads");
4642 rThreadPool.waitUntilDone(aTag);
4644 pDocument->SetThreadedGroupCalcInProgress(false);
4646 for (int i = 0; i < nThreadCount; ++i)
4648 // This is intentionally done in this main thread in order to avoid locking.
4649 pDocument->MergeBackIntoNonThreadedContext(*contexts[i], i);
4650 delete contexts[i];
4653 SAL_INFO("sc.threaded", "Done");
4656 pDocument->HandleStuffAfterParallelCalculation(mxGroup->mpTopCell->aPos, mxGroup->mnLength);
4658 return true;
4661 return false;
4664 // To be called only from InterpretFormulaGroup().
4665 bool ScFormulaCell::InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope& aScope,
4666 bool& bDependencyComputed,
4667 bool& bDependencyCheckFailed)
4669 bool bCanVectorize = pCode->IsEnabledForOpenCL();
4670 switch (pCode->GetVectorState())
4672 case FormulaVectorEnabled:
4673 case FormulaVectorCheckReference:
4674 break;
4676 // Not good.
4677 case FormulaVectorDisabledByOpCode:
4678 aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
4679 break;
4680 case FormulaVectorDisabledNotInSoftwareSubset:
4681 aScope.addMessage("group calc disabled due to vector state (opcode not in software subset)");
4682 break;
4683 case FormulaVectorDisabledByStackVariable:
4684 aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
4685 break;
4686 case FormulaVectorDisabledNotInSubSet:
4687 aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
4688 break;
4689 case FormulaVectorDisabled:
4690 case FormulaVectorUnknown:
4691 default:
4692 aScope.addMessage("group calc disabled due to vector state (unknown)");
4693 return false;
4696 if (!bCanVectorize)
4697 return false;
4699 if (!ScCalcConfig::isOpenCLEnabled())
4701 aScope.addMessage("opencl not enabled");
4702 return false;
4705 if (bDependencyCheckFailed)
4706 return false;
4708 if(!bDependencyComputed && !CheckComputeDependencies(aScope))
4710 bDependencyComputed = true;
4711 bDependencyCheckFailed = true;
4712 return false;
4715 bDependencyComputed = true;
4717 // TODO : Disable invariant formula group interpretation for now in order
4718 // to get implicit intersection to work.
4719 if (mxGroup->mbInvariant && false)
4720 return InterpretInvariantFormulaGroup();
4722 int nMaxGroupLength = INT_MAX;
4724 #ifdef _WIN32
4725 // Heuristic: Certain old low-end OpenCL implementations don't
4726 // work for us with too large group lengths. 1000 was determined
4727 // empirically to be a good compromise.
4728 if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
4729 nMaxGroupLength = 1000;
4730 #endif
4732 if (std::getenv("SC_MAX_GROUP_LENGTH"))
4733 nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
4735 int nNumOnePlus;
4736 const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
4738 int nOffset = 0;
4739 int nCurChunkSize;
4740 ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
4741 for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
4743 nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
4745 ScFormulaCellGroupRef xGroup;
4747 if (nNumParts == 1)
4748 xGroup = mxGroup;
4749 else
4751 // Ugly hack
4752 xGroup = new ScFormulaCellGroup();
4753 xGroup->mpTopCell = mxGroup->mpTopCell;
4754 xGroup->mpTopCell->aPos = aOrigPos;
4755 xGroup->mpTopCell->aPos.IncRow(nOffset);
4756 xGroup->mbInvariant = mxGroup->mbInvariant;
4757 xGroup->mnLength = nCurChunkSize;
4758 xGroup->mpCode = mxGroup->mpCode;
4761 ScTokenArray aCode;
4762 ScGroupTokenConverter aConverter(aCode, *pDocument, *this, xGroup->mpTopCell->aPos);
4763 // TODO avoid this extra compilation
4764 ScCompiler aComp( pDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
4765 aComp.CompileTokenArray();
4766 if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
4768 if(aComp.HasUnhandledPossibleImplicitIntersections())
4770 SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
4771 #ifdef DBG_UTIL
4772 for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
4774 SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
4775 << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
4776 << "(" << int(opcode) << ")");
4778 #endif
4780 else
4781 SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
4783 mxGroup->meCalcState = sc::GroupCalcDisabled;
4785 // Undo the hack above
4786 if (nNumParts > 1)
4788 mxGroup->mpTopCell->aPos = aOrigPos;
4789 xGroup->mpTopCell = nullptr;
4790 xGroup->mpCode = nullptr;
4793 aScope.addMessage("group token conversion failed");
4794 return false;
4797 // The converted code does not have RPN tokens yet. The interpreter will
4798 // generate them.
4799 xGroup->meCalcState = mxGroup->meCalcState = sc::GroupCalcRunning;
4800 sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic();
4802 if (pInterpreter == nullptr ||
4803 !pInterpreter->interpret(*pDocument, xGroup->mpTopCell->aPos, xGroup, aCode))
4805 SAL_INFO("sc.opencl", "interpreting group " << mxGroup << " (state " << static_cast<int>(mxGroup->meCalcState) << ") failed, disabling");
4806 mxGroup->meCalcState = sc::GroupCalcDisabled;
4808 // Undo the hack above
4809 if (nNumParts > 1)
4811 mxGroup->mpTopCell->aPos = aOrigPos;
4812 xGroup->mpTopCell = nullptr;
4813 xGroup->mpCode = nullptr;
4816 aScope.addMessage("group interpretation unsuccessful");
4817 return false;
4820 aScope.setCalcComplete();
4822 if (nNumParts > 1)
4824 xGroup->mpTopCell = nullptr;
4825 xGroup->mpCode = nullptr;
4829 if (nNumParts > 1)
4830 mxGroup->mpTopCell->aPos = aOrigPos;
4831 mxGroup->meCalcState = sc::GroupCalcEnabled;
4832 return true;
4835 bool ScFormulaCell::InterpretInvariantFormulaGroup()
4837 if (pCode->GetVectorState() == FormulaVectorCheckReference)
4839 // An invariant group should only have absolute row references, and no
4840 // external references are allowed.
4842 ScTokenArray aCode;
4843 FormulaTokenArrayPlainIterator aIter(*pCode);
4844 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
4846 switch (p->GetType())
4848 case svSingleRef:
4850 ScSingleRefData aRef = *p->GetSingleRef();
4851 ScAddress aRefPos = aRef.toAbs(aPos);
4852 formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefPos);
4853 if (!pNewToken)
4854 return false;
4856 aCode.AddToken(*pNewToken);
4858 break;
4859 case svDoubleRef:
4861 ScComplexRefData aRef = *p->GetDoubleRef();
4862 ScRange aRefRange = aRef.toAbs(aPos);
4863 formula::FormulaTokenRef pNewToken = pDocument->ResolveStaticReference(aRefRange);
4864 if (!pNewToken)
4865 return false;
4867 aCode.AddToken(*pNewToken);
4869 break;
4870 default:
4871 aCode.AddToken(*p);
4875 ScCompiler aComp(pDocument, aPos, aCode, pDocument->GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE);
4876 aComp.CompileTokenArray(); // Create RPN token array.
4877 ScInterpreter aInterpreter(this, pDocument, pDocument->GetNonThreadedContext(), aPos, aCode);
4878 aInterpreter.Interpret();
4879 aResult.SetToken(aInterpreter.GetResultToken().get());
4881 else
4883 // Formula contains no references.
4884 ScInterpreter aInterpreter(this, pDocument, pDocument->GetNonThreadedContext(), aPos, *pCode);
4885 aInterpreter.Interpret();
4886 aResult.SetToken(aInterpreter.GetResultToken().get());
4889 for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ )
4891 ScAddress aTmpPos = aPos;
4892 aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i);
4893 ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
4894 if (!pCell)
4896 SAL_WARN("sc.core.formulacell", "GetFormulaCell not found");
4897 continue;
4900 // FIXME: this set of horrors is unclear to me ... certainly
4901 // the above GetCell is profoundly nasty & slow ...
4902 // Ensure the cell truly has a result:
4903 pCell->aResult = aResult;
4904 pCell->ResetDirty();
4905 pCell->SetChanged(true);
4908 return true;
4911 namespace {
4913 void startListeningArea(
4914 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
4916 const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
4917 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
4918 ScAddress aCell1 = rRef1.toAbs(rPos);
4919 ScAddress aCell2 = rRef2.toAbs(rPos);
4920 if (aCell1.IsValid() && aCell2.IsValid())
4922 if (rToken.GetOpCode() == ocColRowNameAuto)
4923 { // automagically
4924 if ( rRef1.IsColRel() )
4925 { // ColName
4926 aCell2.SetRow(MAXROW);
4928 else
4929 { // RowName
4930 aCell2.SetCol(MAXCOL);
4933 rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell);
4939 void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
4941 if (mxGroup)
4942 mxGroup->endAllGroupListening(*pDoc);
4944 if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
4945 return;
4947 pDoc->SetDetectiveDirty(true); // It has changed something
4949 ScTokenArray* pArr = GetCode();
4950 if( pArr->IsRecalcModeAlways() )
4952 pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
4953 SetNeedsListening( false);
4954 return;
4957 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
4958 formula::FormulaToken* t;
4959 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
4961 switch (t->GetType())
4963 case svSingleRef:
4965 ScAddress aCell = t->GetSingleRef()->toAbs(aPos);
4966 if (aCell.IsValid())
4967 pDoc->StartListeningCell(aCell, this);
4969 break;
4970 case svDoubleRef:
4971 startListeningArea(this, *pDoc, aPos, *t);
4972 break;
4973 default:
4974 ; // nothing
4977 SetNeedsListening( false);
4980 void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
4982 ScDocument& rDoc = rCxt.getDoc();
4984 if (mxGroup)
4985 mxGroup->endAllGroupListening(rDoc);
4987 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
4988 return;
4990 rDoc.SetDetectiveDirty(true); // It has changed something
4992 ScTokenArray* pArr = GetCode();
4993 if( pArr->IsRecalcModeAlways() )
4995 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
4996 SetNeedsListening( false);
4997 return;
5000 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
5001 formula::FormulaToken* t;
5002 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5004 switch (t->GetType())
5006 case svSingleRef:
5008 ScAddress aCell = t->GetSingleRef()->toAbs(aPos);
5009 if (aCell.IsValid())
5010 rDoc.StartListeningCell(rCxt, aCell, *this);
5012 break;
5013 case svDoubleRef:
5014 startListeningArea(this, rDoc, aPos, *t);
5015 break;
5016 default:
5017 ; // nothing
5020 SetNeedsListening( false);
5023 namespace {
5025 void endListeningArea(
5026 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
5028 const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
5029 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
5030 ScAddress aCell1 = rRef1.toAbs(rPos);
5031 ScAddress aCell2 = rRef2.toAbs(rPos);
5032 if (aCell1.IsValid() && aCell2.IsValid())
5034 if (rToken.GetOpCode() == ocColRowNameAuto)
5035 { // automagically
5036 if ( rRef1.IsColRel() )
5037 { // ColName
5038 aCell2.SetRow(MAXROW);
5040 else
5041 { // RowName
5042 aCell2.SetCol(MAXCOL);
5046 rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell);
5052 void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
5053 ScAddress aCellPos )
5055 if (mxGroup)
5056 mxGroup->endAllGroupListening(*pDoc);
5058 if (pDoc->IsClipOrUndo() || IsInChangeTrack())
5059 return;
5061 if (!HasBroadcaster())
5062 return;
5064 pDoc->SetDetectiveDirty(true); // It has changed something
5066 if ( GetCode()->IsRecalcModeAlways() )
5068 pDoc->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
5069 return;
5072 if (!pArr)
5074 pArr = GetCode();
5075 aCellPos = aPos;
5077 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
5078 formula::FormulaToken* t;
5079 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5081 switch (t->GetType())
5083 case svSingleRef:
5085 ScAddress aCell = t->GetSingleRef()->toAbs(aCellPos);
5086 if (aCell.IsValid())
5087 pDoc->EndListeningCell(aCell, this);
5089 break;
5090 case svDoubleRef:
5091 endListeningArea(this, *pDoc, aCellPos, *t);
5092 break;
5093 default:
5094 ; // nothing
5099 void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
5101 if (mxGroup)
5102 mxGroup->endAllGroupListening(rCxt.getDoc());
5104 if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
5105 return;
5107 if (!HasBroadcaster())
5108 return;
5110 ScDocument& rDoc = rCxt.getDoc();
5111 rDoc.SetDetectiveDirty(true); // It has changed something
5113 ScTokenArray* pArr = rCxt.getOldCode();
5114 ScAddress aCellPos = rCxt.getOldPosition(aPos);
5115 if (!pArr)
5116 pArr = pCode;
5118 if (pArr->IsRecalcModeAlways())
5120 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
5121 return;
5124 formula::FormulaTokenArrayPlainIterator aIter(*pArr);
5125 formula::FormulaToken* t;
5126 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5128 switch (t->GetType())
5130 case svSingleRef:
5132 ScAddress aCell = t->GetSingleRef()->toAbs(aCellPos);
5133 if (aCell.IsValid())
5134 rDoc.EndListeningCell(rCxt, aCell, *this);
5136 break;
5137 case svDoubleRef:
5138 endListeningArea(this, rDoc, aCellPos, *t);
5139 break;
5140 default:
5141 ; // nothing
5146 bool ScFormulaCell::IsShared() const
5148 return mxGroup.get() != nullptr;
5151 bool ScFormulaCell::IsSharedTop() const
5153 if (!mxGroup)
5154 return false;
5156 return mxGroup->mpTopCell == this;
5159 SCROW ScFormulaCell::GetSharedTopRow() const
5161 return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1;
5164 SCROW ScFormulaCell::GetSharedLength() const
5166 return mxGroup ? mxGroup->mnLength : 0;
5169 sal_Int32 ScFormulaCell::GetWeight() const
5171 if (!mxGroup)
5172 return 1;
5174 if (mxGroup->mnWeight > 0)
5175 return mxGroup->mnWeight;
5177 double nSharedCodeWeight = GetSharedCode()->GetWeight();
5178 double nResult = nSharedCodeWeight * GetSharedLength();
5179 if (nResult < SAL_MAX_INT32)
5180 mxGroup->mnWeight = nResult;
5181 else
5182 mxGroup->mnWeight = SAL_MAX_INT32;
5184 return mxGroup->mnWeight;
5187 ScTokenArray* ScFormulaCell::GetSharedCode()
5189 return mxGroup ? mxGroup->mpCode : nullptr;
5192 const ScTokenArray* ScFormulaCell::GetSharedCode() const
5194 return mxGroup ? mxGroup->mpCode : nullptr;
5197 void ScFormulaCell::SyncSharedCode()
5199 if (!mxGroup)
5200 // Not a shared formula cell.
5201 return;
5203 pCode = mxGroup->mpCode;
5206 #if DUMP_COLUMN_STORAGE
5208 void ScFormulaCell::Dump() const
5210 cout << "-- formula cell (" << aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, pDocument) << ")" << endl;
5211 cout << " * shared: " << (mxGroup ? "true" : "false") << endl;
5212 if (mxGroup)
5214 cout << " * shared length: " << mxGroup->mnLength << endl;
5215 cout << " * shared calc state: " << mxGroup->meCalcState << endl;
5218 sc::TokenStringContext aCxt(pDocument, pDocument->GetGrammar());
5219 cout << " * code: " << pCode->CreateString(aCxt, aPos) << endl;
5221 FormulaError nErrCode = pCode->GetCodeError();
5222 cout << " * code error: ";
5223 if (nErrCode == FormulaError::NONE)
5224 cout << "(none)";
5225 else
5227 OUString aStr = ScGlobal::GetErrorString(nErrCode);
5228 cout << " * code error: " << aStr << " (" << int(nErrCode) << ")";
5230 cout << endl;
5232 cout << " * result: ";
5233 sc::FormulaResultValue aRV = aResult.GetResult();
5234 switch (aRV.meType)
5236 case sc::FormulaResultValue::Value:
5237 cout << aRV.mfValue << " (value)";
5238 break;
5239 case sc::FormulaResultValue::String:
5240 cout << aRV.maString.getString() << " (string)";
5241 break;
5242 case sc::FormulaResultValue::Error:
5243 cout << ScGlobal::GetErrorString(aRV.mnError) << " (error: " << int(aRV.mnError) << ")";
5244 break;
5245 case sc::FormulaResultValue::Invalid:
5246 cout << "(invalid)";
5247 break;
5248 default:
5251 cout << endl;
5254 #endif
5256 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */