1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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>
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>
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>
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>
78 using namespace formula
;
80 #define DEBUG_CALCULATION 0
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
89 const ScDocument
* mpDoc
;
91 sal_uInt16 mnRecursion
;
93 DebugCalculationEntry( const ScAddress
& rPos
, ScDocument
* pDoc
, sal_uInt32 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
;
119 DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive
), mbSwitchOff(false),
120 mbPrint(true), mbPrintResults(false) {}
122 /** Print chain in encountered dependency order. */
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
));
176 struct DebugCalculationStacker
178 DebugCalculationStacker( const ScAddress
& rPos
, ScDocument
* pDoc
)
180 if (!aDC
.mbActive
&& rPos
== aDC
.maTrigger
)
181 aDC
.mbActive
= aDC
.mbSwitchOff
= true;
184 aDC
.mvPos
.push_back( DebugCalculationEntry( rPos
, pDoc
, aDC
.mnGroup
));
189 ~DebugCalculationStacker()
193 if (!aDC
.mvPos
.empty())
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())
209 std::vector
< DebugCalculationEntry
>().swap( aDC
.mvResults
);
211 if (aDC
.mbSwitchOff
&& aDC
.mvPos
.empty())
212 aDC
.mbActive
= false;
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.
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
);
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)
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.
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
);
290 const SingleDoubleRefProvider
aRef(**rBegin
);
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
);
302 bOk
= lcl_checkRangeDimensions(rPos
, aRef
, aRefCur
, bColTmp
, bRowTmp
, bTabTmp
);
303 bOk
= bOk
&& (bCol
== bColTmp
&& bRow
== bRowTmp
&& bTab
== bTabTmp
);
307 return bOk
&& aCur
== rEnd
;
310 class LessByReference
312 ScAddress
const maPos
;
313 DimensionSelector
const maFunc
;
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
331 class AdjacentByReference
333 ScAddress
const maPos
;
334 DimensionSelector
const maFunc
;
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;
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
);
356 return std::equal(aBegin
, aEnd
, aBegin1
, AdjacentByReference(rPos
, aWhich
));
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
);
372 lcl_refListFormsOneRange(
373 const ScAddress
& rPos
, std::vector
<formula::FormulaToken
*>& rReferences
,
376 if (rReferences
.size() == 1)
378 lcl_fillRangeFromRefList(rPos
, rReferences
, rRange
);
385 if (lcl_checkRangeDimensions(rPos
, rReferences
.begin(), rReferences
.end(), bCell
, bRow
, bTab
))
387 DimensionSelector aWhich
;
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
);
417 bool lcl_isReference(const FormulaToken
& rToken
)
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
435 // If this happened we have a real problem.
437 OSL_FAIL("inserting the range name should not fail");
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());
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
);
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
)
506 typedef std::map
<AreaListenerKey
, std::unique_ptr
<sc::FormulaGroupAreaListener
>> AreaListenersType
;
510 struct ScFormulaCellGroup::Impl
512 AreaListenersType m_AreaListeners
;
515 ScFormulaCellGroup::ScFormulaCellGroup() :
522 mnFormatType(SvNumFormatType::NUMBER
),
525 mbPartOfCycle(false),
526 meCalcState(sc::GroupCalcEnabled
)
530 ScFormulaCellGroup::~ScFormulaCellGroup()
535 void ScFormulaCellGroup::setCode( const ScTokenArray
& rCode
)
538 mpCode
= rCode
.Clone();
539 mbInvariant
= mpCode
->IsInvariant();
543 void ScFormulaCellGroup::setCode( ScTokenArray
* pCode
)
546 mpCode
= pCode
; // takes ownership of the token array.
547 mpCode
->Finalize(); // Reduce memory usage if needed.
548 mbInvariant
= mpCode
->IsInvariant();
552 void ScFormulaCellGroup::compileCode(
553 ScDocument
& rDoc
, const ScAddress
& rPos
, FormulaGrammar::Grammar eGram
)
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();
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
))
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
) :
605 bTableOpDirty(false),
611 bInChangeTrack(false),
612 bNeedListening(false),
613 mbNeedsNumberFormat(false),
614 mbAllowNumberFormatChange(false),
615 mbPostponedDirty(false),
618 cMatrixFlag(ScMatrixMode::NONE
),
620 nFormatType(SvNumFormatType::NUMBER
),
621 eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT
),
622 pCode(new ScTokenArray
),
626 pPreviousTrack(nullptr),
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 ),
642 bIsIterCell( false ),
643 bInChangeTrack( false ),
644 bNeedListening( false ),
645 mbNeedsNumberFormat( false ),
646 mbAllowNumberFormatChange(false),
647 mbPostponedDirty(false),
650 cMatrixFlag ( cMatInd
),
652 nFormatType ( SvNumFormatType::NUMBER
),
653 eTempGrammar( eGrammar
),
658 pPreviousTrack(nullptr),
662 Compile( rFormula
, true, eGrammar
); // bNoListening, Insert does that
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
) :
672 bTableOpDirty( false ),
677 bIsIterCell( false ),
678 bInChangeTrack( false ),
679 bNeedListening( false ),
680 mbNeedsNumberFormat( false ),
681 mbAllowNumberFormatChange(false),
682 mbPostponedDirty(false),
685 cMatrixFlag ( cMatInd
),
687 nFormatType ( SvNumFormatType::NUMBER
),
688 eTempGrammar( eGrammar
),
693 pPreviousTrack(nullptr),
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();
710 if ( pCode
->HasOpCodeRPN( ocSubTotal
) || pCode
->HasOpCodeRPN( ocAggregate
) )
715 pDocument
->AddSubTotalCell(this);
720 ScFormulaCell::ScFormulaCell(
721 ScDocument
* pDoc
, const ScAddress
& rPos
, const ScTokenArray
& rArray
,
722 const FormulaGrammar::Grammar eGrammar
, ScMatrixMode cMatInd
) :
724 bTableOpDirty( false ),
729 bIsIterCell( false ),
730 bInChangeTrack( false ),
731 bNeedListening( false ),
732 mbNeedsNumberFormat( false ),
733 mbAllowNumberFormatChange(false),
734 mbPostponedDirty(false),
737 cMatrixFlag ( cMatInd
),
739 nFormatType ( SvNumFormatType::NUMBER
),
740 eTempGrammar( eGrammar
),
741 pCode(new ScTokenArray(rArray
)), // also implicitly does Finalize() on the array
745 pPreviousTrack(nullptr),
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();
758 if ( pCode
->HasOpCodeRPN( ocSubTotal
) || pCode
->HasOpCodeRPN( ocAggregate
) )
763 pDocument
->AddSubTotalCell(this);
768 ScFormulaCell::ScFormulaCell(
769 ScDocument
* pDoc
, const ScAddress
& rPos
, const ScFormulaCellGroupRef
& xGroup
,
770 const FormulaGrammar::Grammar eGrammar
, ScMatrixMode cInd
) :
773 bTableOpDirty( false ),
777 bSubTotal(xGroup
->mbSubTotal
),
778 bIsIterCell( false ),
779 bInChangeTrack( false ),
780 bNeedListening( false ),
781 mbNeedsNumberFormat( false ),
782 mbAllowNumberFormatChange(false),
783 mbPostponedDirty(false),
786 cMatrixFlag ( cInd
),
788 nFormatType(xGroup
->mnFormatType
),
789 eTempGrammar( eGrammar
),
790 pCode(xGroup
->mpCode
? xGroup
->mpCode
: new ScTokenArray
),
794 pPreviousTrack(nullptr),
799 pDocument
->AddSubTotalCell(this);
802 ScFormulaCell::ScFormulaCell(const ScFormulaCell
& rCell
, ScDocument
& rDoc
, const ScAddress
& rPos
, ScCloneFlags nCloneFlags
) :
804 bDirty( rCell
.bDirty
),
805 bTableOpDirty( false ),
806 bChanged( rCell
.bChanged
),
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),
818 cMatrixFlag ( rCell
.cMatrixFlag
),
820 nFormatType( rCell
.nFormatType
),
821 aResult( rCell
.aResult
),
822 eTempGrammar( rCell
.eTempGrammar
),
826 pPreviousTrack(nullptr),
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
);
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
);
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.
889 else if ( t
->GetType() == svIndex
)
891 const ScRangeData
* pRangeData
= rDoc
.FindRangeNameBySheetAndIndex( t
->GetSheet(), t
->GetIndex());
894 if( pRangeData
->HasReferences() )
898 bCompile
= true; // invalid reference!
900 else if ( t
->GetOpCode() == ocColRowName
)
902 bCompile
= true; // new lookup needed
903 bCompileLater
= bClipMode
;
909 if ( !bCompileLater
&& bClipMode
)
911 // Merging ranges needs the actual positions after UpdateReference.
912 // ColRowNames and TableRefs need new lookup after positions are
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
);
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.
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());
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();
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
);
995 pCell
->GetFormula( rBuffer
, eGrammar
, pContext
);
1000 ScCompiler
aComp( pDocument
, aPos
, *pCode
, eGrammar
, false, false, pContext
);
1001 aComp
.CreateStringFromTokenArray( rBuffer
);
1006 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
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())
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();
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
);
1061 return pCell
->GetFormula(rCxt
);
1065 ScCompiler
aComp(rCxt
, aPos
, *pCode
, false, false, pContext
);
1066 aComp
.CreateStringFromTokenArray(aBuf
);
1071 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
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, '{');
1087 return aBuf
.makeStringAndClear();
1090 void ScFormulaCell::GetResultDimensions( SCSIZE
& rCols
, SCSIZE
& rRows
)
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
);
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() )
1123 bool bWasInFormulaTree
= pDocument
->IsInFormulaTree( this );
1124 if ( bWasInFormulaTree
)
1125 pDocument
->RemoveFromFormulaTree( this );
1126 // pCode may not deleted for queries, but must be empty
1129 ScTokenArray
* pCodeOld
= pCode
;
1130 ScCompiler
aComp( pDocument
, aPos
, eGrammar
);
1131 pCode
= aComp
.CompileString( rFormula
);
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) );
1140 pCode
->AddBad( rFormula
);
1143 CompileTokenArray( bNoListening
);
1148 if ( bWasInFormulaTree
)
1149 pDocument
->PutInFormulaTree( this );
1152 void ScFormulaCell::Compile(
1153 sc::CompileFormulaContext
& rCxt
, const OUString
& rFormula
, bool bNoListening
)
1155 if ( pDocument
->IsClipOrUndo() )
1157 bool bWasInFormulaTree
= pDocument
->IsInFormulaTree( this );
1158 if ( bWasInFormulaTree
)
1159 pDocument
->RemoveFromFormulaTree( this );
1160 // pCode may not deleted for queries, but must be empty
1163 ScTokenArray
* pCodeOld
= pCode
;
1164 ScCompiler
aComp(rCxt
, aPos
);
1165 pCode
= aComp
.CompileString( rFormula
);
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) );
1174 pCode
->AddBad( rFormula
);
1177 CompileTokenArray(rCxt
, bNoListening
);
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();
1212 aResult
.SetToken( nullptr);
1214 if ( !bNoListening
)
1215 StartListeningTo( pDocument
);
1217 if ( bWasInFormulaTree
)
1218 pDocument
->PutInFormulaTree( this );
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();
1252 aResult
.SetToken( nullptr);
1254 if ( !bNoListening
)
1255 StartListeningTo( pDocument
);
1257 if ( bWasInFormulaTree
)
1258 pDocument
->PutInFormulaTree( this );
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
);
1274 // Error constant formula cell stays as is.
1275 if (!pCode
->GetLen() && pCode
->GetCodeError() != FormulaError::NONE
)
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
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);
1318 SetCellGroup( xGroup
);
1320 // Do setup here based on previous cell.
1322 nFormatType
= pPreviousCell
->nFormatType
;
1323 bSubTotal
= pPreviousCell
->bSubTotal
;
1328 pDocument
->AddSubTotalCell(this);
1331 pCode
= pPreviousCell
->pCode
;
1332 if (pPreviousCell
->mbIsExtRef
)
1333 pDocument
->GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell
, this );
1340 ScTokenArray
* pCodeOld
= pCode
;
1341 pCode
= aComp
.CompileString( aFormula
, aFormulaNmsp
);
1344 if( pCode
->GetCodeError() == FormulaError::NONE
)
1346 if ( !pCode
->GetLen() )
1348 if ( !aFormula
.isEmpty() && aFormula
[0] == '=' )
1349 pCode
->AddBad( aFormula
.copy( 1 ) );
1351 pCode
->AddBad( aFormula
);
1353 bSubTotal
= aComp
.CompileTokenArray();
1354 if( pCode
->GetCodeError() == FormulaError::NONE
)
1356 nFormatType
= aComp
.GetNumFormatType();
1362 pDocument
->AddSubTotalCell(this);
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.
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);
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();
1406 bNewCompiled
= true;
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
);
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() )
1441 if ( pCode
->IsRecalcModeAlways() )
1442 { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
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
);
1455 class RecursionCounter
1457 ScRecursionHelper
& rRec
;
1458 bool bStackedInIteration
;
1460 const ScFormulaCell
* cell
;
1463 RecursionCounter( ScRecursionHelper
& r
, ScFormulaCell
* p
)
1469 bStackedInIteration
= rRec
.IsDoingIteration();
1470 if (bStackedInIteration
)
1471 rRec
.GetRecursionInIterationStack().push( p
);
1472 rRec
.IncRecursionCount();
1476 rRec
.DecRecursionCount();
1477 if (bStackedInIteration
)
1480 assert(rRec
.GetRecursionInIterationStack().top() == cell
);
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
);
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
);
1511 if (!IsDirtyOrInTableOpDirty() || rRecursionHelper
.IsInReturn())
1512 return; // no double/triple processing
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() )
1522 if (!pDocument
->GetDocOptions().IsIter())
1524 aResult
.SetResultError( FormulaError::CircularReference
);
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);
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())
1545 bool bOldRunning
= bRunning
;
1546 if (rRecursionHelper
.GetRecursionCount() > MAXRECURSION
)
1549 rRecursionHelper
.SetInRecursionReturn( true);
1553 pDocument
->IncInterpretLevel();
1555 #if DEBUG_CALCULATION
1558 bool bPartOfCycleBefore
= mxGroup
&& mxGroup
->mbPartOfCycle
;
1559 bool bGroupInterpreted
= InterpretFormulaGroup();
1560 bool bPartOfCycleAfter
= mxGroup
&& mxGroup
->mbPartOfCycle
;
1562 #if DEBUG_CALCULATION
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();
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();
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
!=
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;
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())
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();
1693 rDone
= !pIterCell
->IsDirtyOrInTableOpDirty();
1698 rDone
= !pIterCell
->IsDirtyOrInTableOpDirty();
1701 if (rRecursionHelper
.IsInReturn())
1703 bResumeIteration
= true;
1705 // Don't increment iteration.
1708 if (!bResumeIteration
)
1712 for (ScFormulaRecursionList::const_iterator
aIter(
1713 rRecursionHelper
.GetIterationStart());
1714 aIter
!= rRecursionHelper
.GetIterationEnd();
1717 ScFormulaCell
* pIterCell
= (*aIter
).pCell
;
1718 pIterCell
->bIsIterCell
= false;
1719 pIterCell
->nSeenInIteration
= 0;
1720 pIterCell
->bRunning
= (*aIter
).bOldRunning
;
1725 for (ScFormulaRecursionList::const_iterator
aIter(
1726 rRecursionHelper
.GetIterationStart());
1727 aIter
!= rRecursionHelper
.GetIterationEnd();
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.
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;
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());
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());
1813 aDC
.storeResult( aResult
.GetString());
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?
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
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.
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
);
1864 bool bOldRunning
= bRunning
;
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() );
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
)
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
1897 aResult
.SetResultError( FormulaError::CircularReference
);
1904 if (eTailParam
== SCITP_FROM_ITERATION
&& IsDirtyOrInTableOpDirty())
1906 bool bIsValue
= aResult
.IsValue(); // the previous type
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)
1930 if( pInterpreter
->GetError() != nOldErrCode
)
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
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
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
)
1971 if ((fVal
= aNewResult
.GetDouble()) != 1.0 && fVal
!= 0.0)
1972 bForceNumberFormat
= false;
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;
1985 case SvNumFormatType::NUMBER
:
1986 if ((nOldFormatIndex
% SV_COUNTRY_LANGUAGE_OFFSET
) != 0)
1987 bForceNumberFormat
= false;
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.
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
)
2038 if (eNewCellResultType
!= svDouble
)
2041 nFormatType
= nOldFormatType
; // that? or number?
2043 else if ((fVal
= aNewResult
.GetDouble()) != 1.0 && fVal
!= 0.0)
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
);
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
);
2079 mbNeedsNumberFormat
= false;
2082 // In case of changes just obtain the result, no temporary and
2083 // comparison needed anymore.
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
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() );
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
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
)
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();
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.
2207 case ScInterpreter::VOLATILE_MACRO
:
2208 // The formula contains a volatile macro.
2209 pCode
->SetExclusiveRecalcModeAlways();
2210 pDocument
->PutInFormulaTree(this);
2211 StartListeningTo(pDocument
);
2213 case ScInterpreter::NOT_VOLATILE
:
2214 if (pCode
->IsRecalcModeAlways())
2216 // The formula was previously volatile, but no more.
2217 EndListeningTo(pDocument
);
2218 pCode
->SetExclusiveRecalcModeNormal();
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);
2235 // Cells with compiler errors should not be marked dirty forever
2236 OSL_ENSURE( pCode
->GetCodeError() != FormulaError::NONE
, "no RPN code and no errors ?!?!" );
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
);
2258 case ScInterpreter::NOT_VOLATILE
:
2259 if (pCode
->IsRecalcModeAlways())
2261 // The formula was previously volatile, but no more.
2262 EndListeningTo(pDocument
);
2263 pCode
->SetExclusiveRecalcModeNormal();
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);
2279 void ScFormulaCell::SetCompile( bool bVal
)
2284 void ScFormulaCell::SetMatColsRows( SCCOL nCols
, SCROW nRows
)
2286 ScMatrixFormulaCellToken
* pMat
= aResult
.GetMatrixFormulaCellTokenNonConst();
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.
2298 void ScFormulaCell::GetMatColsRows( SCCOL
& nCols
, SCROW
& nRows
) const
2300 const ScMatrixFormulaCellToken
* pMat
= aResult
.GetMatrixFormulaCellToken();
2302 pMat
->GetMatColsRows( nCols
, nRows
);
2310 void ScFormulaCell::SetInChangeTrack( bool bVal
)
2312 bInChangeTrack
= bVal
;
2315 void ScFormulaCell::Notify( const SfxHint
& rHint
)
2317 if (pDocument
->IsInDtorClear())
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.
2339 CompileTokenArray();
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());
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());
2369 case sc::RefHint::StartListening
:
2371 StartListeningTo( pDocument
);
2374 case sc::RefHint::StopListening
:
2376 EndListeningTo( pDocument
);
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;
2402 bForceTrack
= !bDirty
;
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
);
2430 rRefQuery
.add(aPos
);
2438 void ScFormulaCell::SetDirty( bool bDirtyFlag
)
2440 if (IsInChangeTrack())
2443 if ( pDocument
->GetHardRecalcState() != ScDocument::HardRecalcState::OFF
)
2446 pDocument
->SetStreamValid(aPos
.Tab(), false);
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 ) )
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()
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()
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;
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
)
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
;
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
);
2615 double fValue
= GetValue();
2616 pFormatter
->GetOutputString( fValue
, nCellFormat
, rCellText
, &pColor
);
2620 aCellString
= GetString().getString();
2621 pFormatter
->GetOutputString( aCellString
, nCellFormat
, rCellText
, &pColor
);
2623 ScConstMatrixRef
xMat( aResult
.GetMatrix());
2626 // determine if the matrix result is a string or value.
2627 if (!xMat
->IsValue(0, 1))
2628 rURL
= xMat
->GetString(0, 1).getString();
2630 pFormatter
->GetOutputString(
2631 xMat
->GetDouble(0, 1), nURLFormat
, rURL
, &pColor
);
2637 pFormatter
->GetOutputString( GetValue(), nURLFormat
, rURL
, &pColor
);
2639 pFormatter
->GetOutputString( aCellString
, nURLFormat
, rURL
, &pColor
);
2643 bool ScFormulaCell::IsMultilineResult()
2646 return aResult
.IsMultiline();
2650 bool ScFormulaCell::IsHyperLinkCell() const
2652 return pCode
&& pCode
->IsHyperLink();
2655 std::unique_ptr
<EditTextObject
> ScFormulaCell::CreateURLObject()
2659 GetURLResult( aURL
, aCellText
);
2661 return ScEditUtil::CreateURLObjectFromURL( *pDocument
, aURL
, aCellText
);
2664 bool ScFormulaCell::IsEmpty()
2667 return aResult
.GetCellResultType() == formula::svEmptyCell
;
2670 bool ScFormulaCell::IsEmptyDisplayedAsString()
2673 return aResult
.IsEmptyDisplayedAsString();
2676 bool ScFormulaCell::IsValue()
2679 return aResult
.IsValue();
2682 bool ScFormulaCell::IsValueNoError()
2685 if (pCode
->GetCodeError() != FormulaError::NONE
)
2688 return aResult
.IsValueNoError();
2691 bool ScFormulaCell::IsValueNoError() const
2693 if (NeedsInterpret())
2694 // false if the cell is dirty & needs to be interpreted.
2697 if (pCode
->GetCodeError() != FormulaError::NONE
)
2700 return aResult
.IsValueNoError();
2703 double ScFormulaCell::GetValue()
2706 return GetRawValue();
2709 svl::SharedString
ScFormulaCell::GetString()
2712 return GetRawString();
2715 double ScFormulaCell::GetRawValue() const
2717 if ((pCode
->GetCodeError() == FormulaError::NONE
) &&
2718 aResult
.GetResultError() == FormulaError::NONE
)
2719 return aResult
.GetDouble();
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()))
2741 return aResult
.GetMatrix().get();
2744 bool ScFormulaCell::GetMatrixOrigin( ScAddress
& rPos
) const
2746 switch ( cMatrixFlag
)
2748 case ScMatrixMode::Formula
:
2751 case ScMatrixMode::Reference
:
2753 formula::FormulaTokenArrayPlainIterator
aIter(*pCode
);
2754 formula::FormulaToken
* t
= aIter
.GetNextReferenceRPN();
2757 ScSingleRefData
& rRef
= *t
->GetSingleRef();
2758 ScAddress aAbs
= rRef
.toAbs(aPos
);
2759 if (ValidAddress(aAbs
))
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
;
2782 if ( !GetMatrixOrigin( aOrg
) )
2783 return sc::MatrixEdge::Nothing
;
2784 if ( aOrg
!= rOrgPos
)
2785 { // First time or a different matrix than last time.
2787 const ScFormulaCell
* pFCell
;
2788 if ( cMatrixFlag
== ScMatrixMode::Reference
)
2789 pFCell
= pDocument
->GetFormulaCell(aOrg
);
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.
2802 ScFormulaCell
* pCell
;
2803 ScAddress
aAdr( aOrg
);
2808 pCell
= pDocument
->GetFormulaCell(aAdr
);
2809 if (pCell
&& pCell
->cMatrixFlag
== ScMatrixMode::Reference
&&
2810 pCell
->GetMatrixOrigin(aTmpOrg
) && aTmpOrg
== aOrg
)
2823 pCell
= pDocument
->GetFormulaCell(aAdr
);
2824 if (pCell
&& pCell
->cMatrixFlag
== ScMatrixMode::Reference
&&
2825 pCell
->GetMatrixOrigin(aTmpOrg
) && aTmpOrg
== aOrg
)
2834 const_cast<ScFormulaCell
*>(pFCell
)->SetMatColsRows(nC
, nR
);
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
)
2843 << aOrg
.Format(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
, pDocument
) );
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
)
2855 nEdges
|= sc::MatrixEdge::Left
;
2857 nEdges
|= sc::MatrixEdge::Right
;
2859 nEdges
|= sc::MatrixEdge::Top
;
2861 nEdges
|= sc::MatrixEdge::Bottom
;
2862 if ( nEdges
== sc::MatrixEdge::Nothing
)
2863 nEdges
= sc::MatrixEdge::Inside
;
2867 SAL_WARN( "sc", "broken Matrix, Pos: "
2868 << aPos
.Format(ScRefFlags::COL_VALID
| ScRefFlags::ROW_VALID
, pDocument
)
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
));
2879 return sc::MatrixEdge::Nothing
;
2883 FormulaError
ScFormulaCell::GetErrCode()
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
)
2893 return aResult
.GetResultError();
2896 FormulaError
ScFormulaCell::GetRawError()
2898 FormulaError nErr
= pCode
->GetCodeError();
2899 if (nErr
!= FormulaError::NONE
)
2901 return aResult
.GetResultError();
2904 bool ScFormulaCell::GetErrorOrValue( FormulaError
& rErr
, double& rVal
)
2908 rErr
= pCode
->GetCodeError();
2909 if (rErr
!= FormulaError::NONE
)
2912 return aResult
.GetErrorOrDouble(rErr
, rVal
);
2915 sc::FormulaResultValue
ScFormulaCell::GetResult()
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
);
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
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
))
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
2974 std::vector
<formula::FormulaToken
*> aReferences
;
2975 aReferences
.push_back(pFirstReference
);
2976 FormulaToken
* pToken(aIter
.NextRPN());
2977 FormulaToken
* pFunction(nullptr);
2980 if (lcl_isReference(*pToken
))
2982 aReferences
.push_back(pToken
);
2983 pToken
= aIter
.NextRPN();
2987 if (pToken
->IsFunction())
2994 if (pFunction
&& !aIter
.GetNextReferenceRPN()
2995 && (pFunction
->GetParamCount() == aReferences
.size()))
2997 return lcl_refListFormsOneRange(aPos
, aReferences
, rRange
);
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
;
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.
3033 bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext
& rCxt
)
3035 if (rCxt
.meMode
!= URM_INSDEL
)
3039 if (!rCxt
.mnColDelta
&& !rCxt
.mnRowDelta
&& !rCxt
.mnTabDelta
)
3043 if (!rCxt
.maRange
.In(aPos
))
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");
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
)
3070 if (rCxt
.mnColDelta
<= 0 && rCxt
.mnRowDelta
<= 0)
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())
3082 ScAddress aAdr
= rRef
.toAbs(aPos
);
3083 ScRangePair
* pR
= pColList
->Find( aAdr
);
3086 if (pR
->GetRange(1).aStart
.Row() == rCxt
.maRange
.aStart
.Row())
3091 if (aAdr
.Row() + 1 == rCxt
.maRange
.aStart
.Row())
3095 if (rCxt
.mnColDelta
> 0 && rRef
.IsRowRel())
3097 ScAddress aAdr
= rRef
.toAbs(aPos
);
3098 ScRangePair
* pR
= pRowList
->Find( aAdr
);
3101 if ( pR
->GetRange(1).aStart
.Col() == rCxt
.maRange
.aStart
.Col())
3106 if (aAdr
.Col() + 1 == rCxt
.maRange
.aStart
.Col())
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
);
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
))
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
)
3156 ScFormulaCell
* pFCell
=
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
)
3173 bool bCellStateChanged
= false;
3174 ScAddress
aUndoPos( aPos
); // position for undo cell in pUndoDoc
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;
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
;
3197 pOldCode
.reset(pCode
->Clone());
3199 bool bValChanged
= false;
3200 bool bRefModified
= false;
3201 bool bRecompile
= bCompile
;
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
)
3213 if (bValChanged
|| bRefModified
)
3214 bCellStateChanged
= true;
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;
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;
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
;
3259 CompileTokenArray( bNewListening
); // no Listening
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);
3277 { // Cut off references, invalid or similar?
3278 // Postpone SetDirty() until all listeners have been re-established in
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
)
3292 ScAddress
aUndoPos( aPos
); // position for undo cell in pUndoDoc
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;
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.
3323 bool bCellStateChanged
= false;
3324 std::unique_ptr
<ScTokenArray
> pOldCode
;
3326 pOldCode
.reset(pCode
->Clone());
3328 bool bValChanged
= false;
3329 bool bRefModified
= false;
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.
3342 if (bValChanged
|| bRefModified
)
3343 bCellStateChanged
= true;
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;
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
)
3387 if (pUndoDoc
&& !bCellInMoveTarget
&& (bValChanged
|| bRefModified
|| bOnRefMove
))
3388 setOldCodeToUndo(pUndoDoc
, aUndoPos
, pOldCode
.get(), eTempGrammar
, cMatrixFlag
);
3390 bValChanged
= false;
3392 bCompile
= (bCompile
|| bValChanged
|| bColRowNameCompile
);
3395 CompileTokenArray( bNewListening
); // no Listening
3399 if ( !bInDeleteUndo
)
3400 { // In ChangeTrack Delete-Reject listeners are established in
3401 // InsertCol/InsertRow
3402 if ( bNewListening
)
3404 StartListeningTo( pDocument
);
3409 { // Cut off references, invalid or similar?
3410 sc::AutoCalcSwitch
aACSwitch(*pDocument
, false);
3414 return bCellStateChanged
;
3417 bool ScFormulaCell::UpdateReferenceOnCopy(
3418 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
)
3420 if (rCxt
.meMode
!= URM_COPY
)
3423 ScAddress
aUndoPos( aPos
); // position for undo cell in pUndoDoc
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.
3448 std::unique_ptr
<ScTokenArray
> pOldCode
;
3450 pOldCode
.reset(pCode
->Clone());
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
);
3463 CompileTokenArray(); // no Listening
3468 { // Cut off references, invalid or similar?
3469 sc::AutoCalcSwitch
aACSwitch(*pDocument
, false);
3476 bool ScFormulaCell::UpdateReference(
3477 const sc::RefUpdateContext
& rCxt
, ScDocument
* pUndoDoc
, const ScAddress
* pUndoCellPos
)
3479 if (pDocument
->IsClipOrUndo())
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
)
3489 return UpdatePosOnShift(rCxt
);
3497 switch (rCxt
.meMode
)
3500 return UpdateReferenceOnShift(rCxt
, pUndoDoc
, pUndoCellPos
);
3502 return UpdateReferenceOnMove(rCxt
, pUndoDoc
, pUndoCellPos
);
3504 return UpdateReferenceOnCopy(rCxt
, pUndoDoc
, pUndoCellPos
);
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())
3520 aPos
.IncTab(rCxt
.mnSheets
);
3525 EndListeningTo( pDocument
);
3526 ScAddress aOldPos
= aPos
;
3527 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3529 aPos
.IncTab(rCxt
.mnSheets
);
3534 sc::RefUpdateResult aRes
= pCode
->AdjustReferenceOnInsertedTab(rCxt
, aOldPos
);
3535 if (aRes
.mbNameModified
)
3536 // Re-compile after new sheet(s) have been inserted.
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())
3550 aPos
.IncTab(-1*rCxt
.mnSheets
);
3554 EndListeningTo( pDocument
);
3555 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3556 ScAddress aOldPos
= aPos
;
3558 aPos
.IncTab(-1*rCxt
.mnSheets
);
3563 sc::RefUpdateResult aRes
= pCode
->AdjustReferenceOnDeletedTab(rCxt
, aOldPos
);
3564 if (aRes
.mbNameModified
)
3565 // Re-compile after sheet(s) have been deleted.
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
);
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!
3590 sc::RefUpdateResult aRes
= pCode
->AdjustReferenceOnMovedTab(rCxt
, aOldPos
);
3591 if (aRes
.mbNameModified
)
3592 // Re-compile after sheet(s) have been deleted.
3596 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable
)
3598 if (pDocument
->IsClipOrUndo())
3601 bool bAdjustCode
= !mxGroup
|| mxGroup
->mpTopCell
== this;
3605 formula::FormulaTokenArrayPlainIterator
aIter(*pCode
);
3606 formula::FormulaToken
* p
= aIter
.GetNextReferenceRPN();
3609 ScSingleRefData
& rRef1
= *p
->GetSingleRef();
3610 if (!rRef1
.IsTabRel() && nTable
<= rRef1
.Tab())
3612 if (p
->GetType() == formula::svDoubleRef
)
3614 ScSingleRefData
& rRef2
= p
->GetDoubleRef()->Ref2
;
3615 if (!rRef2
.IsTabRel() && nTable
<= rRef2
.Tab())
3618 p
= aIter
.GetNextReferenceRPN();
3622 bool ScFormulaCell::TestTabRefAbs(SCTAB nTable
)
3624 if (pDocument
->IsClipOrUndo())
3627 bool bAdjustCode
= !mxGroup
|| mxGroup
->mpTopCell
== this;
3632 formula::FormulaTokenArrayPlainIterator
aIter(*pCode
);
3633 formula::FormulaToken
* p
= aIter
.GetNextReferenceRPN();
3636 ScSingleRefData
& rRef1
= *p
->GetSingleRef();
3637 if (!rRef1
.IsTabRel())
3639 if (nTable
!= rRef1
.Tab())
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())
3651 else if (nTable
!= aPos
.Tab())
3652 rRef2
.SetAbsTab(aPos
.Tab());
3655 p
= aIter
.GetNextReferenceRPN();
3660 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse
)
3662 if ( bForceIfNameInUse
&& !bCompile
)
3663 bCompile
= pCode
->HasNameOrColRowName();
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()) )
3686 nTemp
= rRef1
.Col();
3687 rRef1
.SetRelCol(rRef1
.Row());
3688 rRef1
.SetRelRow(nTemp
);
3692 nTemp
= rRef2
.Col();
3693 rRef2
.SetRelCol(rRef2
.Row());
3694 rRef2
.SetRelRow(nTemp
);
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
);
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())
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
);
3750 rRef
.SetRange(aAbs
, aPos
); // based on the new anchor position.
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
);
3768 CompileTokenArray(); // also call StartListeningTo
3772 StartListeningTo( pDocument
); // Listener as previous
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())
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
);
3802 rRef
.SetRange(aAbs
, aPos
);
3811 CompileTokenArray(); // Also call StartListeningTo
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
,
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
);
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
)
3851 void ScFormulaCell::SetCode( ScTokenArray
* pNew
)
3853 assert(!mxGroup
); // Don't call this if it's shared.
3855 pCode
= pNew
; // takes ownership.
3858 void ScFormulaCell::SetRunning( bool 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
)
3872 CompileTokenArray(rCxt
);
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
)
3887 CompileTokenArray(rCxt
);
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
)
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.
3917 void ScFormulaCell::SetCellGroup( const ScFormulaCellGroupRef
&xRef
)
3921 // Make this cell a non-grouped cell.
3923 pCode
= mxGroup
->mpCode
->Clone();
3929 // Group object has shared token array.
3931 // Currently not shared. Delete the existing token array first.
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
)
3945 // are these formulas at all similar ?
3946 if ( GetHash() != rOther
.GetHash() )
3949 if (!pCode
->IsShareable() || !rOther
.pCode
->IsShareable())
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 !"
3963 if ( nThisLen
!= nOtherLen
)
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())
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.
3989 switch (pThisTok
->GetType())
3991 case formula::svMatrix
:
3992 case formula::svExternalSingleRef
:
3993 case formula::svExternalDoubleRef
:
3994 // Ignoring matrix and external references for now.
3997 case formula::svSingleRef
:
3999 // Single cell reference.
4000 const ScSingleRefData
& rRef
= *pThisTok
->GetSingleRef();
4001 if (rRef
!= *pOtherTok
->GetSingleRef())
4004 if (rRef
.IsRowRel())
4008 case formula::svDoubleRef
:
4011 const ScSingleRefData
& rRef1
= *pThisTok
->GetSingleRef();
4012 const ScSingleRefData
& rRef2
= *pThisTok
->GetSingleRef2();
4013 if (rRef1
!= *pOtherTok
->GetSingleRef())
4016 if (rRef2
!= *pOtherTok
->GetSingleRef2())
4019 if (rRef1
.IsRowRel())
4022 if (rRef2
.IsRowRel())
4026 case formula::svDouble
:
4028 if(!rtl::math::approxEqual(pThisTok
->GetDouble(), pOtherTok
->GetDouble()))
4032 case formula::svString
:
4034 if(pThisTok
->GetString() != pOtherTok
->GetString())
4038 case formula::svIndex
:
4040 if(pThisTok
->GetIndex() != pOtherTok
->GetIndex() || pThisTok
->GetSheet() != pOtherTok
->GetSheet())
4044 case formula::svByte
:
4046 if(pThisTok
->GetByte() != pOtherTok
->GetByte())
4050 case formula::svExternal
:
4052 if (pThisTok
->GetExternal() != pOtherTok
->GetExternal())
4055 if (pThisTok
->GetByte() != pOtherTok
->GetByte())
4059 case formula::svError
:
4061 if (pThisTok
->GetError() != pOtherTok
->GetError())
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 !"
4084 if ( nThisLen
!= nOtherLen
)
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.
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())
4113 if (rRef
.IsRowRel())
4117 case formula::svDoubleRef
:
4120 const ScSingleRefData
& rRef1
= *pThisTok
->GetSingleRef();
4121 const ScSingleRefData
& rRef2
= *pThisTok
->GetSingleRef2();
4122 if (rRef1
!= *pOtherTok
->GetSingleRef())
4125 if (rRef2
!= *pOtherTok
->GetSingleRef2())
4128 if (rRef1
.IsRowRel())
4131 if (rRef2
.IsRowRel())
4135 // All index tokens are names. Different categories already had
4136 // different OpCode values.
4137 case formula::svIndex
:
4139 if (pThisTok
->GetIndex() != pOtherTok
->GetIndex())
4141 switch (pThisTok
->GetOpCode())
4144 // nothing, sheet value assumed as -1, silence
4145 // ScTableRefToken::GetSheet() SAL_WARN about
4149 default: // ocName, ocDBArea
4150 if (pThisTok
->GetSheet() != pOtherTok
->GetSheet())
4160 return bInvariant
? EqualInvariant
: EqualRelativeRef
;
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
)
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
;
4191 } // anonymous namespace
4193 struct ScDependantsCalculator
4196 const ScTokenArray
& mrCode
;
4197 const ScFormulaCellGroupRef
& mxGroup
;
4199 const ScAddress
& mrPos
;
4201 ScDependantsCalculator(ScDocument
& rDoc
, const ScTokenArray
& rCode
, const ScFormulaCell
& rCell
, const ScAddress
& rPos
) :
4204 mxGroup(rCell
.GetCellGroup()),
4205 mnLen(mxGroup
->mnLength
),
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())
4220 SCROW nEndRow
= mrPos
.Row() + mnLen
- 1;
4224 SCROW nTest
= nEndRow
;
4226 if (nTest
>= mrPos
.Row())
4231 SCROW nTest
= mrPos
.Row(); // top row.
4233 if (nTest
<= nEndRow
)
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())
4250 SCROW nEndRow
= mrPos
.Row() + mnLen
- 1;
4252 if (rRefPos
.Row() < mrPos
.Row())
4255 if (rRefPos
.Row() > nEndRow
)
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())
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
)))
4283 if (!bIsRef1RowRel
&& nRefStartRow
<= nStartRow
&&
4284 (nRefEndRow
>= nEndRow
|| (nRefEndRow
+ mnLen
- 1) >= nEndRow
))
4287 if (!bIsRef2RowRel
&&
4288 nRefStartRow
<= nStartRow
&& nRefEndRow
>= nEndRow
)
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.
4310 else if (nLastRow
== 0)
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())
4329 ScSingleRefData aRef
= *p
->GetSingleRef(); // =Sheet1!A1
4330 if( aRef
.IsDeleted())
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;
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()));
4353 if (isSelfReferenceAbsolute(aRefPos
))
4355 bHasSelfReferences
= true;
4359 aRangeList
.Join(ScRange(aRefPos
.Col(), aRefPos
.Row(), aRefPos
.Tab()));
4365 ScComplexRefData aRef
= *p
->GetDoubleRef();
4366 if( aRef
.IsDeleted())
4368 ScRange aAbs
= aRef
.toAbs(mrPos
);
4371 if (aRef
.Ref1
.Tab() != aRef
.Ref2
.Tab())
4374 bool bIsRef1RowRel
= aRef
.Ref1
.IsRowRel();
4375 // Check for self reference.
4378 if (isSelfReferenceRelative(aAbs
.aStart
, aRef
.Ref1
.Row()))
4380 bHasSelfReferences
= true;
4384 else if (isSelfReferenceAbsolute(aAbs
.aStart
))
4386 bHasSelfReferences
= true;
4390 bool bIsRef2RowRel
= aRef
.Ref2
.IsRowRel();
4393 if (isSelfReferenceRelative(aAbs
.aEnd
, aRef
.Ref2
.Row()))
4395 bHasSelfReferences
= true;
4399 else if (isSelfReferenceAbsolute(aAbs
.aEnd
))
4401 bHasSelfReferences
= true;
4405 if (isDoubleRefSpanGroupRange(aAbs
, bIsRef1RowRel
, bIsRef2RowRel
))
4407 bHasSelfReferences
= true;
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
;
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()));
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
))
4452 return !bHasSelfReferences
;
4456 bool ScFormulaCell::InterpretFormulaGroup()
4458 if (!mxGroup
|| !pCode
)
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");
4470 if (mxGroup
->meCalcState
== sc::GroupCalcDisabled
)
4472 aScope
.addMessage("group calc disabled");
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);
4485 if (cMatrixFlag
!= ScMatrixMode::NONE
)
4487 mxGroup
->meCalcState
= sc::GroupCalcDisabled
;
4488 aScope
.addMessage("matrix skipped");
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
))
4505 if( InterpretFormulaGroupThreading(aScope
, bDependencyComputed
, bDependencyCheckFailed
))
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");
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");
4540 if (mxGroup
->mbPartOfCycle
)
4542 mxGroup
->meCalcState
= sc::GroupCalcDisabled
;
4543 rScope
.addMessage("found circular formula-group dependencies");
4547 if (!bOKToParallelize
)
4549 mxGroup
->meCalcState
= sc::GroupCalcDisabled
;
4550 rScope
.addMessage("could not do new dependencies calculation thing");
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;
4574 bDependencyComputed
= true;
4576 const static bool bHyperThreadingActive
= tools::cpuid::hasHyperThreading();
4578 // Then do the threaded calculation
4580 class Executor
: public comphelper::ThreadTask
4583 const unsigned mnThisThread
;
4584 const unsigned mnThreadsTotal
;
4585 ScDocument
* mpDocument
;
4586 ScInterpreterContext
* mpContext
;
4587 const ScAddress
& mrTopPos
;
4588 SCROW
const mnLength
;
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
,
4598 comphelper::ThreadTask(rTag
),
4599 mnThisThread(nThisThread
),
4600 mnThreadsTotal(nThreadsTotal
),
4601 mpDocument(pDocument2
),
4602 mpContext(pContext
),
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 )
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
);
4653 SAL_INFO("sc.threaded", "Done");
4656 pDocument
->HandleStuffAfterParallelCalculation(mxGroup
->mpTopCell
->aPos
, mxGroup
->mnLength
);
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
:
4677 case FormulaVectorDisabledByOpCode
:
4678 aScope
.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
4680 case FormulaVectorDisabledNotInSoftwareSubset
:
4681 aScope
.addMessage("group calc disabled due to vector state (opcode not in software subset)");
4683 case FormulaVectorDisabledByStackVariable
:
4684 aScope
.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
4686 case FormulaVectorDisabledNotInSubSet
:
4687 aScope
.addMessage("group calc disabled due to vector state (opcode not in subset)");
4689 case FormulaVectorDisabled
:
4690 case FormulaVectorUnknown
:
4692 aScope
.addMessage("group calc disabled due to vector state (unknown)");
4699 if (!ScCalcConfig::isOpenCLEnabled())
4701 aScope
.addMessage("opencl not enabled");
4705 if (bDependencyCheckFailed
)
4708 if(!bDependencyComputed
&& !CheckComputeDependencies(aScope
))
4710 bDependencyComputed
= true;
4711 bDependencyCheckFailed
= true;
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
;
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;
4732 if (std::getenv("SC_MAX_GROUP_LENGTH"))
4733 nMaxGroupLength
= std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
4736 const int nNumParts
= splitup(GetSharedLength(), nMaxGroupLength
, nNumOnePlus
);
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
;
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
;
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");
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
) << ")");
4781 SAL_INFO("sc.opencl", "conversion of group " << xGroup
->mpTopCell
->aPos
<< " failed, disabling");
4783 mxGroup
->meCalcState
= sc::GroupCalcDisabled
;
4785 // Undo the hack above
4788 mxGroup
->mpTopCell
->aPos
= aOrigPos
;
4789 xGroup
->mpTopCell
= nullptr;
4790 xGroup
->mpCode
= nullptr;
4793 aScope
.addMessage("group token conversion failed");
4797 // The converted code does not have RPN tokens yet. The interpreter will
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
4811 mxGroup
->mpTopCell
->aPos
= aOrigPos
;
4812 xGroup
->mpTopCell
= nullptr;
4813 xGroup
->mpCode
= nullptr;
4816 aScope
.addMessage("group interpretation unsuccessful");
4820 aScope
.setCalcComplete();
4824 xGroup
->mpTopCell
= nullptr;
4825 xGroup
->mpCode
= nullptr;
4830 mxGroup
->mpTopCell
->aPos
= aOrigPos
;
4831 mxGroup
->meCalcState
= sc::GroupCalcEnabled
;
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.
4843 FormulaTokenArrayPlainIterator
aIter(*pCode
);
4844 for (const formula::FormulaToken
* p
= aIter
.First(); p
; p
= aIter
.Next())
4846 switch (p
->GetType())
4850 ScSingleRefData aRef
= *p
->GetSingleRef();
4851 ScAddress aRefPos
= aRef
.toAbs(aPos
);
4852 formula::FormulaTokenRef pNewToken
= pDocument
->ResolveStaticReference(aRefPos
);
4856 aCode
.AddToken(*pNewToken
);
4861 ScComplexRefData aRef
= *p
->GetDoubleRef();
4862 ScRange aRefRange
= aRef
.toAbs(aPos
);
4863 formula::FormulaTokenRef pNewToken
= pDocument
->ResolveStaticReference(aRefRange
);
4867 aCode
.AddToken(*pNewToken
);
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());
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
);
4896 SAL_WARN("sc.core.formulacell", "GetFormulaCell not found");
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);
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
)
4924 if ( rRef1
.IsColRel() )
4926 aCell2
.SetRow(MAXROW
);
4930 aCell2
.SetCol(MAXCOL
);
4933 rDoc
.StartListeningArea(ScRange(aCell1
, aCell2
), false, pCell
);
4939 void ScFormulaCell::StartListeningTo( ScDocument
* pDoc
)
4942 mxGroup
->endAllGroupListening(*pDoc
);
4944 if (pDoc
->IsClipOrUndo() || pDoc
->GetNoListening() || IsInChangeTrack())
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);
4957 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
4958 formula::FormulaToken
* t
;
4959 while ( ( t
= aIter
.GetNextReferenceRPN() ) != nullptr )
4961 switch (t
->GetType())
4965 ScAddress aCell
= t
->GetSingleRef()->toAbs(aPos
);
4966 if (aCell
.IsValid())
4967 pDoc
->StartListeningCell(aCell
, this);
4971 startListeningArea(this, *pDoc
, aPos
, *t
);
4977 SetNeedsListening( false);
4980 void ScFormulaCell::StartListeningTo( sc::StartListeningContext
& rCxt
)
4982 ScDocument
& rDoc
= rCxt
.getDoc();
4985 mxGroup
->endAllGroupListening(rDoc
);
4987 if (rDoc
.IsClipOrUndo() || rDoc
.GetNoListening() || IsInChangeTrack())
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);
5000 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
5001 formula::FormulaToken
* t
;
5002 while ( ( t
= aIter
.GetNextReferenceRPN() ) != nullptr )
5004 switch (t
->GetType())
5008 ScAddress aCell
= t
->GetSingleRef()->toAbs(aPos
);
5009 if (aCell
.IsValid())
5010 rDoc
.StartListeningCell(rCxt
, aCell
, *this);
5014 startListeningArea(this, rDoc
, aPos
, *t
);
5020 SetNeedsListening( false);
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
)
5036 if ( rRef1
.IsColRel() )
5038 aCell2
.SetRow(MAXROW
);
5042 aCell2
.SetCol(MAXCOL
);
5046 rDoc
.EndListeningArea(ScRange(aCell1
, aCell2
), false, pCell
);
5052 void ScFormulaCell::EndListeningTo( ScDocument
* pDoc
, ScTokenArray
* pArr
,
5053 ScAddress aCellPos
)
5056 mxGroup
->endAllGroupListening(*pDoc
);
5058 if (pDoc
->IsClipOrUndo() || IsInChangeTrack())
5061 if (!HasBroadcaster())
5064 pDoc
->SetDetectiveDirty(true); // It has changed something
5066 if ( GetCode()->IsRecalcModeAlways() )
5068 pDoc
->EndListeningArea(BCA_LISTEN_ALWAYS
, false, this);
5077 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
5078 formula::FormulaToken
* t
;
5079 while ( ( t
= aIter
.GetNextReferenceRPN() ) != nullptr )
5081 switch (t
->GetType())
5085 ScAddress aCell
= t
->GetSingleRef()->toAbs(aCellPos
);
5086 if (aCell
.IsValid())
5087 pDoc
->EndListeningCell(aCell
, this);
5091 endListeningArea(this, *pDoc
, aCellPos
, *t
);
5099 void ScFormulaCell::EndListeningTo( sc::EndListeningContext
& rCxt
)
5102 mxGroup
->endAllGroupListening(rCxt
.getDoc());
5104 if (rCxt
.getDoc().IsClipOrUndo() || IsInChangeTrack())
5107 if (!HasBroadcaster())
5110 ScDocument
& rDoc
= rCxt
.getDoc();
5111 rDoc
.SetDetectiveDirty(true); // It has changed something
5113 ScTokenArray
* pArr
= rCxt
.getOldCode();
5114 ScAddress aCellPos
= rCxt
.getOldPosition(aPos
);
5118 if (pArr
->IsRecalcModeAlways())
5120 rDoc
.EndListeningArea(BCA_LISTEN_ALWAYS
, false, this);
5124 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
5125 formula::FormulaToken
* t
;
5126 while ( ( t
= aIter
.GetNextReferenceRPN() ) != nullptr )
5128 switch (t
->GetType())
5132 ScAddress aCell
= t
->GetSingleRef()->toAbs(aCellPos
);
5133 if (aCell
.IsValid())
5134 rDoc
.EndListeningCell(rCxt
, aCell
, *this);
5138 endListeningArea(this, rDoc
, aCellPos
, *t
);
5146 bool ScFormulaCell::IsShared() const
5148 return mxGroup
.get() != nullptr;
5151 bool ScFormulaCell::IsSharedTop() const
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
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
;
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()
5200 // Not a shared formula cell.
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
;
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
)
5227 OUString aStr
= ScGlobal::GetErrorString(nErrCode
);
5228 cout
<< " * code error: " << aStr
<< " (" << int(nErrCode
) << ")";
5232 cout
<< " * result: ";
5233 sc::FormulaResultValue aRV
= aResult
.GetResult();
5236 case sc::FormulaResultValue::Value
:
5237 cout
<< aRV
.mfValue
<< " (value)";
5239 case sc::FormulaResultValue::String
:
5240 cout
<< aRV
.maString
.getString() << " (string)";
5242 case sc::FormulaResultValue::Error
:
5243 cout
<< ScGlobal::GetErrorString(aRV
.mnError
) << " (error: " << int(aRV
.mnError
) << ")";
5245 case sc::FormulaResultValue::Invalid
:
5246 cout
<< "(invalid)";
5256 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */