LibreOffice Module sc (master) 1
formulacell.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <config_feature_opencl.h>
21
22#include <sal/config.h>
23#include <sal/log.hxx>
24#include <osl/diagnose.h>
25
26#include <cassert>
27#include <cstdlib>
28
29#include <formulacell.hxx>
31
32#include <compiler.hxx>
33#include <document.hxx>
34#include <cellvalue.hxx>
35#include <interpre.hxx>
36#include <macromgr.hxx>
37#include <refupdat.hxx>
38#include <recursionhelper.hxx>
39#include <docoptio.hxx>
40#include <rangenam.hxx>
41#include <rangelst.hxx>
42#include <dbdata.hxx>
43#include <progress.hxx>
44#include <scmatrix.hxx>
45#include <rechead.hxx>
46#include <scitems.hxx>
47#include <validat.hxx>
48#include <editutil.hxx>
49#include <chgtrack.hxx>
50#include <tokenarray.hxx>
51
53#include <editeng/editobj.hxx>
55#include <svl/intitem.hxx>
56#include <svl/numformat.hxx>
57#include <formulagroup.hxx>
58#include <listenercontext.hxx>
59#include <types.hxx>
60#include <scopetools.hxx>
61#include <refupdatecontext.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>
69
70#if HAVE_FEATURE_OPENCL
72#endif
73
74#include <memory>
75#include <map>
76
77using namespace formula;
78
79#define DEBUG_CALCULATION 0
80#if DEBUG_CALCULATION
81static bool bDebugCalculationActive = false; // Set to true for global active init,
82static ScAddress aDebugCalculationTriggerAddress(1,2,0); // or on cell Sheet1.B3, whatever you like
83
84struct DebugCalculationEntry
85{
86 ScAddress maPos;
87 OUString maResult;
88 const ScDocument& mrDoc;
89 sal_uInt32 mnGroup;
90 sal_uInt16 mnRecursion;
91
92 DebugCalculationEntry( const ScAddress& rPos, ScDocument& rDoc, sal_uInt32 nGroup ) :
93 maPos(rPos),
94 mrDoc(rDoc),
95 mnGroup(nGroup),
96 mnRecursion(rDoc.GetRecursionHelper().GetRecursionCount())
97 {
98 }
99};
100
107static struct DebugCalculation
108{
109 std::vector< DebugCalculationEntry > mvPos;
110 std::vector< DebugCalculationEntry > mvResults;
111 ScAddress maTrigger;
112 sal_uInt32 mnGroup;
113 bool mbActive;
114 bool mbSwitchOff;
115 bool mbPrint;
116 bool mbPrintResults;
117
118 DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false),
119 mbPrint(true), mbPrintResults(false) {}
120
122 void print() const
123 {
124 for (auto const& it : mvPos)
125 {
126 OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc) +
127 " [" + OUString::number( it.mnRecursion) + "," + OUString::number( it.mnGroup) + "]");
128 fprintf( stderr, "%s -> ", aStr.toUtf8().getStr());
129 }
130 fprintf( stderr, "%s", "END\n");
131 }
132
134 void printResults() const
135 {
136 for (auto const& it : mvResults)
137 {
138 OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc));
139 aStr += " (" + it.maResult + ")";
140 fprintf( stderr, "%s, ", aStr.toUtf8().getStr());
141 }
142 fprintf( stderr, "%s", "END\n");
143 }
144
145 void storeResult( const svl::SharedString& rStr )
146 {
147 if (mbActive && !mvPos.empty())
148 mvPos.back().maResult = "\"" + rStr.getString() + "\"";
149 }
150
151 void storeResult( const double& fVal )
152 {
153 if (mbActive && !mvPos.empty())
154 mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.', true);
155 }
156
157 void storeResultError( FormulaError nErr )
158 {
159 if (mbActive && !mvPos.empty())
160 mvPos.back().maResult = "Err:" + OUString::number( int( nErr ));
161 }
162
163 void enterGroup()
164 {
165 ++mnGroup;
166 }
167
168 void leaveGroup()
169 {
170 --mnGroup;
171 }
172
173} aDC;
174
175struct DebugCalculationStacker
176{
177 DebugCalculationStacker( const ScAddress& rPos, ScDocument& rDoc )
178 {
179 if (!aDC.mbActive && rPos == aDC.maTrigger)
180 aDC.mbActive = aDC.mbSwitchOff = true;
181 if (aDC.mbActive)
182 {
183 aDC.mvPos.push_back( DebugCalculationEntry( rPos, rDoc, aDC.mnGroup));
184 aDC.mbPrint = true;
185 }
186 }
187
188 ~DebugCalculationStacker()
189 {
190 if (aDC.mbActive)
191 {
192 if (!aDC.mvPos.empty())
193 {
194 if (aDC.mbPrint)
195 {
196 aDC.print();
197 aDC.mbPrint = false;
198 }
199 if (aDC.mbPrintResults)
200 {
201 // Store results until final result is available, reversing order.
202 aDC.mvResults.push_back( aDC.mvPos.back());
203 }
204 aDC.mvPos.pop_back();
205 if (aDC.mbPrintResults && aDC.mvPos.empty())
206 {
207 aDC.printResults();
208 std::vector< DebugCalculationEntry >().swap( aDC.mvResults);
209 }
210 if (aDC.mbSwitchOff && aDC.mvPos.empty())
211 aDC.mbActive = false;
212 }
213 }
214 }
215};
216#endif
217
218namespace {
219
220// More or less arbitrary, of course all recursions must fit into available
221// stack space (which is what on all systems we don't know yet?). Choosing a
222// lower value may be better than trying a much higher value that also isn't
223// sufficient but temporarily leads to high memory consumption. On the other
224// hand, if the value fits all recursions, execution is quicker as no resumes
225// are necessary. Could be made a configurable option.
226// Allow for a year's calendar (366).
227const sal_uInt16 MAXRECURSION = 400;
228
229typedef SCCOLROW(*DimensionSelector)(const ScDocument&, const ScAddress&, const ScSingleRefData&);
230
231SCCOLROW lcl_GetCol(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
232{
233 return rData.toAbs(rDoc, rPos).Col();
234}
235
236SCCOLROW lcl_GetRow(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
237{
238 return rData.toAbs(rDoc, rPos).Row();
239}
240
241SCCOLROW lcl_GetTab(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
242{
243 return rData.toAbs(rDoc, rPos).Tab();
244}
245
248bool
249lcl_checkRangeDimension(
250 const ScDocument& rDoc,
251 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
252 const DimensionSelector aWhich)
253{
254 return aWhich(rDoc, rPos, rRef1.Ref1) == aWhich(rDoc, rPos, rRef2.Ref1) &&
255 aWhich(rDoc, rPos, rRef1.Ref2) == aWhich(rDoc, rPos, rRef2.Ref2);
256}
257
258bool
259lcl_checkRangeDimensions(
260 const ScDocument& rDoc,
261 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
262 bool& bCol, bool& bRow, bool& bTab)
263{
264 const bool bSameCols(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetCol));
265 const bool bSameRows(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetRow));
266 const bool bSameTabs(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetTab));
267
268 // Test if exactly two dimensions are equal
269 if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
270 {
271 bCol = !bSameCols;
272 bRow = !bSameRows;
273 bTab = !bSameTabs;
274 return true;
275 }
276 return false;
277}
278
282bool
283lcl_checkRangeDimensions(
284 const ScDocument& rDoc, const ScAddress& rPos,
285 const std::vector<formula::FormulaToken*>::const_iterator& rBegin,
286 const std::vector<formula::FormulaToken*>::const_iterator& rEnd,
287 bool& bCol, bool& bRow, bool& bTab)
288{
289 std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin);
290 ++aCur;
291 const SingleDoubleRefProvider aRef(**rBegin);
292 bool bOk(false);
293 {
294 const SingleDoubleRefProvider aRefCur(**aCur);
295 bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bCol, bRow, bTab);
296 }
297 while (bOk && aCur != rEnd)
298 {
299 const SingleDoubleRefProvider aRefCur(**aCur);
300 bool bColTmp(false);
301 bool bRowTmp(false);
302 bool bTabTmp(false);
303 bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
304 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
305 ++aCur;
306 }
307
308 return bOk && aCur == rEnd;
309}
310
311class LessByReference
312{
313 const ScDocument& mrDoc;
314 ScAddress maPos;
315 DimensionSelector maFunc;
316public:
317 LessByReference(const ScDocument& rDoc, const ScAddress& rPos, const DimensionSelector& rFunc) :
318 mrDoc(rDoc), maPos(rPos), maFunc(rFunc) {}
319
320 bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
321 {
322 const SingleDoubleRefProvider aRef1(*pRef1);
323 const SingleDoubleRefProvider aRef2(*pRef2);
324 return maFunc(mrDoc, maPos, aRef1.Ref1) < maFunc(mrDoc, maPos, aRef2.Ref1);
325 }
326};
327
333class AdjacentByReference
334{
335 const ScDocument& mrDoc;
336 ScAddress maPos;
337 DimensionSelector maFunc;
338public:
339 AdjacentByReference(const ScDocument& rDoc, const ScAddress& rPos, DimensionSelector aFunc) :
340 mrDoc(rDoc), maPos(rPos), maFunc(aFunc) {}
341
342 bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
343 {
344 const SingleDoubleRefProvider aRef1(*p1);
345 const SingleDoubleRefProvider aRef2(*p2);
346 return maFunc(mrDoc, maPos, aRef2.Ref1) - maFunc(mrDoc, maPos, aRef1.Ref2) == 1;
347 }
348};
349
350bool
351lcl_checkIfAdjacent(
352 const ScDocument& rDoc,
353 const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
354{
355 auto aBegin(rReferences.cbegin());
356 auto aEnd(rReferences.cend());
357 auto aBegin1(aBegin);
358 ++aBegin1;
359 --aEnd;
360 return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rDoc, rPos, aWhich));
361}
362
363void
364lcl_fillRangeFromRefList(
365 const ScDocument& rDoc,
366 const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange)
367{
368 const ScSingleRefData aStart(
369 SingleDoubleRefProvider(*rReferences.front()).Ref1);
370 rRange.aStart = aStart.toAbs(rDoc, aPos);
371 const ScSingleRefData aEnd(
372 SingleDoubleRefProvider(*rReferences.back()).Ref2);
373 rRange.aEnd = aEnd.toAbs(rDoc, aPos);
374}
375
376bool
377lcl_refListFormsOneRange(
378 const ScDocument& rDoc,
379 const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
380 ScRange& rRange)
381{
382 if (rReferences.size() == 1)
383 {
384 lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
385 return true;
386 }
387
388 bool bCell(false);
389 bool bRow(false);
390 bool bTab(false);
391 if (lcl_checkRangeDimensions(rDoc, rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
392 {
393 DimensionSelector aWhich;
394 if (bCell)
395 {
396 aWhich = lcl_GetCol;
397 }
398 else if (bRow)
399 {
400 aWhich = lcl_GetRow;
401 }
402 else if (bTab)
403 {
404 aWhich = lcl_GetTab;
405 }
406 else
407 {
408 OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
409 aWhich = lcl_GetRow; // initialize to avoid warning
410 }
411
412 // Sort the references by start of range
413 std::sort(rReferences.begin(), rReferences.end(), LessByReference(rDoc, rPos, aWhich));
414 if (lcl_checkIfAdjacent(rDoc, rPos, rReferences, aWhich))
415 {
416 lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
417 return true;
418 }
419 }
420 return false;
421}
422
423bool lcl_isReference(const FormulaToken& rToken)
424{
425 return
426 rToken.GetType() == svSingleRef ||
427 rToken.GetType() == svDoubleRef;
428}
429
430void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc,
431 const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
432{
433 ScRangeData* pRangeData = nullptr;
434 SCTAB nSheet = pToken->GetSheet();
435 sal_uInt16 nIndex = pToken->GetIndex();
436 if (!rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true))
437 return; // nothing to do
438
439 if (!pRangeData)
440 {
441 // If this happened we have a real problem.
442 pToken->SetIndex(0);
443 assert(!"inserting the range name should not fail");
444 return;
445 }
446
447 pToken->SetIndex(nIndex);
448 pToken->SetSheet(nSheet);
449}
450
451void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc)
452{
453 ScDBCollection* pOldDBCollection = rOldDoc.GetDBCollection();
454 if (!pOldDBCollection)
455 return;//strange error case, don't do anything
456 ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
457 ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
458 if (!pDBData)
459 return; //invalid index
460 OUString aDBName = pDBData->GetUpperName();
461
462 //search in new document
463 ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
464 if (!pNewDBCollection)
465 {
466 rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(rNewDoc)));
467 pNewDBCollection = rNewDoc.GetDBCollection();
468 }
469 ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
470 ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
471 if (!pNewDBData)
472 {
473 pNewDBData = new ScDBData(*pDBData);
474 bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData));
475 assert(ins); (void)ins;
476 }
477 pToken->SetIndex(pNewDBData->GetIndex());
478}
479
480}
481
483{
484 if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
485 return maRange.aStart.Tab() < r.maRange.aStart.Tab();
486 if (maRange.aStart.Col() != r.maRange.aStart.Col())
487 return maRange.aStart.Col() < r.maRange.aStart.Col();
488 if (maRange.aStart.Row() != r.maRange.aStart.Row())
489 return maRange.aStart.Row() < r.maRange.aStart.Row();
490 if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
491 return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
492 if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
493 return maRange.aEnd.Col() < r.maRange.aEnd.Col();
494 if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
495 return maRange.aEnd.Row() < r.maRange.aEnd.Row();
496 if (mbStartFixed != r.mbStartFixed)
497 return r.mbStartFixed;
498 if (mbEndFixed != r.mbEndFixed)
499 return r.mbEndFixed;
500
501 return false;
502}
503
505 mnRefCount(0),
506 mpTopCell(nullptr),
507 mnLength(0),
508 mnWeight(0),
509 mnFormatType(SvNumFormatType::NUMBER),
510 mbInvariant(false),
511 mbSubTotal(false),
512 mbPartOfCycle(false),
513 meCalcState(sc::GroupCalcEnabled)
514{
515}
516
518{
519}
520
522{
523 mpCode = rCode.CloneValue();
524 mbInvariant = mpCode->IsInvariant();
525 mpCode->GenHash();
526}
527
529 ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
530{
531 if (!mpCode)
532 return;
533
534 if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
535 {
536 bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
537 ScCompiler aComp(rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
540 }
541 else
542 {
543 mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
544 }
545}
546
548 ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
549{
550 AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);
551
552 AreaListenersType::iterator it = m_AreaListeners.lower_bound(aKey);
553 if (it == m_AreaListeners.end() || m_AreaListeners.key_comp()(aKey, it->first))
554 {
555 // Insert a new one.
556 it = m_AreaListeners.emplace_hint(
557 it, std::piecewise_construct,
558 std::forward_as_tuple(aKey),
559 std::forward_as_tuple(
560 rRange, (*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed));
561 }
562
563 return &it->second;
564}
565
567{
568 for (auto& rEntry : m_AreaListeners)
569 {
570 sc::FormulaGroupAreaListener& rListener = rEntry.second;
571 ScRange aListenRange = rListener.getListeningRange();
572 // This "always listen" special range is never grouped.
573 bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
574 rDoc.EndListeningArea(aListenRange, bGroupListening, &rListener);
575 }
576
577 m_AreaListeners.clear();
578}
579
581 bDirty(false),
582 bTableOpDirty(false),
583 bChanged(false),
584 bRunning(false),
585 bCompile(false),
586 bSubTotal(false),
587 bIsIterCell(false),
588 bInChangeTrack(false),
589 bNeedListening(false),
590 mbNeedsNumberFormat(false),
591 mbAllowNumberFormatChange(false),
592 mbPostponedDirty(false),
593 mbIsExtRef(false),
594 mbSeenInPath(false),
595 mbFreeFlying(false),
596 cMatrixFlag(ScMatrixMode::NONE),
597 nSeenInIteration(0),
598 nFormatType(SvNumFormatType::NUMBER),
599 eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
600 pCode(new ScTokenArray(rDoc)),
601 rDocument(rDoc),
602 pPrevious(nullptr),
603 pNext(nullptr),
604 pPreviousTrack(nullptr),
605 pNextTrack(nullptr),
606 aPos(rPos)
607{
608}
609
611 const OUString& rFormula,
612 const FormulaGrammar::Grammar eGrammar,
613 ScMatrixMode cMatInd ) :
614 bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
615 bTableOpDirty( false ),
616 bChanged( false ),
617 bRunning( false ),
618 bCompile( false ),
619 bSubTotal( false ),
620 bIsIterCell( false ),
621 bInChangeTrack( false ),
622 bNeedListening( false ),
623 mbNeedsNumberFormat( false ),
624 mbAllowNumberFormatChange(false),
625 mbPostponedDirty(false),
626 mbIsExtRef(false),
627 mbSeenInPath(false),
628 mbFreeFlying(false),
629 cMatrixFlag ( cMatInd ),
630 nSeenInIteration(0),
631 nFormatType ( SvNumFormatType::NUMBER ),
632 eTempGrammar( eGrammar),
633 pCode( nullptr ),
634 rDocument( rDoc ),
635 pPrevious(nullptr),
636 pNext(nullptr),
637 pPreviousTrack(nullptr),
638 pNextTrack(nullptr),
639 aPos(rPos)
640{
641 Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
642 if (!pCode)
643 // We need to have a non-NULL token array instance at all times.
644 pCode = new ScTokenArray(rDoc);
645}
646
648 ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray,
649 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
650 bDirty( true ),
651 bTableOpDirty( false ),
652 bChanged( false ),
653 bRunning( false ),
654 bCompile( false ),
655 bSubTotal( false ),
656 bIsIterCell( false ),
657 bInChangeTrack( false ),
658 bNeedListening( false ),
659 mbNeedsNumberFormat( false ),
660 mbAllowNumberFormatChange(false),
661 mbPostponedDirty(false),
662 mbIsExtRef(false),
663 mbSeenInPath(false),
664 mbFreeFlying(false),
665 cMatrixFlag ( cMatInd ),
666 nSeenInIteration(0),
667 nFormatType ( SvNumFormatType::NUMBER ),
668 eTempGrammar( eGrammar),
669 pCode(pArray.release()),
670 rDocument( rDoc ),
671 pPrevious(nullptr),
672 pNext(nullptr),
673 pPreviousTrack(nullptr),
674 pNextTrack(nullptr),
675 aPos(rPos)
676{
677 assert(pCode); // Never pass a NULL pointer here.
678
679 pCode->Finalize(); // Reduce memory usage if needed.
680
681 // Generate RPN token array.
682 if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
683 {
687 }
688 else
689 {
691 bSubTotal = true;
692 }
693
694 if (bSubTotal)
696
697 pCode->GenHash();
698}
699
701 ScDocument& rDoc, const ScAddress& rPos, const ScTokenArray& rArray,
702 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
703 bDirty( true ),
704 bTableOpDirty( false ),
705 bChanged( false ),
706 bRunning( false ),
707 bCompile( false ),
708 bSubTotal( false ),
709 bIsIterCell( false ),
710 bInChangeTrack( false ),
711 bNeedListening( false ),
712 mbNeedsNumberFormat( false ),
713 mbAllowNumberFormatChange(false),
714 mbPostponedDirty(false),
715 mbIsExtRef(false),
716 mbSeenInPath(false),
717 mbFreeFlying(false),
718 cMatrixFlag ( cMatInd ),
719 nSeenInIteration(0),
720 nFormatType ( SvNumFormatType::NUMBER ),
721 eTempGrammar( eGrammar),
722 pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array
723 rDocument( rDoc ),
724 pPrevious(nullptr),
725 pNext(nullptr),
726 pPreviousTrack(nullptr),
727 pNextTrack(nullptr),
728 aPos(rPos)
729{
730 // RPN array generation
731 if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
732 {
736 }
737 else
738 {
740 bSubTotal = true;
741 }
742
743 if (bSubTotal)
745
746 pCode->GenHash();
747}
748
750 ScDocument& rDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
751 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) :
752 mxGroup(xGroup),
753 bDirty(true),
754 bTableOpDirty( false ),
755 bChanged( false ),
756 bRunning( false ),
757 bCompile( false ),
758 bSubTotal(xGroup->mbSubTotal),
759 bIsIterCell( false ),
760 bInChangeTrack( false ),
761 bNeedListening( false ),
762 mbNeedsNumberFormat( false ),
763 mbAllowNumberFormatChange(false),
764 mbPostponedDirty(false),
765 mbIsExtRef(false),
766 mbSeenInPath(false),
767 mbFreeFlying(false),
768 cMatrixFlag ( cInd ),
769 nSeenInIteration(0),
770 nFormatType(xGroup->mnFormatType),
771 eTempGrammar( eGrammar),
772 pCode(xGroup->mpCode ? &*xGroup->mpCode : new ScTokenArray(rDoc)),
773 rDocument( rDoc ),
774 pPrevious(nullptr),
775 pNext(nullptr),
776 pPreviousTrack(nullptr),
777 pNextTrack(nullptr),
778 aPos(rPos)
779{
780 if (bSubTotal)
782}
783
784ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) :
785 bDirty( rCell.bDirty ),
786 bTableOpDirty( false ),
787 bChanged( rCell.bChanged ),
788 bRunning( false ),
789 bCompile( rCell.bCompile ),
790 bSubTotal( rCell.bSubTotal ),
791 bIsIterCell( false ),
792 bInChangeTrack( false ),
793 bNeedListening( false ),
794 mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ),
795 mbAllowNumberFormatChange(false),
796 mbPostponedDirty(false),
797 mbIsExtRef(false),
798 mbSeenInPath(false),
799 mbFreeFlying(false),
800 cMatrixFlag ( rCell.cMatrixFlag ),
801 nSeenInIteration(0),
802 nFormatType( rCell.nFormatType ),
803 aResult( rCell.aResult ),
804 eTempGrammar( rCell.eTempGrammar),
805 rDocument( rDoc ),
806 pPrevious(nullptr),
807 pNext(nullptr),
808 pPreviousTrack(nullptr),
809 pNextTrack(nullptr),
810 aPos(rPos)
811{
812 pCode = rCell.pCode->Clone().release();
813
814 // set back any errors and recompile
815 // not in the Clipboard - it must keep the received error flag
816 // Special Length=0: as bad cells are generated, then they are also retained
817 if ( pCode->GetCodeError() != FormulaError::NONE && !rDocument.IsClipboard() && pCode->GetLen() )
818 {
819 pCode->SetCodeError( FormulaError::NONE );
820 bCompile = true;
821 }
822 // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
823 bool bCompileLater = false;
824 bool bClipMode = rCell.rDocument.IsClipboard();
825
826 //update ScNameTokens
827 if (!rDocument.IsClipOrUndo() || rDoc.IsUndo())
828 {
829 if (!rDocument.IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
830 {
831 bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default);
832 formula::FormulaToken* pToken = nullptr;
834 while((pToken = aIter.GetNextName())!= nullptr)
835 {
836 OpCode eOpCode = pToken->GetOpCode();
837 if (eOpCode == ocName)
838 adjustRangeName(pToken, rDoc, rCell.rDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
839 else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
840 adjustDBRange(pToken, rDoc, rCell.rDocument);
841 }
842 }
843
844 bool bCopyBetweenDocs = rDocument.GetPool() != rCell.rDocument.GetPool();
845 if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
846 {
848 }
849
850 pCode->AdjustAbsoluteRefs( rCell.rDocument, rCell.aPos, aPos, bCopyBetweenDocs );
851 }
852
853 if (!rDocument.IsClipOrUndo())
854 {
858 }
859
860 if( !bCompile )
861 { // Name references with references and ColRowNames
863 for (;;)
864 {
866 if (!t || bCompile)
867 break;
868 if ( t->IsExternalRef() )
869 {
870 // External name, cell, and area references.
871 bCompile = true;
872 }
873 else if ( t->GetType() == svIndex )
874 {
875 const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
876 if( pRangeData )
877 {
878 if( pRangeData->HasReferences() )
879 bCompile = true;
880 }
881 else
882 bCompile = true; // invalid reference!
883 }
884 else if ( t->GetOpCode() == ocColRowName )
885 {
886 bCompile = true; // new lookup needed
887 bCompileLater = bClipMode;
888 }
889 }
890 }
891 if( bCompile )
892 {
893 if ( !bCompileLater && bClipMode )
894 {
895 // Merging ranges needs the actual positions after UpdateReference.
896 // ColRowNames and TableRefs need new lookup after positions are
897 // adjusted.
898 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
900 }
901 if ( !bCompileLater )
902 {
903 // bNoListening, not at all if in Clipboard/Undo,
904 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
905 CompileTokenArray( true );
906 }
907 }
908
909 if( nCloneFlags & ScCloneFlags::StartListening )
910 StartListeningTo( rDoc );
911
912 if (bSubTotal)
914}
915
917{
921 if (pCode->HasOpCode(ocMacro))
923
926
927 if (!mxGroup || !mxGroup->mpCode)
928 // Formula token is not shared.
929 delete pCode;
930
931 if (mxGroup && mxGroup->mpTopCell == this)
932 mxGroup->mpTopCell = nullptr;
933}
934
936{
937 return new ScFormulaCell(*this, rDocument, aPos);
938}
939
941{
942 return new ScFormulaCell(*this, rDocument, rPos, ScCloneFlags::Default);
943}
944
946{
947 return pCode->GetHash();
948}
949
950OUString ScFormulaCell::GetFormula( const FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext ) const
951{
952 if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
953 {
955 }
956 OUStringBuffer buffer;
958 {
959 // Reference to another cell that contains a matrix formula.
962 if( p )
963 {
964 /* FIXME: original GetFormula() code obtained
965 * pCell only if (!IsInChangeTrack()),
966 * GetEnglishFormula() omitted that test.
967 * Can we live without in all cases? */
968 ScFormulaCell* pCell = nullptr;
969 ScSingleRefData& rRef = *p->GetSingleRef();
970 ScAddress aAbs = rRef.toAbs(rDocument, aPos);
971 if (rDocument.ValidAddress(aAbs))
972 pCell = rDocument.GetFormulaCell(aAbs);
973
974 if (pCell)
975 {
976 return pCell->GetFormula( eGrammar, pContext );
977 }
978 else
979 {
980 ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
981 aComp.CreateStringFromTokenArray( buffer );
982 }
983 }
984 else
985 {
986 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
987 }
988 }
989 else
990 {
991 ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
992 aComp.CreateStringFromTokenArray( buffer );
993 }
994
995 buffer.insert( 0, '=');
997 {
998 buffer.insert( 0, '{');
999 buffer.append( '}');
1000 }
1001 return buffer.makeStringAndClear();
1002}
1003
1005{
1006 OUStringBuffer aBuf;
1007 if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
1008 {
1009 ScTokenArray aCode(rCxt.getDoc());
1011 ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
1013 return aBuf.makeStringAndClear();
1014 }
1016 {
1017 // Reference to another cell that contains a matrix formula.
1020 if( p )
1021 {
1022 /* FIXME: original GetFormula() code obtained
1023 * pCell only if (!IsInChangeTrack()),
1024 * GetEnglishFormula() omitted that test.
1025 * Can we live without in all cases? */
1026 ScFormulaCell* pCell = nullptr;
1027 ScSingleRefData& rRef = *p->GetSingleRef();
1028 ScAddress aAbs = rRef.toAbs(rDocument, aPos);
1029 if (rDocument.ValidAddress(aAbs))
1030 pCell = rDocument.GetFormulaCell(aAbs);
1031
1032 if (pCell)
1033 {
1034 return pCell->GetFormula(rCxt);
1035 }
1036 else
1037 {
1038 ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1040 }
1041 }
1042 else
1043 {
1044 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1045 }
1046 }
1047 else
1048 {
1049 ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1051 }
1052
1053 aBuf.insert( 0, '=');
1055 {
1056 aBuf.insert( 0, '{');
1057 aBuf.append( '}');
1058 }
1059
1060 return aBuf.makeStringAndClear();
1061}
1062
1064{
1066
1067 if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell)
1068 {
1069 const ScMatrix* pMat = aResult.GetToken()->GetMatrix();
1070 if (pMat)
1071 {
1072 pMat->GetDimensions( rCols, rRows );
1073 if (pCode->IsHyperLink())
1074 {
1075 // Row 2 element is the URL that is not to be displayed and the
1076 // result dimension not to be extended.
1077 assert(rRows == 2);
1078 rRows = 1;
1079 }
1080 return;
1081 }
1082 }
1083 rCols = 0;
1084 rRows = 0;
1085}
1086
1089
1091{
1092 mbPostponedDirty = bVar;
1093}
1094
1096{
1098}
1099
1100void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
1101 const FormulaGrammar::Grammar eGrammar )
1102{
1103 if ( rDocument.IsClipOrUndo() )
1104 return;
1105 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1106 if ( bWasInFormulaTree )
1108 // pCode may not deleted for queries, but must be empty
1109 if ( pCode )
1110 pCode->Clear();
1111 ScTokenArray* pCodeOld = pCode;
1112 ScCompiler aComp( rDocument, aPos, eGrammar);
1113 pCode = aComp.CompileString( rFormula ).release();
1114 assert(!mxGroup);
1115 delete pCodeOld;
1116 if( pCode->GetCodeError() == FormulaError::NONE )
1117 {
1118 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1119 { // not recursive CompileTokenArray/Compile/CompileTokenArray
1120 if ( rFormula[0] == '=' )
1121 pCode->AddBad( rFormula.copy(1) );
1122 else
1123 pCode->AddBad( rFormula );
1124 }
1125 bCompile = true;
1126 CompileTokenArray( bNoListening );
1127 }
1128 else
1129 bChanged = true;
1130
1131 if ( bWasInFormulaTree )
1133}
1134
1136 sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1137{
1138 if ( rDocument.IsClipOrUndo() )
1139 return;
1140 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1141 if ( bWasInFormulaTree )
1143 // pCode may not deleted for queries, but must be empty
1144 if ( pCode )
1145 pCode->Clear();
1146 ScTokenArray* pCodeOld = pCode;
1147 ScCompiler aComp(rCxt, aPos);
1148 pCode = aComp.CompileString( rFormula ).release();
1149 assert(!mxGroup);
1150 delete pCodeOld;
1151 if( pCode->GetCodeError() == FormulaError::NONE )
1152 {
1153 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1154 { // not recursive CompileTokenArray/Compile/CompileTokenArray
1155 if ( rFormula[0] == '=' )
1156 pCode->AddBad( rFormula.copy(1) );
1157 else
1158 pCode->AddBad( rFormula );
1159 }
1160 bCompile = true;
1161 CompileTokenArray(rCxt, bNoListening);
1162 }
1163 else
1164 bChanged = true;
1165
1166 if ( bWasInFormulaTree )
1168}
1169
1170void ScFormulaCell::CompileTokenArray( bool bNoListening )
1171{
1172 // Not already compiled?
1173 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1174 {
1175 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
1176 }
1177 else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
1178 {
1179 // RPN length may get changed
1180 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1181 if ( bWasInFormulaTree )
1183
1184 // Loading from within filter? No listening yet!
1186 bNoListening = true;
1187
1188 if( !bNoListening && pCode->GetCodeLen() )
1191 bSubTotal = aComp.CompileTokenArray();
1192 if( pCode->GetCodeError() == FormulaError::NONE )
1193 {
1194 nFormatType = aComp.GetNumFormatType();
1195 bChanged = true;
1196 aResult.SetToken( nullptr);
1197 bCompile = false;
1198 if ( !bNoListening )
1200 }
1201 if ( bWasInFormulaTree )
1203
1204 if (bSubTotal)
1206 }
1207}
1208
1210{
1211 // Not already compiled?
1212 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1213 {
1215 Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
1216 }
1217 else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
1218 {
1219 // RPN length may get changed
1220 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1221 if ( bWasInFormulaTree )
1223
1224 // Loading from within filter? No listening yet!
1226 bNoListening = true;
1227
1228 if( !bNoListening && pCode->GetCodeLen() )
1230 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1231 bSubTotal = aComp.CompileTokenArray();
1232 if( pCode->GetCodeError() == FormulaError::NONE )
1233 {
1234 nFormatType = aComp.GetNumFormatType();
1235 bChanged = true;
1236 aResult.SetToken( nullptr);
1237 bCompile = false;
1238 if ( !bNoListening )
1240 }
1241 if ( bWasInFormulaTree )
1243
1244 if (bSubTotal)
1246 }
1247}
1248
1250{
1252 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1253 // just establish listeners
1255 return ;
1256 }
1257
1258 // Error constant formula cell stays as is.
1259 if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
1260 return;
1261
1262 // Compilation changes RPN count, remove and reinsert to FormulaTree if it
1263 // was in to update its count.
1264 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this);
1265 if (bWasInFormulaTree)
1268 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1269 OUString aFormula, aFormulaNmsp;
1270 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1271 rDocument.DecXMLImportedFormulaCount( aFormula.getLength() );
1273 // pCode may not deleted for queries, but must be empty
1274 pCode->Clear();
1275
1276 bool bDoCompile = true;
1277
1278 if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
1279 {
1280 ScAddress aPreviousCell( aPos );
1281 aPreviousCell.IncRow( -1 );
1282 ScFormulaCell *pPreviousCell = rDocument.GetFormulaCell( aPreviousCell );
1283 if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
1284 {
1285 // Build formula string using the tokens from the previous cell,
1286 // but use the current cell position.
1287 ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
1288 OUStringBuffer aShouldBeBuf;
1289 aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
1290
1291 // The initial '=' is optional in ODFF.
1292 const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
1293 if (aFormula.getLength() == aShouldBeBuf.getLength() + nLeadingEqual &&
1294 aFormula.match( aShouldBeBuf, nLeadingEqual))
1295 {
1296 // Put them in the same formula group.
1297 ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
1298 if (!xGroup) // Last cell is not grouped yet. Start a new group.
1299 xGroup = pPreviousCell->CreateCellGroup(1, false);
1300 ++xGroup->mnLength;
1301 SetCellGroup( xGroup );
1302
1303 // Do setup here based on previous cell.
1304
1305 nFormatType = pPreviousCell->nFormatType;
1306 bSubTotal = pPreviousCell->bSubTotal;
1307 bChanged = true;
1308 bCompile = false;
1309
1310 if (bSubTotal)
1312
1313 bDoCompile = false;
1314 pCode = pPreviousCell->pCode;
1315 if (pPreviousCell->mbIsExtRef)
1317 }
1318 }
1319 }
1320
1321 if (bDoCompile)
1322 {
1323 ScTokenArray* pCodeOld = pCode;
1324 pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release();
1325 assert(!mxGroup);
1326 delete pCodeOld;
1327
1328 if( pCode->GetCodeError() == FormulaError::NONE )
1329 {
1330 if ( !pCode->GetLen() )
1331 {
1332 if ( !aFormula.isEmpty() && aFormula[0] == '=' )
1333 pCode->AddBad( aFormula.copy( 1 ) );
1334 else
1335 pCode->AddBad( aFormula );
1336 }
1337 bSubTotal = aComp.CompileTokenArray();
1338 if( pCode->GetCodeError() == FormulaError::NONE )
1339 {
1340 nFormatType = aComp.GetNumFormatType();
1341 bChanged = true;
1342 bCompile = false;
1343 }
1344
1345 if (bSubTotal)
1347 }
1348 else
1349 bChanged = true;
1350 }
1351
1352 // After loading, it must be known if ocDde/ocWebservice is in any formula
1353 // (for external links warning, CompileXML is called at the end of loading XML file)
1355
1356 //volatile cells must be added here for import
1358 {
1359 // During load, only those cells that are marked explicitly dirty get
1360 // recalculated. So we need to set it dirty here.
1361 SetDirtyVar();
1363 // Do not call TrackFormulas() here, not all listeners may have been
1364 // established, postponed until ScDocument::CompileXML() finishes.
1365 }
1366 else if (bWasInFormulaTree)
1368}
1369
1371{
1372 bool bNewCompiled = false;
1373 // If a Calc 1.0-doc is read, we have a result, but no token array
1374 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1375 {
1377 Compile(rCxt, aResult.GetHybridFormula(), true);
1378 aResult.SetToken( nullptr);
1379 bDirty = true;
1380 bNewCompiled = true;
1381 }
1382 // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1383 if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1384 {
1385 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1386 bSubTotal = aComp.CompileTokenArray();
1387 nFormatType = aComp.GetNumFormatType();
1388 bDirty = true;
1389 bCompile = false;
1390 bNewCompiled = true;
1391
1392 if (bSubTotal)
1394 }
1395
1396 // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1397 // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1398 // We iron this out here for all systems, such that we also have an Err503 here.
1399 if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
1400 {
1401 OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1402 aResult.SetResultError( FormulaError::IllegalFPOperation );
1403 bDirty = true;
1404 }
1405
1406 // DoubleRefs for binary operators were always a Matrix before version v5.0.
1407 // Now this is only the case when in an array formula, otherwise it's an implicit intersection
1410 {
1412 SetMatColsRows( 1, 1);
1413 }
1414
1415 // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1416 // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
1417 if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
1418 {
1419 if (bStartListening)
1421
1422 if( !pCode->IsRecalcModeNormal() )
1423 bDirty = true;
1424 }
1425 if ( pCode->IsRecalcModeAlways() )
1426 { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1427 // for each F9
1428 bDirty = true;
1429 }
1430 // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1431}
1432
1434{
1436}
1437
1438namespace {
1439class RecursionCounter
1440{
1441 ScRecursionHelper& rRec;
1442 bool bStackedInIteration;
1443#if defined DBG_UTIL && !defined NDEBUG
1444 const ScFormulaCell* cell;
1445#endif
1446public:
1447 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
1448 : rRec(r)
1449#if defined DBG_UTIL && !defined NDEBUG
1450 , cell(p)
1451#endif
1452 {
1453 bStackedInIteration = rRec.IsDoingIteration();
1454 if (bStackedInIteration)
1455 rRec.GetRecursionInIterationStack().push( p);
1456 rRec.IncRecursionCount();
1457 }
1458 ~RecursionCounter()
1459 {
1460 rRec.DecRecursionCount();
1461 if (bStackedInIteration)
1462 {
1463#if defined DBG_UTIL && !defined NDEBUG
1464 assert(rRec.GetRecursionInIterationStack().top() == cell);
1465#endif
1466 rRec.GetRecursionInIterationStack().pop();
1467 }
1468 }
1469};
1470
1471// Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group".
1472// Remove the group again at the end, since there are some places throughout the code
1473// that do not handle well groups with just 1 cell. Remove the groups only when the recursion level
1474// reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing
1475// a group immediately would remove the info), for this reason affected cells are stored in the recursion
1476// helper.
1477struct TemporaryCellGroupMaker
1478{
1479 TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable )
1480 : mCell( cell )
1481 , mEnabled( enable )
1482 {
1483 if( mEnabled && mCell->GetCellGroup() == nullptr )
1484 {
1485 mCell->CreateCellGroup( 1, false );
1486 mCell->GetDocument().GetRecursionHelper().AddTemporaryGroupCell( mCell );
1487 }
1488 }
1489 ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE
1490 {
1491 if( mEnabled )
1492 mCell->GetDocument().GetRecursionHelper().CleanTemporaryGroupCells();
1493 }
1494 ScFormulaCell* mCell;
1495 const bool mEnabled;
1496};
1497
1498} // namespace
1499
1500bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset)
1501{
1502 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
1503 bool bGroupInterpreted = false;
1504
1505 // The result would possibly depend on a cell without a valid value, bail out
1506 // the entire dependency computation.
1507 if (rRecursionHelper.IsAbortingDependencyComputation())
1508 return false;
1509
1510 if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent())
1511 return bGroupInterpreted;
1512
1514 TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore );
1515
1516 ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
1517
1518 if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
1519 rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
1520 {
1521 // This call arose from a dependency calculation and we just found a cycle.
1522 // This will mark all elements in the cycle as parts-of-cycle.
1523 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
1524 // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet.
1525 // If there is a genuine circular reference, it will be marked so when all groups
1526 // in the cycle get out of dependency evaluation mode.
1527 // But returning without calculation a new value means other cells depending
1528 // on this one would use a possibly invalid value, so ensure the dependency
1529 // computation is aborted without resetting the dirty flag of any cell.
1530 rRecursionHelper.AbortDependencyComputation();
1531 return bGroupInterpreted;
1532 }
1533
1534#if DEBUG_CALCULATION
1535 static bool bDebugCalculationInit = true;
1536 if (bDebugCalculationInit)
1537 {
1538 aDC.maTrigger = aDebugCalculationTriggerAddress;
1539 aDC.mbPrintResults = true;
1540 bDebugCalculationInit = false;
1541 }
1542 DebugCalculationStacker aDebugEntry(aPos, rDocument);
1543#endif
1544
1545 if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
1546 return bGroupInterpreted; // no double/triple processing
1547
1548 //FIXME:
1549 // If the call originates from a Reschedule in DdeLink update, leave dirty
1550 // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1552 return bGroupInterpreted;
1553
1554 if (bRunning)
1555 {
1557 {
1558 aResult.SetResultError( FormulaError::CircularReference );
1559 return bGroupInterpreted;
1560 }
1561
1562 if (aResult.GetResultError() == FormulaError::CircularReference)
1563 aResult.SetResultError( FormulaError::NONE );
1564
1565 // Start or add to iteration list.
1566 if (!rRecursionHelper.IsDoingIteration() ||
1567 !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
1568 rRecursionHelper.SetInIterationReturn( true);
1569
1570 return bGroupInterpreted;
1571 }
1572 // no multiple interprets for GetErrCode, IsValue, GetValue and
1573 // different entry point recursions. Would also lead to premature
1574 // convergence in iterations.
1575 if (rRecursionHelper.GetIteration() && nSeenInIteration ==
1576 rRecursionHelper.GetIteration())
1577 return bGroupInterpreted;
1578
1579 bool bOldRunning = bRunning;
1580 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1581 {
1582 bRunning = true;
1583 rRecursionHelper.SetInRecursionReturn( true);
1584 }
1585 else
1586 {
1588
1589#if DEBUG_CALCULATION
1590 aDC.enterGroup();
1591#endif
1592 bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
1593 bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset);
1594 bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;
1595
1596#if DEBUG_CALCULATION
1597 aDC.leaveGroup();
1598#endif
1599 if (!bGroupInterpreted)
1600 {
1601 // This call resulted from a dependency calculation for a multigroup-threading attempt,
1602 // but found dependency among the groups.
1603 if (!rRecursionHelper.AreGroupsIndependent())
1604 {
1606 return bGroupInterpreted;
1607 }
1608 // Dependency calc inside InterpretFormulaGroup() failed due to
1609 // detection of a cycle and there are parent FG's in the cycle.
1610 // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
1611 if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
1612 {
1614 return bGroupInterpreted;
1615 }
1616
1617 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
1619 InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1620 }
1621
1623 }
1624
1625 // While leaving a recursion or iteration stack, insert its cells to the
1626 // recursion list in reverse order.
1627 if (rRecursionHelper.IsInReturn())
1628 {
1629 bool bFreeFlyingInserted = false;
1630 if (rRecursionHelper.GetRecursionCount() > 0 || !rRecursionHelper.IsDoingRecursion())
1631 {
1632 rRecursionHelper.Insert( this, bOldRunning, aResult);
1633 bFreeFlyingInserted = mbFreeFlying;
1634 }
1635 bool bIterationFromRecursion = false;
1636 bool bResumeIteration = false;
1637 do
1638 {
1639 if ((rRecursionHelper.IsInIterationReturn() &&
1640 rRecursionHelper.GetRecursionCount() == 0 &&
1641 !rRecursionHelper.IsDoingIteration()) ||
1642 bIterationFromRecursion || bResumeIteration)
1643 {
1644 bool & rDone = rRecursionHelper.GetConvergingReference();
1645 rDone = false;
1646 if (!bIterationFromRecursion && bResumeIteration)
1647 {
1648 bResumeIteration = false;
1649 // Resuming iteration expands the range.
1650 ScFormulaRecursionList::const_iterator aOldStart(
1651 rRecursionHelper.GetLastIterationStart());
1652 rRecursionHelper.ResumeIteration();
1653 // Mark new cells being in iteration.
1654 for (ScFormulaRecursionList::const_iterator aIter(
1655 rRecursionHelper.GetIterationStart()); aIter !=
1656 aOldStart; ++aIter)
1657 {
1658 ScFormulaCell* pIterCell = (*aIter).pCell;
1659 pIterCell->bIsIterCell = true;
1660 }
1661 // Mark older cells dirty again, in case they converted
1662 // without accounting for all remaining cells in the circle
1663 // that weren't touched so far, e.g. conditional. Restore
1664 // backupped result.
1665 sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1666 for (ScFormulaRecursionList::const_iterator aIter(
1667 aOldStart); aIter !=
1668 rRecursionHelper.GetIterationEnd(); ++aIter)
1669 {
1670 ScFormulaCell* pIterCell = (*aIter).pCell;
1671 if (pIterCell->nSeenInIteration == nIteration)
1672 {
1673 if (!pIterCell->bDirty || aIter == aOldStart)
1674 {
1675 pIterCell->aResult = (*aIter).aPreviousResult;
1676 }
1677 --pIterCell->nSeenInIteration;
1678 }
1679 pIterCell->bDirty = true;
1680 }
1681 }
1682 else
1683 {
1684 bResumeIteration = false;
1685 // Close circle once. If 'this' is self-referencing only
1686 // (e.g. counter or self-adder) then it is already
1687 // implicitly closed.
1688 /* TODO: does this even make sense anymore? The last cell
1689 * added above with rRecursionHelper.Insert() should always
1690 * be 'this', shouldn't it? */
1691 if (rRecursionHelper.GetList().size() > 1)
1692 {
1693 ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell;
1694 if (pLastCell != this)
1695 {
1698 pLastCell->InterpretTail(
1699 *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
1701 }
1702 }
1703 // Start at 1, init things.
1704 rRecursionHelper.StartIteration();
1705 // Mark all cells being in iteration. Reset results to
1706 // original values, formula cells have been interpreted
1707 // already, discard that step.
1708 for (ScFormulaRecursionList::const_iterator aIter(
1709 rRecursionHelper.GetIterationStart()); aIter !=
1710 rRecursionHelper.GetIterationEnd(); ++aIter)
1711 {
1712 ScFormulaCell* pIterCell = (*aIter).pCell;
1713 pIterCell->aResult = (*aIter).aPreviousResult;
1714 pIterCell->bIsIterCell = true;
1715 }
1716 }
1717 bIterationFromRecursion = false;
1718 sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount();
1719 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1720 rRecursionHelper.IncIteration())
1721 {
1722 rDone = false;
1723 bool bFirst = true;
1724 for ( ScFormulaRecursionList::iterator aIter(
1725 rRecursionHelper.GetIterationStart()); aIter !=
1726 rRecursionHelper.GetIterationEnd() &&
1727 !rRecursionHelper.IsInReturn(); ++aIter)
1728 {
1729 ScFormulaCell* pIterCell = (*aIter).pCell;
1730 if (pIterCell->IsDirtyOrInTableOpDirty() &&
1731 rRecursionHelper.GetIteration() !=
1732 pIterCell->GetSeenInIteration())
1733 {
1734 (*aIter).aPreviousResult = pIterCell->aResult;
1737 pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
1739 }
1740 if (bFirst)
1741 {
1742 rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1743 bFirst = false;
1744 }
1745 else if (rDone)
1746 {
1747 rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1748 }
1749 }
1750 if (rRecursionHelper.IsInReturn())
1751 {
1752 bResumeIteration = true;
1753 break; // for
1754 // Don't increment iteration.
1755 }
1756 }
1757 if (!bResumeIteration)
1758 {
1759 if (rDone)
1760 {
1761 for (ScFormulaRecursionList::const_iterator aIter(
1762 rRecursionHelper.GetIterationStart());
1763 aIter != rRecursionHelper.GetIterationEnd();
1764 ++aIter)
1765 {
1766 ScFormulaCell* pIterCell = (*aIter).pCell;
1767 pIterCell->bIsIterCell = false;
1768 pIterCell->nSeenInIteration = 0;
1769 pIterCell->bRunning = (*aIter).bOldRunning;
1770 }
1771 }
1772 else
1773 {
1774 for (ScFormulaRecursionList::const_iterator aIter(
1775 rRecursionHelper.GetIterationStart());
1776 aIter != rRecursionHelper.GetIterationEnd();
1777 ++aIter)
1778 {
1779 ScFormulaCell* pIterCell = (*aIter).pCell;
1780 pIterCell->bIsIterCell = false;
1781 pIterCell->nSeenInIteration = 0;
1782 pIterCell->bRunning = (*aIter).bOldRunning;
1783 pIterCell->ResetDirty();
1784 // The difference to Excel is that Excel does not
1785 // produce an error for non-convergence thus a
1786 // delta of 0.001 still works to execute the
1787 // maximum number of iterations and display the
1788 // results no matter if the result anywhere reached
1789 // near delta, but also never indicates whether the
1790 // result actually makes sense in case of
1791 // non-counter context. Calc does check the delta
1792 // in every case. If we wanted to support what
1793 // Excel does then add another option "indicate
1794 // non-convergence error" (default on) and execute
1795 // the following block only if set.
1796#if 1
1797 // If one cell didn't converge, all cells of this
1798 // circular dependency don't, no matter whether
1799 // single cells did.
1800 pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
1801 pIterCell->bChanged = true;
1802#endif
1803 }
1804 }
1805 // End this iteration and remove entries.
1806 rRecursionHelper.EndIteration();
1807 bResumeIteration = rRecursionHelper.IsDoingIteration();
1808 }
1809 }
1810 if (rRecursionHelper.IsInRecursionReturn() &&
1811 rRecursionHelper.GetRecursionCount() == 0 &&
1812 !rRecursionHelper.IsDoingRecursion())
1813 {
1814 bIterationFromRecursion = false;
1815 // Iterate over cells known so far, start with the last cell
1816 // encountered, inserting new cells if another recursion limit
1817 // is reached. Repeat until solved.
1818 rRecursionHelper.SetDoingRecursion( true);
1819 do
1820 {
1821 rRecursionHelper.SetInRecursionReturn( false);
1822 for (ScFormulaRecursionList::const_iterator aIter(
1823 rRecursionHelper.GetIterationStart());
1824 !rRecursionHelper.IsInReturn() && aIter !=
1825 rRecursionHelper.GetIterationEnd(); ++aIter)
1826 {
1827 ScFormulaCell* pCell = (*aIter).pCell;
1828 if (pCell->IsDirtyOrInTableOpDirty())
1829 {
1832 pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1834 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1835 pCell->bRunning = (*aIter).bOldRunning;
1836 }
1837 }
1838 } while (rRecursionHelper.IsInRecursionReturn());
1839 rRecursionHelper.SetDoingRecursion( false);
1840 if (rRecursionHelper.IsInIterationReturn())
1841 {
1842 if (!bResumeIteration)
1843 bIterationFromRecursion = true;
1844 }
1845 else if (bResumeIteration ||
1846 rRecursionHelper.IsDoingIteration())
1847 rRecursionHelper.GetList().erase(
1848 rRecursionHelper.GetIterationStart(),
1849 rRecursionHelper.GetLastIterationStart());
1850 else
1851 rRecursionHelper.Clear();
1852 }
1853 } while (bIterationFromRecursion || bResumeIteration);
1854
1855 if (bFreeFlyingInserted)
1856 {
1857 // Remove this from recursion list, it may get deleted.
1858 // It additionally also should mean that the recursion/iteration
1859 // ends here as it must had been triggered by this free-flying
1860 // out-of-sheets cell
1861 const bool bOnlyThis = (rRecursionHelper.GetList().size() == 1);
1862 rRecursionHelper.GetList().remove_if([this](const ScFormulaRecursionEntry& r){return r.pCell == this;});
1863 if (bOnlyThis)
1864 {
1865 assert(rRecursionHelper.GetList().empty());
1866 if (rRecursionHelper.GetList().empty())
1867 rRecursionHelper.EndIteration();
1868 }
1869 }
1870 }
1871
1872#if DEBUG_CALCULATION
1874 if (nErr != FormulaError::NONE)
1875 aDC.storeResultError( nErr);
1876 else if (aResult.IsValue())
1877 aDC.storeResult( aResult.GetDouble());
1878 else
1879 aDC.storeResult( aResult.GetString());
1880#endif
1881
1882 return bGroupInterpreted;
1883}
1884
1886{
1887 RecursionCounter aRecursionCounter( rDocument.GetRecursionHelper(), this);
1888 // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
1889 if(bIsIterCell)
1891 if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1892 {
1893 // #i11719# no RPN and no error and no token code but result string present
1894 // => interpretation of this cell during name-compilation and unknown names
1895 // => can't exchange underlying code array in CompileTokenArray() /
1896 // Compile() because interpreter's token iterator would crash or pCode
1897 // would be deleted twice if this cell was interpreted during
1898 // compilation.
1899 // This should only be a temporary condition and, since we set an
1900 // error, if ran into it again we'd bump into the dirty-clearing
1901 // condition further down.
1902 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1903 {
1904 pCode->SetCodeError( FormulaError::NoCode );
1905 // This is worth an assertion; if encountered in daily work
1906 // documents we might need another solution. Or just confirm correctness.
1907 return;
1908 }
1910 }
1911
1912 if( pCode->GetCodeLen() )
1913 {
1914 std::unique_ptr<ScInterpreter> pScopedInterpreter;
1915 ScInterpreter* pInterpreter;
1916 if (rContext.pInterpreter)
1917 {
1918 pInterpreter = rContext.pInterpreter;
1919 pInterpreter->Init(this, aPos, *pCode);
1920 }
1921 else
1922 {
1923 pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rContext, aPos, *pCode ));
1924 pInterpreter = pScopedInterpreter.get();
1925 }
1926
1927 FormulaError nOldErrCode = aResult.GetResultError();
1928 if ( nSeenInIteration == 0 )
1929 { // Only the first time
1930 // With bChanged=false, if a newly compiled cell has a result of
1931 // 0.0, no change is detected and the cell will not be repainted.
1932 // bChanged = false;
1933 aResult.SetResultError( FormulaError::NONE );
1934 }
1935
1936 switch ( aResult.GetResultError() )
1937 {
1938 case FormulaError::CircularReference : // will be determined again if so
1939 aResult.SetResultError( FormulaError::NONE );
1940 break;
1941 default: break;
1942 }
1943
1944 bool bOldRunning = bRunning;
1945 bRunning = true;
1946 pInterpreter->Interpret();
1948 {
1949 if (nSeenInIteration > 0)
1950 --nSeenInIteration; // retry when iteration is resumed
1951
1953 aResult.SetToken( pInterpreter->GetResultToken().get() );
1954
1955 return;
1956 }
1957 bRunning = bOldRunning;
1958
1959 // The result may be invalid or depend on another invalid result, just abort
1960 // without updating the cell value. Since the dirty flag will not be reset,
1961 // the proper value will be computed later.
1963 return;
1964
1965 // #i102616# For single-sheet saving consider only content changes, not format type,
1966 // because format type isn't set on loading (might be changed later)
1967 bool bContentChanged = false;
1968
1969 // Do not create a HyperLink() cell if the formula results in an error.
1970 if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
1971 pCode->SetHyperLink(false);
1972
1973 if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
1974 {
1975 bChanged = true;
1976
1977 if (pInterpreter->GetError() == FormulaError::RetryCircular)
1978 {
1979 // Array formula matrix calculation corner case. Keep dirty
1980 // state, do not remove from formula tree or anything else, but
1981 // store FormulaError::CircularReference in case this cell does not get
1982 // recalculated.
1983 aResult.SetResultError( FormulaError::CircularReference);
1984 return;
1985 }
1986
1987 ResetDirty();
1988 }
1989
1990 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1991 {
1992 bool bIsValue = aResult.IsValue(); // the previous type
1993 // Did it converge?
1994 if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
1995 pInterpreter->GetNumResult() - aResult.GetDouble()) <=
1997 (!bIsValue && pInterpreter->GetResultType() == svString &&
1998 pInterpreter->GetStringResult() == aResult.GetString()))
1999 {
2000 // A convergence in the first iteration doesn't necessarily
2001 // mean that it's done, it may be as not all related cells
2002 // of a circle changed their values yet. If the set really
2003 // converges it will do so also during the next iteration. This
2004 // fixes situations like of #i44115#. If this wasn't wanted an
2005 // initial "uncalculated" value would be needed for all cells
2006 // of a circular dependency => graph needed before calculation.
2007 if (nSeenInIteration > 1 ||
2009 {
2010 ResetDirty();
2011 }
2012 }
2013 }
2014
2015 // New error code?
2016 if( pInterpreter->GetError() != nOldErrCode )
2017 {
2018 bChanged = true;
2019 // bContentChanged only has to be set if the file content would be changed
2021 bContentChanged = true;
2022 }
2023
2024 ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
2025
2026 // For IF() and other jumps or changed formatted source data the result
2027 // format may change for different runs, e.g. =IF(B1,B1) with first
2028 // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
2029 // displayed as TRUE. Do not force a general format though if
2030 // mbNeedsNumberFormat is set (because there was a general format..).
2031 // Note that nFormatType may be out of sync here if a format was
2032 // applied or cleared after the last run, but obtaining the current
2033 // format always just to check would be expensive. There may be
2034 // cases where the format should be changed but is not. If that turns
2035 // out to be a real problem then obtain the current format type after
2036 // the initial check when needed.
2037 bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
2039
2040 // We have some requirements additionally to IsCompatible().
2041 // * Do not apply a NumberFormat::LOGICAL if the result value is not
2042 // 1.0 or 0.0
2043 // * Do not override an already set numeric number format if the result
2044 // is of type NumberFormat::LOGICAL, it could be user applied.
2045 // On the other hand, for an empty jump path instead of FALSE an
2046 // unexpected for example 0% could be displayed. YMMV.
2047 // * Never override a non-standard number format that indicates user
2048 // applied.
2049 // * NumberFormat::TEXT does not force a change.
2050 if (bForceNumberFormat)
2051 {
2052 sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
2053 const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
2054 if (nRetType == SvNumFormatType::LOGICAL)
2055 {
2056 double fVal = aNewResult.GetDouble();
2057 if (fVal != 1.0 && fVal != 0.0)
2058 bForceNumberFormat = false;
2059 else
2060 {
2061 nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2062 nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2063 switch (nFormatType)
2064 {
2065 case SvNumFormatType::PERCENT:
2066 case SvNumFormatType::CURRENCY:
2067 case SvNumFormatType::SCIENTIFIC:
2068 case SvNumFormatType::FRACTION:
2069 bForceNumberFormat = false;
2070 break;
2071 case SvNumFormatType::NUMBER:
2072 if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
2073 bForceNumberFormat = false;
2074 break;
2075 default: break;
2076 }
2077 }
2078 }
2079 else if (nRetType == SvNumFormatType::TEXT)
2080 {
2081 bForceNumberFormat = false;
2082 }
2083 if (bForceNumberFormat)
2084 {
2085 if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
2086 {
2087 nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2088 nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2089 }
2090 if (nOldFormatIndex !=
2091 ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
2092 bForceNumberFormat = false;
2093 }
2094 }
2095
2096 if( mbNeedsNumberFormat || bForceNumberFormat )
2097 {
2098 bool bSetFormat = true;
2099 const SvNumFormatType nOldFormatType = nFormatType;
2100 nFormatType = pInterpreter->GetRetFormatType();
2101 sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
2102
2103 if (nFormatType == SvNumFormatType::TEXT)
2104 {
2105 // Don't set text format as hard format.
2106 bSetFormat = false;
2107 }
2108 else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
2109 {
2110 // In a matrix range do not set an (inherited) logical format
2111 // as hard format if the value does not represent a strict TRUE
2112 // or FALSE value. But do set for a top left error value so
2113 // following matrix cells can inherit for non-error values.
2114 // This solves a problem with IF() expressions in array context
2115 // where incidentally the top left element results in logical
2116 // type but some others don't. It still doesn't solve the
2117 // reverse case though, where top left is not logical type but
2118 // some other elements should be. We'd need to transport type
2119 // or format information on arrays.
2120 StackVar eNewCellResultType = aNewResult.GetCellResultType();
2121 if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
2122 {
2123 if (eNewCellResultType != svDouble)
2124 {
2125 bSetFormat = false;
2126 nFormatType = nOldFormatType; // that? or number?
2127 }
2128 else
2129 {
2130 double fVal = aNewResult.GetDouble();
2131 if (fVal != 1.0 && fVal != 0.0)
2132 {
2133 bSetFormat = false;
2134 nFormatType = SvNumFormatType::NUMBER;
2135 }
2136 }
2137 }
2138 }
2139
2140 if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)))
2141 nFormatIndex = ScGlobal::GetStandardFormat(*rContext.GetFormatTable(),
2142 nFormatIndex, nFormatType);
2143
2144 // Do not replace a General format (which was the reason why
2145 // mbNeedsNumberFormat was set) with a General format.
2146 // 1. setting a format has quite some overhead in the
2147 // ScPatternAttr/ScAttrArray handling, even if identical.
2148 // 2. the General formats may be of different locales.
2149 // XXX if mbNeedsNumberFormat was set even if the current format
2150 // was not General then we'd have to obtain the current format here
2151 // and check at least the types.
2152 const bool bSetNumberFormat = bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0));
2153 if (bSetNumberFormat && !rDocument.IsInLayoutStrings())
2154 {
2155 // set number format explicitly
2157 rDocument.SetNumberFormat( aPos, nFormatIndex );
2158 else
2159 {
2160 // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work
2161 // to the main thread. Since thread calculations operate on formula groups,
2162 // it's enough to store just the row.
2163 DelayedSetNumberFormat data = { aPos.Col(), aPos.Row(), nFormatIndex };
2164 rContext.maDelayedSetNumberFormat.push_back( data );
2165 }
2166 bChanged = true;
2167 }
2168
2169 // Currently (2019-05-10) nothing else can cope with a duration
2170 // format type, change to time as it was before.
2171 if (nFormatType == SvNumFormatType::DURATION)
2172 nFormatType = SvNumFormatType::TIME;
2173
2174 mbNeedsNumberFormat = false;
2175 }
2176
2177 // In case of changes just obtain the result, no temporary and
2178 // comparison needed anymore.
2179 if (bChanged)
2180 {
2181 // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
2182 // Also handle special cases of initial results after loading.
2183 if ( !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2184 {
2186 StackVar eNew = aNewResult.GetCellResultType();
2187 if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
2188 {
2189 // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
2190 // -> no change
2191 }
2192 else
2193 {
2194 if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
2195 eOld = svString; // ScHybridCellToken has a valid GetString method
2196
2197 // #i106045# use approxEqual to compare with stored value
2198 bContentChanged = (eOld != eNew ||
2199 (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
2200 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2201 }
2202 }
2203
2204 aResult.SetToken( pInterpreter->GetResultToken().get() );
2205 }
2206 else
2207 {
2209 StackVar eNew = aNewResult.GetCellResultType();
2210 bChanged = (eOld != eNew ||
2211 (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
2212 (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2213
2214 // #i102616# handle special cases of initial results after loading
2215 // (only if the sheet is still marked unchanged)
2216 if ( bChanged && !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2217 {
2218 if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
2219 ((eOld == svHybridCell) &&
2220 eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
2221 (eOld == svDouble && eNew == svDouble &&
2222 rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
2223 {
2224 // no change, see above
2225 }
2226 else
2227 bContentChanged = true;
2228 }
2229
2230 aResult.Assign( aNewResult);
2231 }
2232
2233 // Precision as shown?
2234 if ( aResult.IsValue() && pInterpreter->GetError() == FormulaError::NONE
2236 && nFormatType != SvNumFormatType::DATE
2237 && nFormatType != SvNumFormatType::TIME
2238 && nFormatType != SvNumFormatType::DATETIME )
2239 {
2240 sal_uInt32 nFormat = rDocument.GetNumberFormat( rContext, aPos );
2242 aResult.GetDouble(), nFormat, &rContext));
2243 }
2244 if (eTailParam == SCITP_NORMAL)
2245 {
2246 ResetDirty();
2247 }
2248 if( aResult.GetMatrix() )
2249 {
2250 // If the formula wasn't entered as a matrix formula, live on with
2251 // the upper left corner and let reference counting delete the matrix.
2254 }
2255 if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
2256 {
2257 // Coded double error may occur via filter import.
2259 aResult.SetResultError( nErr);
2260 bChanged = bContentChanged = true;
2261 }
2262
2263 if (bContentChanged && rDocument.IsStreamValid(aPos.Tab()))
2264 {
2265 // pass bIgnoreLock=true, because even if called from pending row height update,
2266 // a changed result must still reset the stream flag
2267 rDocument.SetStreamValid(aPos.Tab(), false, true);
2268 }
2271
2272 // FORCED cells also immediately tested for validity (start macro possibly)
2273
2274 if ( pCode->IsRecalcModeForced() )
2275 {
2276 sal_uLong nValidation = rDocument.GetAttr(
2277 aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
2278 if ( nValidation )
2279 {
2280 const ScValidationData* pData = rDocument.GetValidationEntry( nValidation );
2281 ScRefCellValue aTmpCell(this);
2282 if ( pData && !pData->IsDataValid(aTmpCell, aPos))
2283 pData->DoCalcError( this );
2284 }
2285 }
2286
2287 // Reschedule slows the whole thing down considerably, thus only execute on percent change
2289 {
2291 if (pProgress && pProgress->Enabled())
2292 {
2293 pProgress->SetStateCountDownOnPercent(
2295 }
2296
2297 switch (pInterpreter->GetVolatileType())
2298 {
2300 // Volatile via built-in volatile functions. No actions needed.
2301 break;
2303 // The formula contains a volatile macro.
2307 break;
2310 {
2311 // The formula was previously volatile, but no more.
2314 }
2315 else
2316 {
2317 // non-volatile formula. End listening to the area in case
2318 // it's listening due to macro module change.
2320 }
2322 break;
2323 default:
2324 ;
2325 }
2326 }
2327 }
2328 else
2329 {
2330 // Cells with compiler errors should not be marked dirty forever
2331 OSL_ENSURE( pCode->GetCodeError() != FormulaError::NONE, "no RPN code and no errors ?!?!" );
2332 ResetDirty();
2333 }
2334
2336}
2337
2339{
2340 if( !pCode->GetCodeLen() )
2341 return;
2342
2343 if ( !pCode->IsRecalcModeAlways() )
2345
2346 std::unique_ptr<ScInterpreter> pScopedInterpreter;
2347 if (pInterpreter)
2348 pInterpreter->Init(this, aPos, *pCode);
2349 else
2350 {
2351 pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode ));
2352 pInterpreter = pScopedInterpreter.get();
2353 }
2354
2355 switch (pInterpreter->GetVolatileType())
2356 {
2358 // The formula contains a volatile macro.
2362 break;
2365 {
2366 // The formula was previously volatile, but no more.
2369 }
2370 else
2371 {
2372 // non-volatile formula. End listening to the area in case
2373 // it's listening due to macro module change.
2375 }
2377 break;
2378 default:
2379 ;
2380 }
2381}
2382
2384{
2385 bCompile = bVal;
2386}
2387
2389{
2391 if (pMat)
2392 pMat->SetMatColsRows( nCols, nRows );
2393 else if (nCols || nRows)
2394 {
2395 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2396 // Setting the new token actually forces an empty result at this top
2397 // left cell, so have that recalculated.
2398 SetDirty();
2399 }
2400}
2401
2402void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2403{
2405 if (pMat)
2406 pMat->GetMatColsRows( nCols, nRows);
2407 else
2408 {
2409 nCols = 0;
2410 nRows = 0;
2411 }
2412}
2413
2415{
2416 bInChangeTrack = bVal;
2417}
2418
2419void ScFormulaCell::Notify( const SfxHint& rHint )
2420{
2422 return;
2423
2424 const SfxHintId nHint = rHint.GetId();
2425 if (nHint == SfxHintId::ScReference)
2426 {
2427 const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2428
2429 switch (rRefHint.getType())
2430 {
2432 {
2433 const sc::RefColReorderHint& rRefColReorder =
2434 static_cast<const sc::RefColReorderHint&>(rRefHint);
2435 if (!IsShared() || IsSharedTop())
2437 aPos, rRefColReorder.getTab(),
2438 rRefColReorder.getStartRow(),
2439 rRefColReorder.getEndRow(),
2440 rRefColReorder.getColMap());
2441 }
2442 break;
2444 {
2445 const sc::RefRowReorderHint& rRefRowReorder =
2446 static_cast<const sc::RefRowReorderHint&>(rRefHint);
2447 if (!IsShared() || IsSharedTop())
2449 aPos, rRefRowReorder.getTab(),
2450 rRefRowReorder.getStartColumn(),
2451 rRefRowReorder.getEndColumn(),
2452 rRefRowReorder.getRowMap());
2453 }
2454 break;
2456 {
2458 }
2459 break;
2461 {
2463 }
2464 break;
2465 default:
2466 ;
2467 }
2468
2469 return;
2470 }
2471
2473 return;
2474
2475 if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged)))
2476 return;
2477
2478 bool bForceTrack = false;
2479 if ( nHint == SfxHintId::ScTableOpDirty )
2480 {
2481 bForceTrack = !bTableOpDirty;
2482 if ( !bTableOpDirty )
2483 {
2485 bTableOpDirty = true;
2486 }
2487 }
2488 else
2489 {
2490 bForceTrack = !bDirty;
2491 SetDirtyVar();
2492 }
2493 // Don't remove from FormulaTree to put in FormulaTrack to
2494 // put in FormulaTree again and again, only if necessary.
2495 // Any other means except ScRecalcMode::ALWAYS by which a cell could
2496 // be in FormulaTree if it would notify other cells through
2497 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2498 // Yes. The new TableOpDirty made it necessary to have a
2499 // forced mode where formulas may still be in FormulaTree from
2500 // TableOpDirty but have to notify dependents for normal dirty.
2501 if ( (bForceTrack || !rDocument.IsInFormulaTree( this )
2503 && !rDocument.IsInFormulaTrack( this ) )
2505}
2506
2508{
2509 switch (rQuery.getId())
2510 {
2512 {
2513 sc::RefQueryFormulaGroup& rRefQuery =
2514 static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2515 if (IsShared())
2516 rRefQuery.add(aPos);
2517 }
2518 break;
2519 default:
2520 ;
2521 }
2522}
2523
2524void ScFormulaCell::SetDirty( bool bDirtyFlag )
2525{
2526 if (IsInChangeTrack())
2527 return;
2528
2530 {
2531 SetDirtyVar();
2532 rDocument.SetStreamValid(aPos.Tab(), false);
2533 return;
2534 }
2535
2536 // Avoid multiple formula tracking in Load() and in CompileAll()
2537 // after CopyScenario() and CopyBlockFromClip().
2538 // If unconditional formula tracking is needed, set bDirty=false
2539 // before calling SetDirty(), for example in CompileTokenArray().
2540 if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) )
2541 {
2542 if( bDirtyFlag )
2543 SetDirtyVar();
2545
2546 // While loading a document listeners have not been established yet.
2547 // Tracking would remove this cell from the FormulaTrack and add it to
2548 // the FormulaTree, once in there it would be assumed that its
2549 // dependents already had been tracked and it would be skipped on a
2550 // subsequent notify. Postpone tracking until all listeners are set.
2553 }
2554
2555 rDocument.SetStreamValid(aPos.Tab(), false);
2556}
2557
2559{
2560 bDirty = true;
2561 mbPostponedDirty = false;
2562 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2563 {
2564 mxGroup->meCalcState = sc::GroupCalcEnabled;
2565 mxGroup->mbPartOfCycle = false;
2566 }
2567
2568 // mark the sheet of this cell to be calculated
2569 //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() );
2570}
2571
2573{
2574 bDirty = true;
2577}
2578
2580{
2581 bTableOpDirty = false;
2582}
2583
2585{
2586 if ( IsInChangeTrack() )
2587 return;
2588
2590 bTableOpDirty = true;
2591 else
2592 {
2593 if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) )
2594 {
2595 if ( !bTableOpDirty )
2596 {
2598 bTableOpDirty = true;
2599 }
2601 rDocument.TrackFormulas( SfxHintId::ScTableOpDirty );
2602 }
2603 }
2604}
2605
2607{
2609}
2610
2612{
2613 aResult.SetToken(pToken);
2614}
2615
2617{
2618 return aResult.GetString();
2619}
2620
2622{
2624}
2625
2627{
2628 aResult.SetMatrix(nCols, nRows, pMat, pUL);
2629}
2630
2632{
2633 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2634 * used whether it is solely for transport of a simple result error and get
2635 * rid of that abuse. */
2636 pCode->SetCodeError( n );
2637 // Hard set errors are transported as result type value per convention,
2638 // e.g. via clipboard. ScFormulaResult::IsValue() and
2639 // ScFormulaResult::GetDouble() handle that.
2641}
2642
2644{
2646}
2647
2649{
2650 if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
2651 SetDirtyVar();
2652 pCode->AddRecalcMode( nBits );
2653}
2654
2656{
2658}
2659
2661{
2663}
2664
2666{
2668}
2669
2670void ScFormulaCell::SetHybridFormula( const OUString& r,
2671 const formula::FormulaGrammar::Grammar eGrammar )
2672{
2673 aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2674}
2675
2677{
2678 return aResult.GetHybridFormula();
2679}
2680
2681// Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2682void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2683{
2684 OUString aCellString;
2685
2686 const Color* pColor;
2687
2688 // Cell Text uses the Cell format while the URL uses
2689 // the default format for the type.
2690 const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos );
2692
2693 const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
2694
2695 if ( IsValue() )
2696 {
2697 double fValue = GetValue();
2698 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2699 }
2700 else
2701 {
2702 aCellString = GetString().getString();
2703 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2704 }
2706 if (xMat)
2707 {
2708 // determine if the matrix result is a string or value.
2709 if (!xMat->IsValue(0, 1))
2710 rURL = xMat->GetString(0, 1).getString();
2711 else
2712 pFormatter->GetOutputString(
2713 xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2714 }
2715
2716 if(rURL.isEmpty())
2717 {
2718 if(IsValue())
2719 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2720 else
2721 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2722 }
2723}
2724
2726{
2727 if (!IsValue())
2728 return aResult.IsMultiline();
2729 return false;
2730}
2731
2733{
2734 return pCode && pCode->IsHyperLink();
2735}
2736
2737std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
2738{
2739 OUString aCellText;
2740 OUString aURL;
2741 GetURLResult( aURL, aCellText );
2742
2744}
2745
2747{
2750}
2751
2753{
2756}
2757
2759{
2761 return aResult.IsValue();
2762}
2763
2765{
2767 if (pCode->GetCodeError() != FormulaError::NONE)
2768 return false;
2769
2770 return aResult.IsValueNoError();
2771}
2772
2774{
2775 if (NeedsInterpret())
2776 // false if the cell is dirty & needs to be interpreted.
2777 return false;
2778
2779 if (pCode->GetCodeError() != FormulaError::NONE)
2780 return false;
2781
2782 return aResult.IsValueNoError();
2783}
2784
2786{
2788 return GetRawValue();
2789}
2790
2792{
2794 return GetRawString();
2795}
2796
2798{
2799 if ((pCode->GetCodeError() == FormulaError::NONE) &&
2800 aResult.GetResultError() == FormulaError::NONE)
2801 return aResult.GetDouble();
2802 return 0.0;
2803}
2804
2806{
2807 if ((pCode->GetCodeError() == FormulaError::NONE) &&
2808 aResult.GetResultError() == FormulaError::NONE)
2809 return aResult.GetString();
2810
2812}
2813
2815{
2816 if ( rDocument.GetAutoCalc() )
2817 {
2819 // Was stored !bDirty but an accompanying matrix cell was bDirty?
2821 Interpret();
2822 }
2823 return aResult.GetMatrix().get();
2824}
2825
2827{
2828 switch ( cMatrixFlag )
2829 {
2831 rPos = aPos;
2832 return true;
2834 {
2837 if( t )
2838 {
2839 ScSingleRefData& rRef = *t->GetSingleRef();
2840 ScAddress aAbs = rRef.toAbs(rDoc, aPos);
2841 if (rDoc.ValidAddress(aAbs))
2842 {
2843 rPos = aAbs;
2844 return true;
2845 }
2846 }
2847 }
2848 break;
2849 default: break;
2850 }
2851 return false;
2852}
2853
2855{
2856 switch ( cMatrixFlag )
2857 {
2860 {
2861 static thread_local SCCOL nC;
2862 static thread_local SCROW nR;
2863 ScAddress aOrg;
2864 if ( !GetMatrixOrigin( rDoc, aOrg ) )
2866 if ( aOrg != rOrgPos )
2867 { // First time or a different matrix than last time.
2868 rOrgPos = aOrg;
2869 const ScFormulaCell* pFCell;
2871 pFCell = rDocument.GetFormulaCell(aOrg);
2872 else
2873 pFCell = this; // this ScMatrixMode::Formula
2874 // There's only one this, don't compare pFCell==this.
2875 if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
2876 {
2877 pFCell->GetMatColsRows( nC, nR );
2878 if ( nC == 0 || nR == 0 )
2879 {
2880 // No ScMatrixFormulaCellToken available yet, calculate new.
2881 nC = 1;
2882 nR = 1;
2883 ScAddress aTmpOrg;
2884 ScFormulaCell* pCell;
2885 ScAddress aAdr( aOrg );
2886 aAdr.IncCol();
2887 bool bCont = true;
2888 do
2889 {
2890 pCell = rDocument.GetFormulaCell(aAdr);
2891 if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2892 pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2893 {
2894 nC++;
2895 aAdr.IncCol();
2896 }
2897 else
2898 bCont = false;
2899 } while ( bCont );
2900 aAdr = aOrg;
2901 aAdr.IncRow();
2902 bCont = true;
2903 do
2904 {
2905 pCell = rDocument.GetFormulaCell(aAdr);
2906 if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2907 pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2908 {
2909 nR++;
2910 aAdr.IncRow();
2911 }
2912 else
2913 bCont = false;
2914 } while ( bCont );
2915
2916 const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2917 }
2918 }
2919 else
2920 {
2921#if OSL_DEBUG_LEVEL > 0
2922 SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
2924 << ", MatOrg: "
2926#endif
2928 }
2929 }
2930 // here we are, healthy and clean, somewhere in between
2931 SCCOL dC = aPos.Col() - aOrg.Col();
2932 SCROW dR = aPos.Row() - aOrg.Row();
2934 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2935 {
2936 if ( dC == 0 )
2937 nEdges |= sc::MatrixEdge::Left;
2938 if ( dC+1 == nC )
2939 nEdges |= sc::MatrixEdge::Right;
2940 if ( dR == 0 )
2941 nEdges |= sc::MatrixEdge::Top;
2942 if ( dR+1 == nR )
2943 nEdges |= sc::MatrixEdge::Bottom;
2944 if ( nEdges == sc::MatrixEdge::Nothing )
2945 nEdges = sc::MatrixEdge::Inside;
2946 }
2947 else
2948 {
2949 SAL_WARN( "sc", "broken Matrix, Pos: "
2951 << ", MatOrg: "
2953 << ", MatCols: " << static_cast<sal_Int32>( nC )
2954 << ", MatRows: " << static_cast<sal_Int32>( nR )
2955 << ", DiffCols: " << static_cast<sal_Int32>( dC )
2956 << ", DiffRows: " << static_cast<sal_Int32>( dR ));
2957 }
2958 return nEdges;
2959 }
2960 default:
2962 }
2963}
2964
2966{
2968
2969 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2970 * and not also abused for signaling other error conditions we could bail
2971 * out even before attempting to interpret broken code. */
2973 if (nErr != FormulaError::NONE)
2974 return nErr;
2975 return aResult.GetResultError();
2976}
2977
2979{
2981 if (nErr != FormulaError::NONE)
2982 return nErr;
2983 return aResult.GetResultError();
2984}
2985
2987{
2989
2990 rErr = pCode->GetCodeError();
2991 if (rErr != FormulaError::NONE)
2992 return true;
2993
2994 return aResult.GetErrorOrDouble(rErr, rVal);
2995}
2996
2998{
3000
3002 if (nErr != FormulaError::NONE)
3003 return sc::FormulaResultValue(nErr);
3004
3005 return aResult.GetResult();
3006}
3007
3009{
3011 if (nErr != FormulaError::NONE)
3012 return sc::FormulaResultValue(nErr);
3013
3014 return aResult.GetResult();
3015}
3016
3018{
3021 if( p && !aIter.GetNextReferenceRPN() ) // only one!
3022 {
3023 SingleDoubleRefProvider aProv( *p );
3024 r.aStart = aProv.Ref1.toAbs(rDocument, aPos);
3025 r.aEnd = aProv.Ref2.toAbs(rDocument, aPos);
3026 return true;
3027 }
3028 else
3029 return false;
3030}
3031
3032bool
3034{
3035 /* If there appears just one reference in the formula, it's the same
3036 as HasOneReference(). If there are more of them, they can denote
3037 one range if they are (sole) arguments of one function.
3038 Union of these references must form one range and their
3039 intersection must be empty set.
3040 */
3041
3042 // Detect the simple case of exactly one reference in advance without all
3043 // overhead.
3044 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
3045 // work again, where the function does not have only references.
3046 if (HasOneReference( rRange))
3047 return true;
3048
3049 // Get first reference, if any
3051 formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
3052 if (pFirstReference)
3053 {
3054 // Collect all consecutive references, starting by the one
3055 // already found
3056 std::vector<formula::FormulaToken*> aReferences { pFirstReference };
3057 FormulaToken* pToken(aIter.NextRPN());
3058 FormulaToken* pFunction(nullptr);
3059 while (pToken)
3060 {
3061 if (lcl_isReference(*pToken))
3062 {
3063 aReferences.push_back(pToken);
3064 pToken = aIter.NextRPN();
3065 }
3066 else
3067 {
3068 if (pToken->IsFunction())
3069 {
3070 pFunction = pToken;
3071 }
3072 break;
3073 }
3074 }
3075 if (pFunction && !aIter.GetNextReferenceRPN()
3076 && (pFunction->GetParamCount() == aReferences.size()))
3077 {
3078 return lcl_refListFormsOneRange(rDocument, aPos, aReferences, rRange);
3079 }
3080 }
3081 return false;
3082}
3083
3085{
3086 RelNameRef eRelNameRef = RelNameRef::NONE;
3089 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
3090 {
3091 switch (t->GetType())
3092 {
3094 if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
3095 eRelNameRef = RelNameRef::SINGLE;
3096 break;
3098 if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
3099 // May originate from individual cell names, in which case
3100 // it needs recompilation.
3101 return RelNameRef::DOUBLE;
3102 /* TODO: have an extra flag at ScComplexRefData if range was
3103 * extended? or too cumbersome? might narrow recompilation to
3104 * only needed cases.
3105 * */
3106 break;
3107 default:
3108 ; // nothing
3109 }
3110 }
3111 return eRelNameRef;
3112}
3113
3115{
3116 if (rCxt.meMode != URM_INSDEL)
3117 // Just in case...
3118 return false;
3119
3120 if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
3121 // No movement.
3122 return false;
3123
3124 if (!rCxt.maRange.Contains(aPos))
3125 return false;
3126
3127 // This formula cell itself is being shifted during cell range
3128 // insertion or deletion. Update its position.
3130 if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
3131 {
3132 assert(!"can't move ScFormulaCell");
3133 }
3134
3135 return true;
3136}
3137
3138namespace {
3139
3143bool checkCompileColRowName(
3144 const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
3145 const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
3146{
3147 switch (rCxt.meMode)
3148 {
3149 case URM_INSDEL:
3150 {
3151 if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
3152 return false;
3153
3156 ScRangePairList* pColList = rDoc.GetColNameRanges();
3157 ScRangePairList* pRowList = rDoc.GetRowNameRanges();
3158 while ((t = aIter.GetNextColRowName()) != nullptr)
3159 {
3160 ScSingleRefData& rRef = *t->GetSingleRef();
3161 if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
3162 { // ColName
3163 ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3164 ScRangePair* pR = pColList->Find( aAdr );
3165 if ( pR )
3166 { // defined
3167 if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
3168 return true;
3169 }
3170 else
3171 { // on the fly
3172 if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
3173 return true;
3174 }
3175 }
3176 if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
3177 { // RowName
3178 ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3179 ScRangePair* pR = pRowList->Find( aAdr );
3180 if ( pR )
3181 { // defined
3182 if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
3183 return true;
3184 }
3185 else
3186 { // on the fly
3187 if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
3188 return true;
3189 }
3190 }
3191 }
3192 }
3193 break;
3194 case URM_MOVE:
3195 { // Recompile for Move/D&D when ColRowName was moved or this Cell
3196 // points to one and was moved.
3197 bool bMoved = (aPos != aOldPos);
3198 if (bMoved)
3199 return true;
3200
3202 const formula::FormulaToken* t = aIter.GetNextColRowName();
3203 for (; t; t = aIter.GetNextColRowName())
3204 {
3205 const ScSingleRefData& rRef = *t->GetSingleRef();
3206 ScAddress aAbs = rRef.toAbs(rDoc, aPos);
3207 if (rDoc.ValidAddress(aAbs))
3208 {
3209 if (rCxt.maRange.Contains(aAbs))
3210 return true;
3211 }
3212 }
3213 }
3214 break;
3215 case URM_COPY:
3216 return bValChanged;
3217 default:
3218 ;
3219 }
3220
3221 return false;
3222}
3223
3224void setOldCodeToUndo(
3225 ScDocument& rUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
3226{
3227 // Copy the cell to aUndoPos, which is its current position in the document,
3228 // so this works when UpdateReference is called before moving the cells
3229 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
3230 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
3231
3232 // If there is already a formula cell in the undo document, don't overwrite it,
3233 // the first (oldest) is the important cell.
3234 if (rUndoDoc.GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3235 return;
3236
3237 ScFormulaCell* pFCell =
3238 new ScFormulaCell(
3239 rUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(rUndoDoc), eTempGrammar, cMatrixFlag);
3240
3241 pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3242 rUndoDoc.SetFormulaCell(aUndoPos, pFCell);
3243}
3244
3245}
3246
3248 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3249{
3250 if (rCxt.meMode != URM_INSDEL)
3251 // Just in case...
3252 return false;
3253
3254 bool bCellStateChanged = false;
3255 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3256 if ( pUndoCellPos )
3257 aUndoPos = *pUndoCellPos;
3258 ScAddress aOldPos( aPos );
3259 bCellStateChanged = UpdatePosOnShift(rCxt);
3260
3261 // Check presence of any references or column row names.
3262 bool bHasRefs = pCode->HasReferences();
3263 bool bHasColRowNames = false;
3264 if (!bHasRefs)
3265 {
3266 bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3267 bHasRefs = bHasColRowNames;
3268 }
3269 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3270
3271 if (!bHasRefs && !bOnRefMove)
3272 // This formula cell contains no references, nor needs recalculating
3273 // on reference update. Bail out.
3274 return bCellStateChanged;
3275
3276 std::unique_ptr<ScTokenArray> pOldCode;
3277 if (pUndoDoc)
3278 pOldCode = pCode->Clone();
3279
3280 bool bValChanged = false;
3281 bool bRefModified = false;
3282 bool bRecompile = bCompile;
3283
3284 if (bHasRefs)
3285 {
3286 // Update cell or range references.
3287 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
3288 bRefModified = aRes.mbReferenceModified;
3289 bValChanged = aRes.mbValueChanged;
3290 if (aRes.mbNameModified)
3291 bRecompile = true;
3292 }
3293
3294 if (bValChanged || bRefModified)
3295 bCellStateChanged = true;
3296
3297 if (bOnRefMove)
3298 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3299 bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
3300
3301 bool bNewListening = false;
3302 bool bInDeleteUndo = false;
3303
3304 if (bHasRefs)
3305 {
3306 // Upon Insert ColRowNames have to be recompiled in case the
3307 // insertion occurs right in front of the range.
3308 if (bHasColRowNames && !bRecompile)
3309 bRecompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3310
3311 ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3312 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3313
3314 // RelNameRefs are always moved
3315 bool bHasRelName = false;
3316 if (!bRecompile)
3317 {
3318 RelNameRef eRelNameRef = HasRelNameReference();
3319 bHasRelName = (eRelNameRef != RelNameRef::NONE);
3320 bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
3321 }
3322 // Reference changed and new listening needed?
3323 // Except in Insert/Delete without specialities.
3324 bNewListening = (bRefModified || bRecompile
3325 || (bValChanged && bInDeleteUndo) || bHasRelName);
3326
3327 if ( bNewListening )
3328 EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3329 }
3330
3331 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3332 bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
3333
3334 if (pUndoDoc && (bValChanged || bOnRefMove))
3335 setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3336
3337 bCompile |= bRecompile;
3338 if (bCompile)
3339 {
3340 CompileTokenArray( bNewListening ); // no Listening
3341 bNeedDirty = true;
3342 }
3343
3344 if ( !bInDeleteUndo )
3345 { // In ChangeTrack Delete-Reject listeners are established in
3346 // InsertCol/InsertRow
3347 if ( bNewListening )
3348 {
3349 // Inserts/Deletes re-establish listeners after all
3350 // UpdateReference calls.
3351 // All replaced shared formula listeners have to be
3352 // established after an Insert or Delete. Do nothing here.
3353 SetNeedsListening( true);
3354 }
3355 }
3356
3357 if (bNeedDirty)
3358 { // Cut off references, invalid or similar?
3359 // Postpone SetDirty() until all listeners have been re-established in
3360 // Inserts/Deletes.
3361 mbPostponedDirty = true;
3362 }
3363
3364 return bCellStateChanged;
3365}
3366
3368 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3369{
3370 if (rCxt.meMode != URM_MOVE)
3371 return false;
3372
3373 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3374 if ( pUndoCellPos )
3375 aUndoPos = *pUndoCellPos;
3376 ScAddress aOldPos( aPos );
3377
3378 bool bCellInMoveTarget = rCxt.maRange.Contains(aPos);
3379
3380 if ( bCellInMoveTarget )
3381 {
3382 // The cell is being moved or copied to a new position. I guess the
3383 // position has been updated prior to this call? Determine
3384 // its original position before the move which will be used to adjust
3385 // relative references later.
3386 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3387 }
3388
3389 // Check presence of any references or column row names.
3390 bool bHasRefs = pCode->HasReferences();
3391 bool bHasColRowNames = false;
3392 if (!bHasRefs)
3393 {
3394 bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3395 bHasRefs = bHasColRowNames;
3396 }
3397 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3398
3399 if (!bHasRefs && !bOnRefMove)
3400 // This formula cell contains no references, nor needs recalculating
3401 // on reference update. Bail out.
3402 return false;
3403
3404 bool bCellStateChanged = false;
3405 std::unique_ptr<ScTokenArray> pOldCode;
3406 if (pUndoDoc)
3407 pOldCode = pCode->Clone();
3408
3409 bool bValChanged = false;
3410 bool bRefModified = false;
3411
3412 if (bHasRefs)
3413 {
3414 // Update cell or range references.
3415 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3416 bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3417 bValChanged = aRes.mbValueChanged;
3418 if (aRes.mbNameModified)
3419 // Re-compile to get the RPN token regenerated to reflect updated names.
3420 bCompile = true;
3421 }
3422
3423 if (bValChanged || bRefModified)
3424 bCellStateChanged = true;
3425
3426 if (bOnRefMove)
3427 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3428 bOnRefMove = (bValChanged || (aPos != aOldPos));
3429
3430 bool bColRowNameCompile = false;
3431 bool bHasRelName = false;
3432 bool bNewListening = false;
3433 bool bInDeleteUndo = false;
3434
3435 if (bHasRefs)
3436 {
3437 // Upon Insert ColRowNames have to be recompiled in case the
3438 // insertion occurs right in front of the range.
3439 if (bHasColRowNames)
3440 bColRowNameCompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3441
3442 ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3443 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3444
3445 // RelNameRefs are always moved
3446 RelNameRef eRelNameRef = HasRelNameReference();
3447 bHasRelName = (eRelNameRef != RelNameRef::NONE);
3448 bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
3449 // Reference changed and new listening needed?
3450 // Except in Insert/Delete without specialties.
3451 bNewListening = (bRefModified || bColRowNameCompile
3452 || bValChanged || bHasRelName)
3453 // #i36299# Don't duplicate action during cut&paste / drag&drop
3454 // on a cell in the range moved, start/end listeners is done
3455 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3457
3458 if ( bNewListening )
3459 EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3460 }
3461
3462 bool bNeedDirty = false;
3463 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3464 if ( bRefModified || bColRowNameCompile ||
3465 (bValChanged && bHasRelName ) || bOnRefMove)
3466 bNeedDirty = true;
3467
3468 if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
3469 setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3470
3471 bValChanged = false;
3472
3473 bCompile = (bCompile || bValChanged || bColRowNameCompile);
3474 if ( bCompile )
3475 {
3476 CompileTokenArray( bNewListening ); // no Listening
3477 bNeedDirty = true;
3478 }
3479
3480 if ( !bInDeleteUndo )
3481 { // In ChangeTrack Delete-Reject listeners are established in
3482 // InsertCol/InsertRow
3483 if ( bNewListening )
3484 {
3486 }
3487 }
3488
3489 if (bNeedDirty)
3490 { // Cut off references, invalid or similar?
3491 sc::AutoCalcSwitch aACSwitch(rDocument, false);
3492 SetDirty();
3493 }
3494
3495 return bCellStateChanged;
3496}
3497
3499 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3500{
3501 if (rCxt.meMode != URM_COPY)
3502 return false;
3503
3504 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3505 if ( pUndoCellPos )
3506 aUndoPos = *pUndoCellPos;
3507 ScAddress aOldPos( aPos );
3508
3509 if (rCxt.maRange.Contains(aPos))
3510 {
3511 // The cell is being moved or copied to a new position. I guess the
3512 // position has been updated prior to this call? Determine
3513 // its original position before the move which will be used to adjust
3514 // relative references later.
3515 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3516 }
3517
3518 // Check presence of any references or column row names.
3519 bool bHasRefs = pCode->HasReferences();
3520 bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3521 bHasRefs = bHasRefs || bHasColRowNames;
3522 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3523
3524 if (!bHasRefs && !bOnRefMove)
3525 // This formula cell contains no references, nor needs recalculating
3526 // on reference update. Bail out.
3527 return false;
3528
3529 std::unique_ptr<ScTokenArray> pOldCode;
3530 if (pUndoDoc)
3531 pOldCode = pCode->Clone();
3532
3533 if (bOnRefMove)
3534 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3535 bOnRefMove = (aPos != aOldPos);
3536
3537 bool bNeedDirty = bOnRefMove;
3538
3539 if (pUndoDoc && bOnRefMove)
3540 setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3541
3542 if (bCompile)
3543 {
3544 CompileTokenArray(); // no Listening
3545 bNeedDirty = true;
3546 }
3547
3548 if (bNeedDirty)
3549 { // Cut off references, invalid or similar?
3550 sc::AutoCalcSwitch aACSwitch(rDocument, false);
3551 SetDirty();
3552 }
3553
3554 return false;
3555}
3556
3558 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3559{
3560 if (rDocument.IsClipOrUndo())
3561 return false;
3562
3563 if (mxGroup && mxGroup->mpTopCell != this)
3564 {
3565 // This is not a top cell of a formula group. Don't update references.
3566
3567 switch (rCxt.meMode)
3568 {
3569 case URM_INSDEL:
3570 return UpdatePosOnShift(rCxt);
3571 default:
3572 ;
3573 }
3574 return false;
3575 }
3576
3577 switch (rCxt.meMode)
3578 {
3579 case URM_INSDEL:
3580 return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3581 case URM_MOVE:
3582 return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3583 case URM_COPY:
3584 return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3585 default:
3586 ;
3587 }
3588
3589 return false;
3590}
3591
3593{
3594 // Adjust tokens only when it's not grouped or grouped top cell.
3595 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3596 bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3598 {
3599 if (bPosChanged)
3600 aPos.IncTab(rCxt.mnSheets);
3601
3602 return;
3603 }
3604
3606 ScAddress aOldPos = aPos;
3607 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3608 if (bPosChanged)
3609 aPos.IncTab(rCxt.mnSheets);
3610
3611 if (!bAdjustCode)
3612 return;
3613
3615 if (aRes.mbNameModified)
3616 // Re-compile after new sheet(s) have been inserted.
3617 bCompile = true;
3618
3619 // no StartListeningTo because the new sheets have not been inserted yet.
3620}
3621
3623{
3624 // Adjust tokens only when it's not grouped or grouped top cell.
3625 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3626 bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3628 {
3629 if (bPosChanged)
3630 aPos.IncTab(-1*rCxt.mnSheets);
3631 return;
3632 }
3633
3635 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3636 ScAddress aOldPos = aPos;
3637 if (bPosChanged)
3638 aPos.IncTab(-1*rCxt.mnSheets);
3639
3640 if (!bAdjustCode)
3641 return;
3642
3644 if (aRes.mbNameModified)
3645 // Re-compile after sheet(s) have been deleted.
3646 bCompile = true;
3647}
3648
3650{
3651 // Adjust tokens only when it's not grouped or grouped top cell.
3652 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3653
3655 {
3656 aPos.SetTab(nTabNo);
3657 return;
3658 }
3659
3661 ScAddress aOldPos = aPos;
3662 // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3663 aPos.SetTab(nTabNo);
3664
3665 // no StartListeningTo because pTab[nTab] not yet correct!
3666
3667 if (!bAdjustCode)
3668 return;
3669
3671 if (aRes.mbNameModified)
3672 // Re-compile after sheet(s) have been deleted.
3673 bCompile = true;
3674}
3675
3677{
3678 if (rDocument.IsClipOrUndo())
3679 return;
3680
3681 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3682 if (!bAdjustCode)
3683 return;
3684
3687 while (p)
3688 {
3689 ScSingleRefData& rRef1 = *p->GetSingleRef();
3690 if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3691 rRef1.IncTab(1);
3692 if (p->GetType() == formula::svDoubleRef)
3693 {
3694 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3695 if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3696 rRef2.IncTab(1);
3697 }
3698 p = aIter.GetNextReferenceRPN();
3699 }
3700}
3701
3703{
3704 if (rDocument.IsClipOrUndo())
3705 return false;
3706
3707 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3708 if (!bAdjustCode)
3709 return false;
3710
3711 bool bRet = false;
3714 while (p)
3715 {
3716 ScSingleRefData& rRef1 = *p->GetSingleRef();
3717 if (!rRef1.IsTabRel())
3718 {
3719 if (nTable != rRef1.Tab())
3720 bRet = true;
3721 else if (nTable != aPos.Tab())
3722 rRef1.SetAbsTab(aPos.Tab());
3723 }
3724 if (p->GetType() == formula::svDoubleRef)
3725 {
3726 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3727 if (!rRef2.IsTabRel())
3728 {
3729 if(nTable != rRef2.Tab())
3730 bRet = true;
3731 else if (nTable != aPos.Tab())
3732 rRef2.SetAbsTab(aPos.Tab());
3733 }
3734 }
3735 p = aIter.GetNextReferenceRPN();
3736 }
3737 return bRet;
3738}
3739
3740void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3741{
3742 if ( bForceIfNameInUse && !bCompile )
3744 if ( bCompile )
3745 pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3747}
3748
3750{
3751 // References to or over filtered rows are not adjusted
3752 // analog to the normal (non-transposed) case
3753 SCCOLROW nTemp = rRef.Col();
3754 rRef.SetRelCol(rRef.Row());
3755 rRef.SetRelRow(nTemp);
3756}
3757
3758// Reference transposition is only called in Clipboard Document
3760{
3761 bool bFound = false;
3764 while ( ( t = aIter.GetNextReference() ) != nullptr )
3765 {
3766 ScSingleRefData& rRef1 = *t->GetSingleRef();
3767 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3768 {
3769 bool bDouble = (t->GetType() == formula::svDoubleRef);
3770 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3771 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3772 {
3774
3775 if ( bDouble )
3777
3778 bFound = true;
3779 }
3780 }
3781 }
3782
3783 if (bFound)
3784 bCompile = true;
3785}
3786
3787void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3788 ScDocument* pUndoDoc )
3789{
3791
3792 ScAddress aOldPos = aPos;
3793 bool bPosChanged = false; // Whether this cell has been moved
3794
3795 // Dest range is transposed
3796 ScRange aDestRange( rDest, ScAddress(
3797 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3798 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3799 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3800
3801 // cell within range
3802 if ( aDestRange.Contains( aOldPos ) )
3803 {
3804 // References of these cells were not changed by ScTokenArray::AdjustReferenceOnMove()
3805 // Count back Positions
3806 SCCOL nRelPosX = aOldPos.Col();
3807 SCROW nRelPosY = aOldPos.Row();
3808 SCTAB nRelPosZ = aOldPos.Tab();
3809 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, rDocument, aDestRange, rSource.aStart );
3810 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3811 bPosChanged = true;
3812 }
3813
3814 std::unique_ptr<ScTokenArray> pOld;
3815 if (pUndoDoc)
3816 pOld = pCode->Clone();
3817 bool bRefChanged = false;
3818
3821 while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3822 {
3823 if( t->GetOpCode() == ocName )
3824 {
3825 const ScRangeData* pName = rDocument.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
3826 if (pName && pName->IsModified())
3827 bRefChanged = true;
3828 }
3829 else if( t->GetType() != svIndex )
3830 {
3832 ScComplexRefData& rRef = aMod.Ref();
3833 ScRange aAbs = rRef.toAbs(rDocument, aOldPos);
3834 bool bMod = (ScRefUpdate::UpdateTranspose(rDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3835 if (bMod)
3836 {
3837 rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
3838 bRefChanged = true;
3839
3840 // Absolute sheet reference => set 3D flag.
3841 // More than one sheet referenced => has to have both 3D flags.
3842 // If end part has 3D flag => start part must have it too.
3843 // The same behavior as in ScTokenArray::AdjustReferenceOnMove() is used for 3D-Flags.
3844 rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
3845 rRef.Ref1.SetFlag3D(
3846 (rSource.aStart.Tab() != rDest.Tab() && !bPosChanged)
3847 || !rRef.Ref1.IsTabRel() || rRef.Ref2.IsFlag3D());
3848 }
3849 }
3850 }
3851
3852 if (bRefChanged)
3853 {
3854 if (pUndoDoc)
3855 {
3856 // Similar to setOldCodeToUndo(), but it cannot be used due to the check
3857 // pUndoDoc->GetCellType(aPos) == CELLTYPE_FORMULA
3858 ScFormulaCell* pFCell = new ScFormulaCell(
3859 *pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
3860
3861 pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3862 pUndoDoc->SetFormulaCell(aPos, pFCell);
3863 }
3864
3865 bCompile = true;
3866 CompileTokenArray(); // also call StartListeningTo
3867 SetDirty();
3868 }
3869 else
3870 StartListeningTo( rDocument ); // Listener as previous
3871}
3872
3873void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3874{
3876
3877 bool bRefChanged = false;
3878
3881
3882 while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3883 {
3884 if( t->GetOpCode() == ocName )
3885 {
3886 const ScRangeData* pName = rDocument.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
3887 if (pName && pName->IsModified())
3888 bRefChanged = true;
3889 }
3890 else if( t->GetType() != svIndex )
3891 {
3893 ScComplexRefData& rRef = aMod.Ref();
3894 ScRange aAbs = rRef.toAbs(rDocument, aPos);
3895 bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3896 if (bMod)
3897 {
3898 rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos);
3899 bRefChanged = true;
3900 }
3901 }
3902 }
3903
3904 if (bRefChanged)
3905 {
3906 bCompile = true;
3907 CompileTokenArray(); // Also call StartListeningTo
3908 SetDirty();
3909 }
3910 else
3911 StartListeningTo( rDocument ); // Listener as previous
3912}
3913
3914// See also ScDocument::FindRangeNamesReferencingSheet()
3915static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument& rDoc,
3916 int nRecursion)
3917{
3918 FormulaTokenArrayPlainIterator aIter(*pCode);
3919 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3920 {
3921 if (p->GetOpCode() == ocName)
3922 {
3923 sal_uInt16 nTokenIndex = p->GetIndex();
3924 SCTAB nTab = p->GetSheet();
3925 rIndexes.setUpdatedName( nTab, nTokenIndex);
3926
3927 if (nRecursion < 126) // whatever... 42*3
3928 {
3929 ScRangeData* pSubName = rDoc.FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3930 if (pSubName)
3931 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), rDoc, nRecursion+1);
3932 }
3933 }
3934 }
3935}
3936
3938{
3939 lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0);
3940}
3941
3943{
3944 bChanged = b;
3945}
3946
3947void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
3948{
3949 assert(!mxGroup); // Don't call this if it's shared.
3950 delete pCode;
3951 pCode = pNew.release(); // takes ownership.
3952}
3953
3955{
3956 bRunning = bVal;
3957}
3958
3960{
3962 for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3963 {
3964 OpCode eOp = p->GetOpCode();
3965 if ( eOp == ocDBArea || eOp == ocTableRef )
3966 {
3967 bCompile = true;
3968 CompileTokenArray(rCxt);
3969 SetDirty();
3970 break;
3971 }
3972 }
3973}
3974
3976{
3978 for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3979 {
3980 if ( p->GetOpCode() == ocColRowName )
3981 {
3982 bCompile = true;
3983 CompileTokenArray(rCxt);
3984 SetDirty();
3985 break;
3986 }
3987 }
3988}
3989
3994
3996{
3997 if (mxGroup)
3998 {
3999 // You can't create a new group if the cell is already a part of a group.
4000 // Is this a sign of some inconsistent or incorrect data structures? Or normal?
4001 SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
4002 return ScFormulaCellGroupRef();
4003 }
4004
4005 mxGroup.reset(new ScFormulaCellGroup);
4006 mxGroup->mpTopCell = this;
4007 mxGroup->mbInvariant = bInvariant;
4008 mxGroup->mnLength = nLen;
4009 mxGroup->mpCode = std::move(*pCode); // Move this to the shared location.
4010 delete pCode;
4011 pCode = &*mxGroup->mpCode;
4012 return mxGroup;
4013}
4014
4016{
4017 if (!xRef)
4018 {
4019 // Make this cell a non-grouped cell.
4020 if (mxGroup)
4021 pCode = mxGroup->mpCode->Clone().release();
4022
4023 mxGroup = xRef;
4024 return;
4025 }
4026
4027 // Group object has shared token array.
4028 if (!mxGroup)
4029 // Currently not shared. Delete the existing token array first.
4030 delete pCode;
4031
4032 mxGroup = xRef;
4033 pCode = &*mxGroup->mpCode;
4034 mxGroup->mnWeight = 0; // invalidate
4035}
4036
4038{
4039 // no Matrix formulae yet.
4041 return NotEqual;
4042
4043 // are these formulas at all similar ?
4044 if ( GetHash() != rOther.GetHash() )
4045 return NotEqual;
4046
4047 if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
4048 return NotEqual;
4049
4050 FormulaToken **pThis = pCode->GetCode();
4051 sal_uInt16 nThisLen = pCode->GetCodeLen();
4052 FormulaToken **pOther = rOther.pCode->GetCode();
4053 sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
4054
4055 if ( !pThis || !pOther )
4056 {
4057 // Error: no compiled code for cells !"
4058 return NotEqual;
4059 }
4060
4061 if ( nThisLen != nOtherLen )
4062 return NotEqual;
4063
4064 // No tokens can be an error cell so check error code, otherwise we could
4065 // end up with a series of equal error values instead of individual error
4066 // values. Also if for any reason different errors are set even if all
4067 // tokens are equal, the cells are not equal.
4068 if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
4069 return NotEqual;
4070
4071 bool bInvariant = true;
4072
4073 // check we are basically the same function
4074 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4075 {
4076 formula::FormulaToken *pThisTok = pThis[i];
4077 formula::FormulaToken *pOtherTok = pOther[i];
4078
4079 if ( pThisTok->GetType() != pOtherTok->GetType() ||
4080 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4081 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4082 {
4083 // Incompatible type, op-code or param counts.
4084 return NotEqual;
4085 }
4086
4087 switch (pThisTok->GetType())
4088 {
4089 case formula::svMatrix:
4092 // Ignoring matrix and external references for now.
4093 return NotEqual;
4094
4096 {
4097 // Single cell reference.
4098 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4099 if (rRef != *pOtherTok->GetSingleRef())
4100 return NotEqual;
4101
4102 if (rRef.IsRowRel())
4103 bInvariant = false;
4104 }
4105 break;
4107 {
4108 // Range reference.
4109 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4110 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4111 if (rRef1 != *pOtherTok->GetSingleRef())
4112 return NotEqual;
4113
4114 if (rRef2 != *pOtherTok->GetSingleRef2())
4115 return NotEqual;
4116
4117 if (rRef1.IsRowRel())
4118 bInvariant = false;
4119
4120 if (rRef2.IsRowRel())
4121 bInvariant = false;
4122 }
4123 break;
4124 case formula::svDouble:
4125 {
4126 if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4127 return NotEqual;
4128 }
4129 break;
4130 case formula::svString:
4131 {
4132 if(pThisTok->GetString() != pOtherTok->GetString())
4133 return NotEqual;
4134 }
4135 break;
4136 case formula::svIndex:
4137 {
4138 if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4139 return NotEqual;
4140 }
4141 break;
4142 case formula::svByte:
4143 {
4144 if(pThisTok->GetByte() != pOtherTok->GetByte())
4145 return NotEqual;
4146 }
4147 break;
4149 {
4150 if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4151 return NotEqual;
4152
4153 if (pThisTok->GetByte() != pOtherTok->GetByte())
4154 return NotEqual;
4155 }
4156 break;
4157 case formula::svError:
4158 {
4159 if (pThisTok->GetError() != pOtherTok->GetError())
4160 return NotEqual;
4161 }
4162 break;
4163 default:
4164 ;
4165 }
4166 }
4167
4168 // If still the same, check lexical names as different names may result in
4169 // identical RPN code.
4170
4171 pThis = pCode->GetArray();
4172 nThisLen = pCode->GetLen();
4173 pOther = rOther.pCode->GetArray();
4174 nOtherLen = rOther.pCode->GetLen();
4175
4176 if ( !pThis || !pOther )
4177 {
4178 // Error: no code for cells !"
4179 return NotEqual;
4180 }
4181
4182 if ( nThisLen != nOtherLen )
4183 return NotEqual;
4184
4185 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4186 {
4187 formula::FormulaToken *pThisTok = pThis[i];
4188 formula::FormulaToken *pOtherTok = pOther[i];
4189
4190 if ( pThisTok->GetType() != pOtherTok->GetType() ||
4191 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4192 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4193 {
4194 // Incompatible type, op-code or param counts.
4195 return NotEqual;
4196 }
4197
4198 switch (pThisTok->GetType())
4199 {
4200 // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4201 // resulting in identical RPN references that could lead to creating
4202 // a formula group from formulas that should not be merged into a group,
4203 // so check also the formula itself.
4205 {
4206 // Single cell reference.
4207 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4208 if (rRef != *pOtherTok->GetSingleRef())
4209 return NotEqual;
4210
4211 if (rRef.IsRowRel())
4212 bInvariant = false;
4213 }
4214 break;
4216 {
4217 // Range reference.
4218 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4219 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4220 if (rRef1 != *pOtherTok->GetSingleRef())
4221 return NotEqual;
4222
4223 if (rRef2 != *pOtherTok->GetSingleRef2())
4224 return NotEqual;
4225
4226 if (rRef1.IsRowRel())
4227 bInvariant = false;
4228
4229 if (rRef2.IsRowRel())
4230 bInvariant = false;
4231 }
4232 break;
4233 // All index tokens are names. Different categories already had
4234 // different OpCode values.
4235 case formula::svIndex:
4236 {
4237 if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4238 return NotEqual;
4239 switch (pThisTok->GetOpCode())
4240 {
4241 case ocTableRef:
4242 // nothing, sheet value assumed as -1, silence
4243 // ScTableRefToken::GetSheet() SAL_WARN about
4244 // unhandled
4245 ;
4246 break;
4247 default: // ocName, ocDBArea
4248 if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4249 return NotEqual;
4250 }
4251 }
4252 break;
4253 default:
4254 ;
4255 }
4256 }
4257
4258 return bInvariant ? EqualInvariant : EqualRelativeRef;
4259}
4260
4261namespace {
4262
4263// Split N into optimally equal-sized pieces, each not larger than K.
4264// Return value P is number of pieces. A returns the number of pieces
4265// one larger than N/P, 0..P-1.
4266
4267int splitup(int N, int K, int& A)
4268{
4269 assert(N > 0);
4270 assert(K > 0);
4271
4272 A = 0;
4273
4274 if (N <= K)
4275 return 1;
4276
4277 const int ideal_num_parts = N / K;
4278 if (ideal_num_parts * K == N)
4279 return ideal_num_parts;
4280
4281 const int num_parts = ideal_num_parts + 1;
4282 const int nominal_part_size = N / num_parts;
4283
4284 A = N - num_parts * nominal_part_size;
4285
4286 return num_parts;
4287}
4288
4289struct ScDependantsCalculator
4290{
4291 ScDocument& mrDoc;
4292 const ScTokenArray& mrCode;
4293 const ScFormulaCellGroupRef& mxGroup;
4294 const SCROW mnLen;
4295 const ScAddress& mrPos;
4296 const bool mFromFirstRow;
4297 const SCROW mnStartOffset;
4298 const SCROW mnEndOffset;
4299 const SCROW mnSpanLen;
4300
4301 ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
4302 const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
4303 mrDoc(rDoc),
4304 mrCode(rCode),
4305 mxGroup(rCell.GetCellGroup()),
4306 mnLen(mxGroup->mnLength),
4307 mrPos(rPos),
4308 // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
4309 // only from further rows. This data fetching could also lead to Interpret() calls, so
4310 // in OpenCL mode the formula in practice depends on those cells too.
4311 mFromFirstRow(fromFirstRow),
4312 mnStartOffset(nStartOffset),
4313 mnEndOffset(nEndOffset),
4314 mnSpanLen(nEndOffset - nStartOffset + 1)
4315 {
4316 }
4317
4318 // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4319 // (note already modified a bit, mFromFirstRow)
4320
4321 // I think what this function does is to check whether the relative row reference nRelRow points
4322 // to a row that is inside the range of rows covered by the formula group.
4323
4324 bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4325 {
4326 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4327 return false;
4328
4329 SCROW nEndRow = mrPos.Row() + mnLen - 1;
4330
4331 if (nRelRow <= 0)
4332 {
4333 SCROW nTest = nEndRow;
4334 nTest += nRelRow;
4335 if (nTest >= mrPos.Row())
4336 return true;
4337 }
4338 else
4339 {
4340 SCROW nTest = mrPos.Row(); // top row.
4341 nTest += nRelRow;
4342 if (nTest <= nEndRow)
4343 return true;
4344 // If pointing below the formula, it's always included if going from first row.
4345 if (mFromFirstRow)
4346 return true;
4347 }
4348
4349 return false;
4350 }
4351
4352 // FIXME: another copy-paste
4353
4354 // And this correspondingly checks whether an absolute row is inside the range of rows covered
4355 // by the formula group.
4356
4357 bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4358 {
4359 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4360 return false;
4361
4362 SCROW nEndRow = mrPos.Row() + mnLen - 1;
4363
4364 if (rRefPos.Row() < mrPos.Row())
4365 return false;
4366
4367 // If pointing below the formula, it's always included if going from first row.
4368 if (rRefPos.Row() > nEndRow && !mFromFirstRow)
4369 return false;
4370
4371 return true;
4372 }
4373
4374 // Checks if the doubleref engulfs all of formula group cells
4375 // Note : does not check if there is a partial overlap, that can be done by calling
4376 // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4377 bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4378 {
4379 if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4380 || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4381 {
4382 return false;
4383 }
4384
4385 SCROW nStartRow = mrPos.Row();
4386 SCROW nEndRow = nStartRow + mnLen - 1;
4387 SCROW nRefStartRow = rAbs.aStart.Row();
4388 SCROW nRefEndRow = rAbs.aEnd.Row();
4389
4390 if (bIsRef1RowRel && bIsRef2RowRel &&
4391 ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4392 ((nRefStartRow + mnLen - 1) <= nStartRow &&
4393 (nRefEndRow + mnLen - 1) >= nEndRow)))
4394 return true;
4395
4396 if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4397 (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4398 return true;
4399
4400 if (!bIsRef2RowRel &&
4401 nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4402 return true;
4403
4404 // If going from first row, the referenced range must be entirely above the formula,
4405 // otherwise the formula would be included.
4406 if (mFromFirstRow && nRefEndRow >= nStartRow)
4407 return true;
4408
4409 return false;
4410 }
4411
4412 // FIXME: another copy-paste
4413 SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4414 {
4415 SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4416 nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4417 if (nLastRow < (nRow + nRowLen - 1))
4418 {
4419 // This can end up negative! Was that the original intent, or
4420 // is it accidental? Was it not like that originally but the
4421 // surrounding conditions changed?
4422 nRowLen = nLastRow - nRow + 1;
4423 // Anyway, let's assume it doesn't make sense to return a
4424 // negative or zero value here.
4425 if (nRowLen <= 0)
4426 nRowLen = 1;
4427 }
4428 else if (nLastRow == 0)
4429 // Column is empty.
4430 nRowLen = 1;
4431
4432 return nRowLen;
4433 }
4434
4435 bool DoIt()
4436 {
4437 // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4438
4439 ScRangeList aRangeList;
4440
4441 // Self references should be checked by considering the entire formula-group not just the provided span.
4442 bool bHasSelfReferences = false;
4443 bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
4444
4445 FormulaToken** pRPNArray = mrCode.GetCode();
4446 sal_uInt16 nCodeLen = mrCode.GetCodeLen();
4447 for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
4448 {
4449 auto p = pRPNArray[nTokenIdx];
4450 if (!bInDocShellRecalc)
4451 {
4452 // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
4453 // of the result of the condition expression.
4454 // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
4455 // in the document. So lets disable threading and stop dependency evaluation if
4456 // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
4457 // for formulae with IF/IFS/SWITCH
4458 OpCode nOpCode = p->GetOpCode();
4459 if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
4460 return false;
4461 }
4462
4463 switch (p->GetType())
4464 {
4465 case svSingleRef:
4466 {
4467 ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4468 if( aRef.IsDeleted())
4469 return false;
4470 ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
4471
4472 if (!mrDoc.HasTable(aRefPos.Tab()))
4473 return false; // or true?
4474
4475 if (aRef.IsRowRel())
4476 {
4477 if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4478 {
4479 bHasSelfReferences = true;
4480 continue;
4481 }
4482
4483 // Trim data array length to actual data range.
4484 SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
4485
4486 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
4487 aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
4488 }
4489 else
4490 {
4491 if (isSelfReferenceAbsolute(aRefPos))
4492 {
4493 bHasSelfReferences = true;
4494 continue;
4495 }
4496
4497 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4498 }
4499 }
4500 break;
4501 case svDoubleRef:
4502 {
4503 ScComplexRefData aRef = *p->GetDoubleRef();
4504 if( aRef.IsDeleted())
4505 return false;
4506 ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
4507
4508 // Multiple sheet
4509 if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
4510 return false;
4511
4512 bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4513 // Check for self reference.
4514 if (bIsRef1RowRel)
4515 {
4516 if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4517 {
4518 bHasSelfReferences = true;
4519 continue;
4520 }
4521 }
4522 else if (isSelfReferenceAbsolute(aAbs.aStart))
4523 {
4524 bHasSelfReferences = true;
4525 continue;
4526 }
4527
4528 bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4529 if (bIsRef2RowRel)
4530 {
4531 if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4532 {
4533 bHasSelfReferences = true;
4534 continue;
4535 }
4536 }
4537 else if (isSelfReferenceAbsolute(aAbs.aEnd))
4538 {
4539 bHasSelfReferences = true;
4540 continue;
4541 }
4542
4543 if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4544 {
4545 bHasSelfReferences = true;
4546 continue;
4547 }
4548
4549 // The first row that will be referenced through the doubleref.
4550 SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
4551 // The last row that will be referenced through the doubleref.
4552 SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
4553 // Number of rows to be evaluated from nFirstRefRow.
4554 SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
4555 assert(nArrayLength > 0);
4556
4557 // Trim trailing empty rows.
4558 nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
4559
4560 aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
4561 aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
4562 }
4563 break;
4564 default:
4565 break;
4566 }
4567 }
4568
4569 // Compute dependencies irrespective of the presence of any self references.
4570 // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4571 // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4572 for (size_t i = 0; i < aRangeList.size(); ++i)
4573 {
4574 const ScRange & rRange = aRangeList[i];
4575 assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4576 for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4577 {
4578 SCROW nStartRow = rRange.aStart.Row();
4579 SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
4580 if( mFromFirstRow )
4581 { // include also all previous rows
4582 nLength += nStartRow;
4583 nStartRow = 0;
4584 }
4585 if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
4586 nLength, mxGroup))
4587 return false;
4588 }
4589 }
4590
4591 if (bHasSelfReferences)
4592 mxGroup->mbPartOfCycle = true;
4593
4594 return !bHasSelfReferences;
4595 }
4596};
4597
4598} // anonymous namespace
4599
4601{
4602 if (!mxGroup || !pCode)
4603 return false;
4604
4605 auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this);
4606 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4607
4608 if (mxGroup->mbPartOfCycle)
4609 {
4610 aScope.addMessage("This formula-group is part of a cycle");
4611 return false;
4612 }
4613
4614 if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4615 {
4616 static constexpr OUStringLiteral MESSAGE = u"group calc disabled";
4617 aScope.addMessage(MESSAGE);
4618 return false;
4619 }
4620
4621 // Use SC_FORCE_CALCULATION=opencl/threads to force calculation e.g. for unittests
4623 if (forceType == ForceCalculationCore
4624 || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
4625 && forceType != ForceCalculationOpenCL
4626 && forceType != ForceCalculationThreads))
4627 {
4628 mxGroup->meCalcState = sc::GroupCalcDisabled;
4629 aScope.addGroupSizeThresholdMessage(*this);
4630 return false;
4631 }
4632
4634 {
4635 mxGroup->meCalcState = sc::GroupCalcDisabled;
4636 aScope.addMessage("matrix skipped");
4637 return false;
4638 }
4639
4640 if( forceType != ForceCalculationNone )
4641 {
4642 // ScConditionEntry::Interpret() creates a temporary cell and interprets it
4643 // without it actually being in the document at the specified position.
4644 // That would confuse opencl/threading code, as they refer to the cell group
4645 // also using the position. This is normally not triggered (single cells
4646 // are normally not in a cell group), but if forced, check for this explicitly.
4647 if( rDocument.GetFormulaCell( aPos ) != this )
4648 {
4649 mxGroup->meCalcState = sc::GroupCalcDisabled;
4650 aScope.addMessage("cell not in document");
4651 return false;
4652 }
4653 }
4654
4655 // Get rid of -1's in offsets (defaults) or any invalid offsets.
4656 SCROW nMaxOffset = mxGroup->mnLength - 1;
4657 nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
4658 nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
4659
4660 if (nEndOffset < nStartOffset)
4661 {
4662 nStartOffset = 0;
4663 nEndOffset = nMaxOffset;
4664 }
4665
4666 if (nEndOffset == nStartOffset && forceType == ForceCalculationNone)
4667 return false; // Do not use threads for a single row.
4668
4669 // Guard against endless recursion of Interpret() calls, for this to work
4670 // ScFormulaCell::InterpretFormulaGroup() must never be called through
4671 // anything else than ScFormulaCell::Interpret(), same as
4672 // ScFormulaCell::InterpretTail()
4673 RecursionCounter aRecursionCounter( rRecursionHelper, this);
4674
4675 bool bDependencyComputed = false;
4676 bool bDependencyCheckFailed = false;
4677
4678 // Preference order: First try OpenCL, then threading.
4679 // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
4680 if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4681 return true;
4682
4683 if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
4684 return true;
4685
4686 return false;
4687}
4688
4690 SCROW nStartOffset, SCROW nEndOffset,
4691 bool bCalcDependencyOnly)
4692{
4693 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4694 // iterate over code in the formula ...
4695 // ensure all input is pre-calculated -
4696 // to avoid writing during the calculation
4697 if (bCalcDependencyOnly)
4698 {
4699 // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
4700 // "ScFormulaGroupCycleCheckGuard" for this formula-group.
4701 // (We can only reach here from a multi-group dependency evaluation attempt).
4702 // (These two have to be in pairs always for any given formula-group)
4703 ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4704 return aCalculator.DoIt();
4705 }
4706
4707 bool bOKToParallelize = false;
4708 {
4709 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4710 if (mxGroup->mbPartOfCycle)
4711 {
4712 mxGroup->meCalcState = sc::GroupCalcDisabled;
4713 rScope.addMessage("found circular formula-group dependencies");
4714 return false;
4715 }
4716
4717 ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4718 ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4719 bOKToParallelize = aCalculator.DoIt();
4720
4721 }
4722
4723 if (rRecursionHelper.IsInRecursionReturn())
4724 {
4725 mxGroup->meCalcState = sc::GroupCalcDisabled;
4726 rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4727 return false;
4728 }
4729
4730 if (mxGroup->mbPartOfCycle)
4731 {
4732 mxGroup->meCalcState = sc::GroupCalcDisabled;
4733 rScope.addMessage("found circular formula-group dependencies");
4734 return false;
4735 }
4736
4737 if (!rRecursionHelper.AreGroupsIndependent())
4738 {
4739 // This call resulted from a dependency calculation for a multigroup-threading attempt,
4740 // but found dependency among the groups.
4741 rScope.addMessage("multi-group-dependency failed");
4742 return false;
4743 }
4744
4745 if (!bOKToParallelize)
4746 {
4747 mxGroup->meCalcState = sc::GroupCalcDisabled;
4748 rScope.addMessage("could not do new dependencies calculation thing");
4749 return false;
4750 }
4751
4752 return true;
4753}
4754
4757 std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
4758{
4759 const SCROW nLen = xGroup->mnLength;
4760 const sal_Int32 nWt = xGroup->mnWeight;
4761 ScAddress aAddr(xGroup->mpTopCell->aPos);
4762
4763 SCCOL nColRet = aAddr.Col();
4764
4765 const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
4766 if (bLeft)
4767 --nColRet;
4768 else
4769 ++nColRet;
4770
4771 while (nColRet >= 0 && nColRet <= nMaxCol)
4772 {
4773 aAddr.SetCol(nColRet);
4774 const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
4775 if (!pCell)
4776 break;
4777
4778 if (!pCell->NeedsInterpret())
4779 break;
4780
4781 const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
4782 if (!xNGroup)
4783 break;
4784
4785 if (!pCell->GetCode()->IsEnabledForThreading())
4786 break;
4787
4788 if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
4789 break;
4790
4791 const SCROW nNLen = xNGroup->mnLength;
4792 const sal_Int32 nNWt = pCell->GetWeight();
4793 if (nNLen != nLen || nNWt != nWt)
4794 break;
4795
4796 rFGSet.insert(xNGroup.get());
4797 rFGMap[nColRet] = xNGroup->mpTopCell;
4798
4799 if (bLeft)
4800 --nColRet;
4801 else
4802 ++nColRet;
4803 }
4804
4805 if (bLeft)
4806 ++nColRet;
4807 else
4808 --nColRet;
4809
4810 return nColRet;
4811}
4812
4813// To be called only from InterpretFormulaGroup().
4815 bool& bDependencyComputed,
4816 bool& bDependencyCheckFailed,
4817 SCROW nStartOffset,
4818 SCROW nEndOffset)
4819{
4820 static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4821 if (!bDependencyCheckFailed && !bThreadingProhibited &&
4824 {
4825 if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset))
4826 {
4827 bDependencyComputed = true;
4828 bDependencyCheckFailed = true;
4829 return false;
4830 }
4831
4832 bDependencyComputed = true;
4833
4834 // Then do the threaded calculation
4835
4836 class Executor : public comphelper::ThreadTask
4837 {
4838 private:
4839 const unsigned mnThisThread;
4840 const unsigned mnThreadsTotal;
4841 ScDocument* mpDocument;
4843 const ScAddress& mrTopPos;
4844 SCCOL mnStartCol;
4845 SCCOL mnEndCol;
4846 SCROW mnStartOffset;
4847 SCROW mnEndOffset;
4848
4849 public:
4850 Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4851 unsigned nThisThread,
4852 unsigned nThreadsTotal,
4853 ScDocument* pDocument2,
4854 ScInterpreterContext* pContext,
4855 const ScAddress& rTopPos,
4856 SCCOL nStartCol,
4857 SCCOL nEndCol,
4858 SCROW nStartOff,
4859 SCROW nEndOff) :
4861 mnThisThread(nThisThread),
4862 mnThreadsTotal(nThreadsTotal),
4863 mpDocument(pDocument2),
4864 mpContext(pContext),
4865 mrTopPos(rTopPos),
4866 mnStartCol(nStartCol),
4867 mnEndCol(nEndCol),
4868 mnStartOffset(nStartOff),
4869 mnEndOffset(nEndOff)
4870 {
4871 }
4872
4873 virtual void doWork() override
4874 {
4875 ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
4876 mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
4877 mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
4878 }
4879
4880 };
4881
4883
4885 sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4886
4887 SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4888
4890 std::map<SCCOL, ScFormulaCell*> aFGMap;
4891 aFGSet.insert(mxGroup.get());
4892
4893 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4894 SCCOL nColStart = aPos.Col();
4895 SCCOL nColEnd = nColStart;
4896 if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc())
4897 {
4898 nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true);
4899 nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false);
4900 }
4901
4902 if (nColStart != nColEnd)
4903 {
4904 ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
4905 for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
4906 {
4907 if (nCurrCol == aPos.Col())
4908 continue;
4909
4910 bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true);
4911 if (!bFGOK || !aGuard.AreGroupsIndependent())
4912 {
4913 nColEnd = nColStart = aPos.Col();
4914 break;
4915 }
4916 }
4917 }
4918
4919 std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount);
4920 {
4923
4925
4926 // Start nThreadCount new threads
4927 std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
4928 ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter);
4929 ScInterpreterContext* context = nullptr;
4930
4931 for (int i = 0; i < nThreadCount; ++i)
4932 {
4933 context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4934 assert(!context->pInterpreter);
4935 aInterpreters[i].reset(new ScInterpreter(this, rDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true));
4936 context->pInterpreter = aInterpreters[i].get();
4938 rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, &rDocument, context, mxGroup->mpTopCell->aPos,
4939 nColStart, nColEnd, nStartOffset, nEndOffset));
4940 }
4941
4942 SAL_INFO("sc.threaded", "Waiting for threads to finish work");
4943 // Do not join the threads here. They will get joined in ScDocument destructor
4944 // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone).
4945 rThreadPool.waitUntilDone(aTag, false);
4946
4948
4949 for (int i = 0; i < nThreadCount; ++i)
4950 {
4951 context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4952 // This is intentionally done in this main thread in order to avoid locking.
4954 context->pInterpreter = nullptr;
4955 }
4956
4957 SAL_INFO("sc.threaded", "Done");
4958 }
4959
4960 ScAddress aStartPos(mxGroup->mpTopCell->aPos);
4961 SCROW nSpanLen = nEndOffset - nStartOffset + 1;
4962 aStartPos.SetRow(aStartPos.Row() + nStartOffset);
4963 // Reuse one of the previously allocated interpreter objects here.
4964 rDocument.HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen,
4965 aStartPos.Tab(), aInterpreters[0].get());
4966
4967 return true;
4968 }
4969
4970 return false;
4971}
4972
4973// To be called only from InterpretFormulaGroup().
4975 bool& bDependencyComputed,
4976 bool& bDependencyCheckFailed)
4977{
4978 bool bCanVectorize = pCode->IsEnabledForOpenCL();
4979 switch (pCode->GetVectorState())
4980 {
4983 break;
4984
4985 // Not good.
4987 aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
4988 break;
4990 aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
4991 break;
4993 aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
4994 break;
4997 default:
4998 aScope.addMessage("group calc disabled due to vector state (unknown)");
4999 return false;
5000 }
5001
5002 if (!bCanVectorize)
5003 return false;
5004
5006 {
5007 aScope.addMessage("opencl not enabled");
5008 return false;
5009 }
5010
5011 // TableOp does tricks with using a cell with different values, just bail out.
5013 return false;
5014
5015 if (bDependencyCheckFailed)
5016 return false;
5017
5018 if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1))
5019 {
5020 bDependencyComputed = true;
5021 bDependencyCheckFailed = true;
5022 return false;
5023 }
5024
5025 bDependencyComputed = true;
5026
5027 // TODO : Disable invariant formula group interpretation for now in order
5028 // to get implicit intersection to work.
5029 if (mxGroup->mbInvariant && false)
5031
5032 int nMaxGroupLength = INT_MAX;
5033
5034#ifdef _WIN32
5035 // Heuristic: Certain old low-end OpenCL implementations don't
5036 // work for us with too large group lengths. 1000 was determined
5037 // empirically to be a good compromise.
5038 if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
5039 nMaxGroupLength = 1000;
5040#endif
5041
5042 if (std::getenv("SC_MAX_GROUP_LENGTH"))
5043 nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
5044
5045 int nNumOnePlus;
5046 const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
5047
5048 int nOffset = 0;
5049 int nCurChunkSize;
5050 ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
5051 for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
5052 {
5053 nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
5054
5055 ScFormulaCellGroupRef xGroup;
5056
5057 if (nNumParts == 1)
5058 xGroup = mxGroup;
5059 else
5060 {
5061 // Ugly hack
5062 xGroup = new ScFormulaCellGroup();
5063 xGroup->mpTopCell = mxGroup->mpTopCell;
5064 xGroup->mpTopCell->aPos = aOrigPos;
5065 xGroup->mpTopCell->aPos.IncRow(nOffset);
5066 xGroup->mbInvariant = mxGroup->mbInvariant;
5067 xGroup->mnLength = nCurChunkSize;
5068 xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer
5069 }
5070
5071 ScTokenArray aCode(rDocument);
5072 ScGroupTokenConverter aConverter(aCode, rDocument, *this, xGroup->mpTopCell->aPos);
5073 // TODO avoid this extra compilation
5075 aComp.CompileTokenArray();
5076 if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
5077 {
5079 {
5080 SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
5081#ifdef DBG_UTIL
5082 for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
5083 {
5084 SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
5085 << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
5086 << "(" << int(opcode) << ")");
5087 }
5088#endif
5089 }
5090 else
5091 SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
5092
5093 mxGroup->meCalcState = sc::GroupCalcDisabled;
5094
5095 // Undo the hack above
5096 if (nNumParts > 1)
5097 {
5098 mxGroup->mpTopCell->aPos = aOrigPos;
5099 xGroup->mpTopCell = nullptr;
5100 mxGroup->mpCode = std::move(xGroup->mpCode);
5101 }
5102
5103 aScope.addMessage("group token conversion failed");
5104 return false;
5105 }
5106
5107 // The converted code does not have RPN tokens yet. The interpreter will
5108 // generate them.
5109 xGroup->meCalcState = mxGroup->meCalcState = sc::GroupCalcRunning;
5111
5112 if (pInterpreter == nullptr ||
5113 !pInterpreter->interpret(rDocument, xGroup->mpTopCell->aPos, xGroup, aCode))
5114 {
5115 SAL_INFO("sc.opencl", "interpreting group " << mxGroup->mpTopCell->aPos
5116 << " (state " << static_cast<int>(mxGroup->meCalcState) << ") failed, disabling");
5117 mxGroup->meCalcState = sc::GroupCalcDisabled;
5118
5119 // Undo the hack above
5120 if (nNumParts > 1)
5121 {
5122 mxGroup->mpTopCell->aPos = aOrigPos;
5123 xGroup->mpTopCell = nullptr;
5124 mxGroup->mpCode = std::move(xGroup->mpCode);
5125 }
5126
5127 aScope.addMessage("group interpretation unsuccessful");
5128 return false;
5129 }
5130
5131 aScope.setCalcComplete();
5132
5133 if (nNumParts > 1)
5134 {
5135 xGroup->mpTopCell = nullptr;
5136 mxGroup->mpCode = std::move(xGroup->mpCode);
5137 }
5138 }
5139
5140 if (nNumParts > 1)
5141 mxGroup->mpTopCell->aPos = aOrigPos;
5142 mxGroup->meCalcState = sc::GroupCalcEnabled;
5143 return true;
5144}
5145
5147{
5149 {
5150 // An invariant group should only have absolute row references, and no
5151 // external references are allowed.
5152
5153 ScTokenArray aCode(rDocument);
5155 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
5156 {
5157 switch (p->GetType())
5158 {
5159 case svSingleRef:
5160 {
5161 ScSingleRefData aRef = *p->GetSingleRef();
5162 ScAddress aRefPos = aRef.toAbs(rDocument, aPos);
5164 if (!pNewToken)
5165 return false;
5166
5167 aCode.AddToken(*pNewToken);
5168 }
5169 break;
5170 case svDoubleRef:
5171 {
5172 ScComplexRefData aRef = *p->GetDoubleRef();
5173 ScRange aRefRange = aRef.toAbs(rDocument, aPos);
5175 if (!pNewToken)
5176 return false;
5177
5178 aCode.AddToken(*pNewToken);
5179 }
5180 break;
5181 default:
5182 aCode.AddToken(*p);
5183 }
5184 }
5185
5187 aComp.CompileTokenArray(); // Create RPN token array.
5188 ScInterpreter aInterpreter(this, rDocument, rDocument.GetNonThreadedContext(), aPos, aCode);
5189 aInterpreter.Interpret();
5190 aResult.SetToken(aInterpreter.GetResultToken().get());
5191 }
5192 else
5193 {
5194 // Formula contains no references.
5196 aInterpreter.Interpret();
5197 aResult.SetToken(aInterpreter.GetResultToken().get());
5198 }
5199
5200 for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ )
5201 {
5202 ScAddress aTmpPos = aPos;
5203 aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i);
5204 ScFormulaCell* pCell = rDocument.GetFormulaCell(aTmpPos);
5205 if (!pCell)
5206 {
5207 SAL_WARN("sc.core.formulacell", "GetFormulaCell not found");
5208 continue;
5209 }
5210
5211 // FIXME: this set of horrors is unclear to me ... certainly
5212 // the above GetCell is profoundly nasty & slow ...
5213 // Ensure the cell truly has a result:
5214 pCell->aResult = aResult;
5215 pCell->ResetDirty();
5216 pCell->SetChanged(true);
5217 }
5218
5219 return true;
5220}
5221
5222namespace {
5223
5224void startListeningArea(
5225 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
5226{
5227 const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
5228 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
5229 ScAddress aCell1 = rRef1.toAbs(rDoc, rPos);
5230 ScAddress aCell2 = rRef2.toAbs(rDoc, rPos);
5231 if (!(aCell1.IsValid() && aCell2.IsValid()))
5232 return;
5233
5234 if (rToken.GetOpCode() == ocColRowNameAuto)
5235 { // automagically
5236 if ( rRef1.IsColRel() )
5237 { // ColName
5238 aCell2.SetRow(rDoc.MaxRow());
5239 }
5240 else
5241 { // RowName
5242 aCell2.SetCol(rDoc.MaxCol());
5243 }
5244 }
5245 rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell);
5246}
5247
5248}
5249
5251{
5252 if (mxGroup)
5253 mxGroup->endAllGroupListening(rDoc);
5254
5255 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
5256 return;
5257
5258 rDoc.SetDetectiveDirty(true); // It has changed something
5259
5260 ScTokenArray* pArr = GetCode();
5261 if( pArr->IsRecalcModeAlways() )
5262 {
5263 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
5264 SetNeedsListening( false);
5265 return;
5266 }
5267
5270 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5271 {
5272 switch (t->GetType())
5273 {
5274 case svSingleRef:
5275 {
5276 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aPos);
5277 if (aCell.IsValid())
5278 rDoc.StartListeningCell(aCell, this);
5279 }
5280 break;
5281 case svDoubleRef:
5282 startListeningArea(this, rDoc, aPos, *t);
5283 break;
5284 default:
5285 ; // nothing
5286 }
5287 }
5288 SetNeedsListening( false);
5289}
5290
5292{
5293 ScDocument& rDoc = rCxt.getDoc();
5294
5295 if (mxGroup)
5296 mxGroup->endAllGroupListening(rDoc);
5297
5298 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
5299 return;
5300
5301 rDoc.SetDetectiveDirty(true); // It has changed something
5302
5303 ScTokenArray* pArr = GetCode();
5304 if( pArr->IsRecalcModeAlways() )
5305 {
5306 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
5307 SetNeedsListening( false);
5308 return;
5309 }
5310
5313 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5314 {
5315 switch (t->GetType())
5316 {
5317 case svSingleRef:
5318 {
5319 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aPos);
5320 if (aCell.IsValid())
5321 rDoc.StartListeningCell(rCxt, aCell, *this);
5322 }
5323 break;
5324 case svDoubleRef:
5325 startListeningArea(this, rDoc, aPos, *t);
5326 break;
5327 default:
5328 ; // nothing
5329 }
5330 }
5331 SetNeedsListening( false);
5332}
5333
5334namespace {
5335
5336void endListeningArea(
5337 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken)
5338{
5339 const ScSingleRefData& rRef1 = *rToken.GetSingleRef();
5340 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2();
5341 ScAddress aCell1 = rRef1.toAbs(rDoc, rPos);
5342 ScAddress aCell2 = rRef2.toAbs(rDoc, rPos);
5343 if (!(aCell1.IsValid() && aCell2.IsValid()))
5344 return;
5345
5346 if (rToken.GetOpCode() == ocColRowNameAuto)
5347 { // automagically
5348 if ( rRef1.IsColRel() )
5349 { // ColName
5350 aCell2.SetRow(rDoc.MaxRow());
5351 }
5352 else
5353 { // RowName
5354 aCell2.SetCol(rDoc.MaxCol());
5355 }
5356 }
5357
5358 rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell);
5359}
5360
5361}
5362
5364 ScAddress aCellPos )
5365{
5366 if (mxGroup)
5367 mxGroup->endAllGroupListening(rDoc);
5368
5369 if (rDoc.IsClipOrUndo() || IsInChangeTrack())
5370 return;
5371
5372 if (!HasBroadcaster())
5373 return;
5374
5375 rDoc.SetDetectiveDirty(true); // It has changed something
5376
5377 if ( GetCode()->IsRecalcModeAlways() )
5378 {
5379 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
5380 return;
5381 }
5382
5383 if (!pArr)
5384 {
5385 pArr = GetCode();
5386 aCellPos = aPos;
5387 }
5390 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5391 {
5392 switch (t->GetType())
5393 {
5394 case svSingleRef:
5395 {
5396 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aCellPos);
5397 if (aCell.IsValid())
5398 rDoc.EndListeningCell(aCell, this);
5399 }
5400 break;
5401 case svDoubleRef:
5402 endListeningArea(this, rDoc, aCellPos, *t);
5403 break;
5404 default:
5405 ; // nothing
5406 }
5407 }
5408}
5409
5411{
5412 if (mxGroup)
5413 mxGroup->endAllGroupListening(rCxt.getDoc());
5414
5415 if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
5416 return;
5417
5418 if (!HasBroadcaster())
5419 return;
5420
5421 ScDocument& rDoc = rCxt.getDoc();
5422 rDoc.SetDetectiveDirty(true); // It has changed something
5423
5424 ScTokenArray* pArr = rCxt.getOldCode();
5425 ScAddress aCellPos = rCxt.getOldPosition(aPos);
5426 if (!pArr)
5427 pArr = pCode;
5428
5429 if (pArr->IsRecalcModeAlways())
5430 {
5431 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
5432 return;
5433 }
5434
5437 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
5438 {
5439 switch (t->GetType())
5440 {
5441 case svSingleRef:
5442 {
5443 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aCellPos);
5444 if (aCell.IsValid())
5445 rDoc.EndListeningCell(rCxt, aCell, *this);
5446 }
5447 break;
5448 case svDoubleRef:
5449 endListeningArea(this, rDoc, aCellPos, *t);
5450 break;
5451 default:
5452 ; // nothing
5453 }
5454 }
5455}
5456
5458{
5459 return bool(mxGroup);
5460}
5461
5463{
5464 if (!mxGroup)
5465 return false;
5466
5467 return mxGroup->mpTopCell == this;
5468}
5469
5471{
5472 return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1;
5473}
5474
5476{
5477 return mxGroup ? mxGroup->mnLength : 0;
5478}
5479
5481{
5482 if (!mxGroup)
5483 return 1;
5484
5485 if (mxGroup->mnWeight > 0)
5486 return mxGroup->mnWeight;
5487
5488 double nSharedCodeWeight = GetSharedCode()->GetWeight();
5489 double nResult = nSharedCodeWeight * GetSharedLength();
5490 if (nResult < SAL_MAX_INT32)
5491 mxGroup->mnWeight = nResult;
5492 else
5493 mxGroup->mnWeight = SAL_MAX_INT32;
5494
5495 return mxGroup->mnWeight;
5496}
5497
5499{
5500 return mxGroup ? &*mxGroup->mpCode : nullptr;
5501}
5502
5504{
5505 return mxGroup ? &*mxGroup->mpCode : nullptr;
5506}
5507
5509{
5510 if (!mxGroup)
5511 // Not a shared formula cell.
5512 return;
5513
5514 pCode = &*mxGroup->mpCode;
5515}
5516
5517#if DUMP_COLUMN_STORAGE
5518
5519void ScFormulaCell::Dump() const
5520{
5521 cout << "-- formula cell (" << aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDocument) << ")" << endl;
5522 cout << " * shared: " << (mxGroup ? "true" : "false") << endl;
5523 if (mxGroup)
5524 {
5525 cout << " * shared length: " << mxGroup->mnLength << endl;
5526 cout << " * shared calc state: " << mxGroup->meCalcState << endl;
5527 }
5528
5530 cout << " * code: " << pCode->CreateString(aCxt, aPos) << endl;
5531
5532 FormulaError nErrCode = pCode->GetCodeError();
5533 cout << " * code error: ";
5534 if (nErrCode == FormulaError::NONE)
5535 cout << "(none)";
5536 else
5537 {
5538 OUString aStr = ScGlobal::GetErrorString(nErrCode);
5539 cout << " * code error: " << aStr << " (" << int(nErrCode) << ")";
5540 }
5541 cout << endl;
5542
5543 cout << " * result: ";
5545 switch (aRV.meType)
5546 {
5548 cout << aRV.mfValue << " (value)";
5549 break;
5551 cout << aRV.maString.getString() << " (string)";
5552 break;
5554 cout << ScGlobal::GetErrorString(aRV.mnError) << " (error: " << int(aRV.mnError) << ")";
5555 break;
5557 cout << "(invalid)";
5558 break;
5559 default:
5560 ;
5561 }
5562 cout << endl;
5563}
5564
5565#endif
5566
5567/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 mnWeight
XPropertyListType t
const char * pName
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
#define BCA_LISTEN_ALWAYS
Definition: address.hxx:945
triangulator::B2DTriangleVector maResult
ForceCalculationType
Definition: calcconfig.hxx:33
@ ForceCalculationThreads
Definition: calcconfig.hxx:37
@ ForceCalculationOpenCL
Definition: calcconfig.hxx:36
@ ForceCalculationCore
Definition: calcconfig.hxx:35
@ ForceCalculationNone
Definition: calcconfig.hxx:34
void IncTab(SCTAB nDelta=1)
Definition: address.hxx:320
@ UNINITIALIZED
Definition: address.hxx:220
SCTAB Tab() const
Definition: address.hxx:283
void Set(SCCOL nCol, SCROW nRow, SCTAB nTab)
Definition: address.hxx:403
void SetCol(SCCOL nColP)
Definition: address.hxx:291
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2074
SC_DLLPUBLIC bool Move(SCCOL nDeltaX, SCROW nDeltaY, SCTAB nDeltaZ, ScAddress &rErrorPos, const ScDocument &rDoc)
Definition: address.cxx:2280
void IncCol(SCCOL nDelta=1)
Definition: address.hxx:316
bool IsValid() const
Definition: address.hxx:305
SCROW Row() const
Definition: address.hxx:274
void SetRow(SCROW nRowP)
Definition: address.hxx:287
void SetTab(SCTAB nTabP)
Definition: address.hxx:295
void IncRow(SCROW nDelta=1)
Definition: address.hxx:312
SCCOL Col() const
Definition: address.hxx:279
bool IsInDeleteUndo() const
Definition: chgtrack.hxx:967
bool HasUnhandledPossibleImplicitIntersections() const
Definition: compiler.hxx:508
void CreateStringFromXMLTokenArray(OUString &rFormula, OUString &rFormulaNmsp)
Definition: compiler.cxx:4658
const std::set< OpCode > & UnhandledPossibleImplicitIntersectionsOpCodes()
Definition: compiler.hxx:510
std::unique_ptr< ScTokenArray > CompileString(const OUString &rFormula)
Tokenize formula expression string into an array of tokens.
Definition: compiler.cxx:4691
Stores global named database ranges.
Definition: dbdata.hxx:243
bool insert(std::unique_ptr< ScDBData > p)
Takes ownership of p and attempts to insert it into the collection.
Definition: dbdata.cxx:1240
ScDBData * findByIndex(sal_uInt16 nIndex)
Definition: dbdata.cxx:1214
ScDBData * findByUpperName(const OUString &rName)
Definition: dbdata.cxx:1221
NamedDBs & getNamedDBs()
Definition: dbdata.hxx:324
const OUString & GetUpperName() const
Definition: dbdata.hxx:128
sal_uInt16 GetIndex() const
Definition: dbdata.hxx:141
sal_uInt16 GetIterCount() const
Definition: docoptio.hxx:61
bool IsIter() const
Definition: docoptio.hxx:59
bool IsCalcAsShown() const
Definition: docoptio.hxx:81
double GetIterEps() const
Definition: docoptio.hxx:63
bool IsInFormulaTree(const ScFormulaCell *pCell) const
SC_DLLPUBLIC SCCOL GetAllocatedColumnsCount(SCTAB nTab) const
Definition: documen3.cxx:2139
void TrackFormulas(SfxHintId nHintId=SfxHintId::ScDataChanged)
Definition: documen7.cxx:524
void SetThreadedGroupCalcInProgress(bool set)
Definition: document.hxx:629
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:898
bool IsUndo() const
Definition: document.hxx:1593
SC_DLLPUBLIC sal_uInt32 GetNumberFormat(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:3640
bool IsInFormulaTrack(const ScFormulaCell *pCell) const
SC_DLLPUBLIC ScFormulaCell * SetFormulaCell(const ScAddress &rPos, ScFormulaCell *pCell)
Set formula cell, and transfer its ownership to the document.
Definition: documen2.cxx:1149
bool IsInInterpreterTableOp() const
Definition: document.hxx:2439
static sal_uInt16 GetSrcVersion()
Definition: document.hxx:2137
bool IsInDocShellRecalc() const
Definition: document.hxx:2632
sal_uInt64 GetFormulaCodeInTree() const
Definition: document.hxx:2410
void SetDetectiveDirty(bool bSet)
Definition: document.hxx:2211
ScRangePairList * GetRowNameRanges()
Definition: document.hxx:820
void EndListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: documen7.cxx:61
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:892
SC_DLLPUBLIC const ScValidationData * GetValidationEntry(sal_uInt32 nIndex) const
Definition: documen4.cxx:873
bool IsClipboard() const
Definition: document.hxx:1594
HardRecalcState GetHardRecalcState() const
Definition: document.hxx:2402
SC_DLLPUBLIC ScMacroManager * GetMacroManager()
Definition: documen8.cxx:377
SC_DLLPUBLIC ScDocumentPool * GetPool()
Definition: document.cxx:6050
bool ValidAddress(const ScAddress &rAddress) const
Definition: document.hxx:904
void DecXMLImportedFormulaCount(sal_uInt64 nVal)
Definition: document.hxx:2465
ScRangeData * FindRangeNameBySheetAndIndex(SCTAB nTab, sal_uInt16 nIndex) const
Find a named expression / range name in either global or a local scope.
Definition: documen3.cxx:269
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:893
void RemoveFromFormulaTree(ScFormulaCell *pCell)
Definition: documen7.cxx:281
SC_DLLPUBLIC void CheckLinkFormulaNeedingCheck(const ScTokenArray &rCode)
Check token array and set link check if ocDde/ocWebservice is contained.
Definition: documen8.cxx:1149
bool IsInDdeLinkUpdate() const
Definition: document.hxx:1045
void MergeContextBackIntoNonThreadedContext(ScInterpreterContext &threadedContext, int threadNumber)
Definition: document.cxx:6915
void AddTableOpFormulaCell(ScFormulaCell *)
Definition: document.cxx:3919
SC_DLLPUBLIC double RoundValueAsShown(double fVal, sal_uInt32 nFormat, const ScInterpreterContext *pContext=nullptr) const
Definition: documen4.cxx:627
SC_DLLPUBLIC SCROW GetLastDataRow(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow) const
Return the last non-empty row position in given columns that's no greater than the initial last row p...
Definition: document.cxx:1063
ScInterpreterContext & GetNonThreadedContext() const
Definition: document.hxx:617
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:625
bool IsInLayoutStrings() const
Definition: document.hxx:2635
void SetStreamValid(SCTAB nTab, bool bSet, bool bIgnoreLock=false)
Definition: document.cxx:904
bool GetNoListening() const
Definition: document.hxx:2230
void AddSubTotalCell(ScFormulaCell *pCell)
Definition: document.cxx:6386
ScRecursionHelper & GetRecursionHelper()
Definition: document.cxx:6893
SC_DLLPUBLIC bool GetAutoCalc() const
Definition: document.hxx:1413
SC_DLLPUBLIC void SetNumberFormat(const ScAddress &rPos, sal_uInt32 nNumberFormat)
Definition: document.cxx:3682
ScRangePairList * GetColNameRanges()
Definition: document.hxx:819
bool IsStreamValid(SCTAB nTab) const
Definition: document.cxx:897
void RemoveFromFormulaTrack(ScFormulaCell *pCell)
Definition: documen7.cxx:469
SC_DLLPUBLIC formula::FormulaGrammar::Grammar GetGrammar() const
Definition: document.hxx:1010
void MarkUsedExternalReferences()
Definition: documen3.cxx:639
void StartListeningArea(const ScRange &rRange, bool bGroupListening, SvtListener *pListener)
Definition: documen7.cxx:35
void EndListeningCell(const ScAddress &rAddress, SvtListener *pListener)
Definition: documen7.cxx:230
bool IsClipOrUndo() const
Definition: document.hxx:1592
SC_DLLPUBLIC ScDBCollection * GetDBCollection() const
Definition: document.hxx:827
sal_uInt64 GetXMLImportedFormulaCount() const
Definition: document.hxx:2459
void SetupContextFromNonThreadedContext(ScInterpreterContext &threadedContext, int threadNumber)
Definition: document.cxx:6909
bool IsInsertingFromOtherDoc() const
Definition: document.hxx:2224
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:601
void AppendToFormulaTrack(ScFormulaCell *pCell)
Definition: documen7.cxx:453
void RemoveSubTotalCell(ScFormulaCell *pCell)
Definition: document.cxx:6391
void HandleStuffAfterParallelCalculation(SCCOL nColStart, SCCOL nColEnd, SCROW nRow, size_t nLen, SCTAB nTab, ScInterpreter *pInterpreter)
Definition: documen8.cxx:431
bool IsThreadedGroupCalcInProgress() const
Definition: document.hxx:630
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:467
bool HasExternalRefManager() const
Definition: document.hxx:1028
bool IsInDtorClear() const
Definition: document.hxx:2455
bool HandleRefArrayForParallelism(const ScAddress &rPos, SCROW nLength, const ScFormulaCellGroupRef &mxGroup)
Definition: document.cxx:1788
SC_DLLPUBLIC const ScFormulaCell * GetFormulaCell(const ScAddress &rPos) const
Definition: document.cxx:3714
void SetDBCollection(std::unique_ptr< ScDBCollection > pNewDBCollection, bool bRemoveAutoFilter=false)
Definition: documen3.cxx:275
ScChangeTrack * GetChangeTrack() const
Definition: document.hxx:2494
SC_DLLPUBLIC CellType GetCellType(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:3736
SC_DLLPUBLIC bool HasTable(SCTAB nTab) const
Definition: document.cxx:2502
bool CopyAdjustRangeName(SCTAB &rSheet, sal_uInt16 &rIndex, ScRangeData *&rpRangeData, ScDocument &rNewDoc, const ScAddress &rNewPos, const ScAddress &rOldPos, const bool bGlobalNamesToLocal, const bool bUsedByFormula) const
If necessary (name references sheet rOldPos.Tab()) copy and adjust named expression/range from sheet-...
Definition: document10.cxx:801
void IncInterpretLevel()
Definition: document.hxx:2414
bool IsClipboardSource() const
Definition: document.cxx:2577
void PutInFormulaTree(ScFormulaCell *pCell)
Definition: documen7.cxx:265
SC_DLLPUBLIC const SfxPoolItem * GetAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich) const
Definition: document.cxx:4684
formula::FormulaTokenRef ResolveStaticReference(const ScAddress &rPos)
Definition: document.cxx:1745
SC_DLLPUBLIC const ScDocOptions & GetDocOptions() const
Definition: documen3.cxx:1936
void StartListeningCell(const ScAddress &rAddress, SvtListener *pListener)
Definition: documen7.cxx:221
void CalculateInColumnInThread(ScInterpreterContext &rContext, const ScRange &rCalcRange, unsigned nThisThread, unsigned nThreadsTotal)
Definition: documen8.cxx:411
bool IsImportingXML() const
Definition: document.hxx:2227
void DecInterpretLevel()
Definition: document.hxx:2420
static std::unique_ptr< EditTextObject > CreateURLObjectFromURL(ScDocument &rDoc, const OUString &rURL, const OUString &rText)
Definition: editutil.cxx:144
void insertRefCellFromTemplate(ScFormulaCell *pTemplateCell, ScFormulaCell *pCell)
Add a cell to reference the same files as the template cell.
void removeRefCell(ScFormulaCell *pCell)
Stop tracking a specific formula cell.
void SetInChangeTrack(bool bVal)
bool NeedsInterpret() const
bool IsInChangeTrack() const
void SyncSharedCode()
bool GetErrorOrValue(FormulaError &rErr, double &rVal)
size_t GetHash() const
void GetMatColsRows(SCCOL &nCols, SCROW &nRows) const
bool IsShared() const
SCROW GetSharedLength() const
ScTokenArray * pCode
ScMatrixMode GetMatrixFlag() const
void SetErrCode(FormulaError n)
void SetResultDouble(double n)
For import only: set a double result.
void CompileColRowNameFormula(sc::CompileFormulaContext &rCxt)
void SetNeedNumberFormat(bool bVal)
ScFormulaCellGroupRef mxGroup
void SetCellGroup(const ScFormulaCellGroupRef &xRef)
void Dump() const
void UpdateTranspose(const ScRange &rSource, const ScAddress &rDest, ScDocument *pUndoDoc)
bool mbAllowNumberFormatChange
void CompileXML(sc::CompileFormulaContext &rCxt, ScProgress &rProgress)
const ScFormulaCellGroupRef & GetCellGroup() const
SCROW GetSharedTopRow() const
ScFormulaCell(const ScFormulaCell &)=delete
virtual void Query(SvtListener::QueryBase &rQuery) const override
void SetNeedsListening(bool bVar)
void CompileDBFormula(sc::CompileFormulaContext &rCxt)
void HandleStuffAfterParallelCalculation(ScInterpreter *pInterpreter)
void GetURLResult(OUString &rURL, OUString &rCellText)
sc::FormulaResultValue GetResult()
bool IsEmptyDisplayedAsString()
bool IsValueNoError()
ScDocument & rDocument
bool InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope &aScope, bool &bDependencyComputed, bool &bDependencyCheckFailed, SCROW nStartOffset, SCROW nEndOffset)
void SetResultMatrix(SCCOL nCols, SCROW nRows, const ScConstMatrixRef &pMat, const formula::FormulaToken *pUL)
ScFormulaResult aResult
void SetCompile(bool bVal)
bool InterpretInvariantFormulaGroup()
RelNameRef HasRelNameReference() const
ScMatrixMode cMatrixFlag
bool IsSharedTop() const
sc::MatrixEdge GetMatrixEdge(const ScDocument &rDoc, ScAddress &rOrgPos) const
bool CheckComputeDependencies(sc::FormulaLogger::GroupScope &rScope, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset, bool bCalcDependencyOnly=false)
void SetRunning(bool bVal)
const svl::SharedString & GetResultString() const
void UpdateCompile(bool bForceIfNameInUse)
double GetValue()
void UpdateDeleteTab(const sc::RefUpdateDeleteTabContext &rCxt)
void UpdateInsertTab(const sc::RefUpdateInsertTabContext &rCxt)
void GetResultDimensions(SCSIZE &rCols, SCSIZE &rRows)
bool InterpretFormulaGroup(SCROW nStartOffset=-1, SCROW nEndOffset=-1)
void SetPreviousTrack(ScFormulaCell *pF)
bool HasOneReference(ScRange &r) const
bool UpdateReference(const sc::RefUpdateContext &rCxt, ScDocument *pUndoDoc=nullptr, const ScAddress *pUndoCellPos=nullptr)
void CalcAfterLoad(sc::CompileFormulaContext &rCxt, bool bStartListening)
ScFormulaCell * Clone() const
SvNumFormatType nFormatType
virtual void Notify(const SfxHint &rHint) override
OUString GetHybridFormula() const
void SetChanged(bool b)
bool GetMatrixOrigin(const ScDocument &rDoc, ScAddress &rPos) const
bool mbNeedsNumberFormat
void AddRecalcMode(ScRecalcMode)
sal_Int32 GetWeight() const
const svl::SharedString & GetString()
double GetRawValue() const
Get a numeric value without potentially triggering re-calculation.
void SetTableOpDirty()
bool IsHyperLinkCell() const
bool TestTabRefAbs(SCTAB nTable)
@ SCITP_CLOSE_ITERATION_CIRCLE
void SetCode(std::unique_ptr< ScTokenArray > pNew)
void TransposeReference()
bool IsIterCell() const
void SetNext(ScFormulaCell *pF)
FormulaError GetErrCode()
ScFormulaCell * pNext
bool IsDirtyOrInTableOpDirty() const
void UpdateInsertTabAbs(SCTAB nTable)
void EndListeningTo(ScDocument &rDoc, ScTokenArray *pArr=nullptr, ScAddress aPos=ScAddress())
void ResetTableOpDirtyVar()
const ScMatrix * GetMatrix()
void StartListeningTo(ScDocument &rDoc)
const svl::SharedString & GetRawString() const
Get a string value without potentially triggering re-calculation.
bool UpdateReferenceOnShift(const sc::RefUpdateContext &rCxt, ScDocument *pUndoDoc, const ScAddress *pUndoCellPos)
Update reference in response to cell insertion or deletion.
bool HasRefListExpressibleAsOneReference(ScRange &rRange) const
void SetDirtyAfterLoad()
formula::FormulaGrammar::Grammar eTempGrammar
sal_uInt16 nSeenInIteration
ScTokenArray * GetSharedCode()
void UpdateGrow(const ScRange &rArea, SCCOL nGrowX, SCROW nGrowY)
void MaybeInterpret()
bool HasHybridStringResult() const
FormulaError GetRawError() const
std::unique_ptr< EditTextObject > CreateURLObject()
bool MarkUsedExternalReferences()
void SetPrevious(ScFormulaCell *pF)
void SetDirty(bool bDirtyFlag=true)
void FindRangeNamesInUse(sc::UpdatedRangeNames &rIndexes) const
ScFormulaCellGroupRef CreateCellGroup(SCROW nLen, bool bInvariant)
Turn a non-grouped cell into the top of a grouped cell.
void SetMatColsRows(SCCOL nCols, SCROW nRows)
void SetResultToken(const formula::FormulaToken *pToken)
ScFormulaCell * pNextTrack
void InterpretTail(ScInterpreterContext &, ScInterpretTailParameter)
bool UpdatePosOnShift(const sc::RefUpdateContext &rCxt)
Shift the position of formula cell as part of reference update.
bool UpdateReferenceOnCopy(const sc::RefUpdateContext &rCxt, ScDocument *pUndoDoc, const ScAddress *pUndoCellPos)
Update reference in response to cell copy-n-paste.
void Compile(const OUString &rFormula, bool bNoListening, const formula::FormulaGrammar::Grammar)
CompareState CompareByTokenArray(const ScFormulaCell &rOther) const
virtual ~ScFormulaCell() override
void SetHybridEmptyDisplayedAsString()
For import only: set an empty cell result to be displayed as empty string.
ScFormulaCell * pPrevious
ScTokenArray * GetCode()
bool IsMultilineResult()
Determines whether or not the result string contains more than one paragraph.
sal_uInt16 GetSeenInIteration() const
bool InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope &aScope, bool &bDependencyComputed, bool &bDependencyCheckFailed)
void SetHybridString(const svl::SharedString &r)
For import only: set a string result.
bool UpdateReferenceOnMove(const sc::RefUpdateContext &rCxt, ScDocument *pUndoDoc, const ScAddress *pUndoCellPos)
Update reference in response to cell move.
void SetHybridFormula(const OUString &r, const formula::FormulaGrammar::Grammar eGrammar)
For import only: set a temporary formula string to be compiled later.
void SetNeedsDirty(bool bVar)
ScFormulaCell * pPreviousTrack
ScAddress aPos
bool Interpret(SCROW nStartOffset=-1, SCROW nEndOffset=-1)
void UpdateMoveTab(const sc::RefUpdateMoveTabContext &rCxt, SCTAB nTabNo)
OUString GetFormula(const formula::FormulaGrammar::Grammar=formula::FormulaGrammar::GRAM_DEFAULT, const ScInterpreterContext *pContext=nullptr) const
void SetHybridDouble(double n)
For import only: set a double result.
void SetNextTrack(ScFormulaCell *pF)
void CompileTokenArray(bool bNoListening=false)
void SetResultError(FormulaError n)
@ SINGLE
only single cell relative reference
@ NONE
no relative reference from named expression
@ DOUBLE
at least one range relative reference from named expression
A class to wrap ScRecursionHelper::PushFormulaGroup(), ScRecursionHelper::PopFormulaGroup() and make ...
Store a variable formula cell result, balancing between runtime performance and memory consumption.
void SetDouble(double f)
Set direct double.
const ScMatrixFormulaCellToken * GetMatrixFormulaCellToken() const
Get the const ScMatrixFormulaCellToken* if token is of that type, else NULL.
void SetHybridDouble(double f)
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridString(),...
void SetResultError(FormulaError nErr)
Set error code, don't touch token or double.
ScMatrixFormulaCellToken * GetMatrixFormulaCellTokenNonConst()
Get the ScMatrixFormulaCellToken* if token is of that type, else NULL.
formula::FormulaConstTokenRef GetToken() const
May be NULL if SetToken() did so, also if type formula::svDouble or formula::svError!
void SetToken(const formula::FormulaToken *p)
Sets a direct double if token type is formula::svDouble, or mbEmpty if formula::svEmptyCell,...
ScConstMatrixRef GetMatrix() const
Return matrix if type formula::svMatrixCell and ScMatrix present, else NULL.
formula::FormulaConstTokenRef GetCellResultToken() const
Return upper left token if formula::svMatrixCell, else return GetToken().
double GetDouble() const
Return value if type formula::svDouble or formula::svHybridCell or formula::svMatrixCell and upper le...
bool IsEmptyDisplayedAsString() const
If type is formula::svEmptyCell (including matrix upper left) and should be displayed as empty string...
void SetHybridEmptyDisplayedAsString()
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridFormula(),...
bool GetErrorOrDouble(FormulaError &rErr, double &rVal) const
void SetHybridString(const svl::SharedString &rStr)
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridString()/SetHybr...
const svl::SharedString & GetString() const
Return string if type formula::svString or formula::svHybridCell or formula::svMatrixCell and upper l...
bool IsValueNoError() const
formula::StackVar GetType() const
Return type of result, including formula::svError, formula::svEmptyCell, formula::svDouble and formul...
void SetHybridFormula(const OUString &rFormula)
Should only be used by import filters, best in the order SetHybridDouble(), SetHybridString()/SetHybr...
bool IsMultiline() const
Determines whether or not the result is a string containing more than one paragraph.
void SetMatrix(SCCOL nCols, SCROW nRows, const ScConstMatrixRef &pMat, const formula::FormulaToken *pUL)
bool IsValue() const
Test for cell result type formula::svDouble, including upper left if formula::svMatrixCell.
OUString GetHybridFormula() const
Return formula string if type formula::svHybridCell, else empty string.
void Assign(const ScFormulaResult &r)
Assignment as in operator=() but without return.
formula::StackVar GetCellResultType() const
If type is formula::svMatrixCell return the type of upper left element, else GetType()
sc::FormulaResultValue GetResult() const
FormulaError GetResultError() const
Get error code if set or GetCellResultType() is formula::svError or svUnknown, else 0.
static SC_DLLPUBLIC sal_uInt32 GetStandardFormat(SvNumberFormatter &, sal_uInt32 nFormat, SvNumFormatType nType)
Definition: global.cxx:160
static OUString GetErrorString(FormulaError nErrNumber)
Definition: global.cxx:315
bool convert(const ScTokenArray &rCode, sc::FormulaLogger::GroupScope &rScope)
ScInterpreterContext * GetInterpreterContext() const
sal_uLong GetRetFormatIndex() const
Definition: interpre.hxx:1021
formula::StackVar Interpret()
Definition: interpr4.cxx:3972
formula::StackVar GetResultType() const
Definition: interpre.hxx:1016
const svl::SharedString & GetStringResult() const
Definition: interpr4.cxx:4814
const formula::FormulaConstTokenRef & GetResultToken() const
Definition: interpre.hxx:1019
static const ScCalcConfig & GetGlobalConfig()
Definition: interpr4.cxx:3902
void Init(ScFormulaCell *pCell, const ScAddress &rPos, ScTokenArray &rTokArray)
Definition: interpr4.cxx:3866
double GetNumResult() const
Definition: interpre.hxx:1018
SvNumFormatType GetRetFormatType() const
Definition: interpre.hxx:1020
FormulaError GetError() const
Definition: interpre.hxx:1015
VolatileType GetVolatileType() const
Definition: interpre.hxx:172
void RemoveDependentCell(const ScFormulaCell *pCell)
Definition: macromgr.cxx:182
Stores the matrix result at the formula cell, additionally the range the matrix formula occupies.
Definition: token.hxx:325
void GetMatColsRows(SCCOL &nC, SCROW &nR) const
Definition: token.hxx:342
void SetMatColsRows(SCCOL nC, SCROW nR)
Definition: token.hxx:337
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:101
void GetDimensions(SCSIZE &rC, SCSIZE &rR) const
Definition: scmatrix.cxx:3143
void SetStateCountDownOnPercent(sal_uInt64 nVal)
Definition: progress.hxx:102
static ScProgress * GetInterpretProgress()
Definition: progress.hxx:65
bool Enabled() const
Definition: progress.hxx:115
ScTokenArray * GetCode()
Definition: rangenam.hxx:119
bool HasReferences() const
Definition: rangenam.cxx:520
void Join(const ScRange &, bool bIsInList=false)
Definition: rangelst.cxx:152
size_t size() const
Definition: rangelst.hxx:89
ScRangePair * Find(const ScAddress &)
Definition: rangelst.cxx:1279
const ScRange & GetRange(sal_uInt16 n) const
Definition: address.hxx:810
ScAddress aEnd
Definition: address.hxx:498
bool Contains(const ScAddress &) const
is Address& fully in Range?
Definition: address.hxx:718
ScAddress aStart
Definition: address.hxx:497
bool IsInRecursionReturn() const
A pure recursion return, no iteration.
void SetDoingRecursion(bool b)
void SetInRecursionReturn(bool b)
const ScFormulaRecursionList::iterator & GetLastIterationStart() const
sal_uInt16 GetIteration() const
sal_uInt16 GetDepComputeLevel() const
bool IsInIterationReturn() const
ScRecursionInIterationStack & GetRecursionInIterationStack()
const ScFormulaRecursionList & GetList() const
bool CheckFGIndependence(ScFormulaCellGroup *pFG)
void SetInIterationReturn(bool b)
bool & GetConvergingReference()
ScFormulaRecursionList::iterator GetIterationStart()
bool IsDoingRecursion() const
void Insert(ScFormulaCell *p, bool bOldRunning, const ScFormulaResult &rRes)
bool IsDoingIteration() const
bool HasFormulaGroupSet() const
bool IsAbortingDependencyComputation() const
bool AnyCycleMemberInDependencyEvalMode(const ScFormulaCell *pCell)
bool IsInReturn() const
Any return, recursion or iteration, iteration is always coupled with recursion.
sal_uInt16 GetRecursionCount() const
ScFormulaRecursionList::iterator GetIterationEnd()
static ScRefUpdateRes UpdateTranspose(const ScDocument &rDoc, const ScRange &rSource, const ScAddress &rDest, ScRange &rRef)
Definition: refupdat.cxx:531
static void DoTranspose(SCCOL &rCol, SCROW &rRow, SCTAB &rTab, const ScDocument &rDoc, const ScRange &rSource, const ScAddress &rDest)
Definition: refupdat.cxx:507
static ScRefUpdateRes UpdateGrow(const ScRange &rArea, SCCOL nGrowX, SCROW nGrowY, ScRange &rRef)
Definition: refupdat.cxx:555
ScInterpreterContext * GetInterpreterContextForThreadIdx(size_t nThreadIdx) const
sc::RefUpdateResult AdjustReferenceOnMovedTab(const sc::RefUpdateMoveTabContext &rCxt, const ScAddress &rOldPos)
Definition: token.cxx:4413
void AdjustReferenceOnCopy(const ScAddress &rNewPos)
Adjust internal range references on base position change to justify / put in order the relative refer...
Definition: token.cxx:4581
ScTokenArray CloneValue() const
True copy!
Definition: token.cxx:1989
sc::RefUpdateResult AdjustReferenceOnShift(const sc::RefUpdateContext &rCxt, const ScAddress &rOldPos)
Adjust all references in response to shifting of cells during cell insertion and deletion.
Definition: token.cxx:3130
size_t GetHash() const
Definition: tokenarray.hxx:79
std::unique_ptr< ScTokenArray > Clone() const
Definition: token.cxx:1931
bool IsEnabledForThreading() const
Definition: tokenarray.hxx:269
ScFormulaVectorState GetVectorState() const
Definition: tokenarray.hxx:81
void MoveReferenceColReorder(const ScAddress &rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColRowReorderMapType &rColMap)
Move reference positions in response to column reordering.
Definition: token.cxx:3524
bool IsEnabledForOpenCL() const
Definition: tokenarray.hxx:268
sc::RefUpdateResult AdjustReferenceOnDeletedTab(const sc::RefUpdateDeleteTabContext &rCxt, const ScAddress &rOldPos)
Adjust all references on sheet deletion.
Definition: token.cxx:4255
sc::RefUpdateResult AdjustReferenceOnInsertedTab(const sc::RefUpdateInsertTabContext &rCxt, const ScAddress &rOldPos)
Definition: token.cxx:4327
sc::RefUpdateResult AdjustReferenceOnMove(const sc::RefUpdateContext &rCxt, const ScAddress &rOldPos, const ScAddress &rNewPos)
Definition: token.cxx:3361
OUString CreateString(sc::TokenStringContext &rCxt, const ScAddress &rPos) const
Create a string representation of formula token array without modifying the internal state of the tok...
Definition: token.cxx:5238
sal_Int32 GetWeight() const
Definition: token.cxx:5359
void GenHash()
True copy!
Definition: token.cxx:1730
void MoveReferenceRowReorder(const ScAddress &rPos, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, const sc::ColRowReorderMapType &rRowMap)
Definition: token.cxx:3594
void AdjustAbsoluteRefs(const ScDocument &rOldDoc, const ScAddress &rOldPos, const ScAddress &rNewPos, bool bCheckCopyArea)
Make all absolute references pointing to the copied range if the range is copied too.
Definition: token.cxx:2597
void ReadjustAbsolute3DReferences(const ScDocument &rOldDoc, ScDocument &rNewDoc, const ScAddress &rPos, bool bRangeName=false)
Make all absolute references external references pointing to the old document.
Definition: token.cxx:2545
virtual void Clear() override
Definition: token.cxx:1924
SfxHintId GetId() const
ScComplexRefData & Ref()
Definition: token.hxx:437
const ScSingleRefData & Ref2
Definition: token.hxx:445
const ScSingleRefData & Ref1
Definition: token.hxx:444
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, const Color **ppColor, bool bUseStarFormat=false)
SvNumFormatType GetType(sal_uInt32 nFIndex) const
static bool IsCompatible(SvNumFormatType eOldType, SvNumFormatType eNewType)
sal_uInt16 getId() const
bool HasBroadcaster() const
static ThreadPool & getSharedOptimalPool()
void waitUntilDone(const std::shared_ptr< ThreadTaskTag > &, bool bJoin=true)
static std::shared_ptr< ThreadTaskTag > createThreadTaskTag()
void pushTask(std::unique_ptr< ThreadTask > pTask)
sal_Int32 getWorkerCount() const
SvNumFormatType GetNumFormatType() const
void CreateStringFromTokenArray(OUString &rFormula)
bool HasNameOrColRowName() const
FormulaToken * AddToken(const FormulaToken &)
void SetHyperLink(bool bVal)
bool IsRecalcModeNormal() const
bool HasMatrixDoubleRefOps() const
sal_uInt16 GetLen() const
void AddRecalcMode(ScRecalcMode nBits)
bool IsRecalcModeAlways() const
bool IsRecalcModeForced() const
FormulaToken ** GetArray() const
void SetCodeError(FormulaError n)
FormulaError GetCodeError() const
void ReinternStrings(svl::SharedStringPool &rPool)
sal_uInt16 GetCodeLen() const
FormulaToken ** GetCode() const
bool HasOpCodeRPN(OpCode) const
bool IsRecalcModeOnRefMove() const
bool HasOpCode(OpCode) const
FormulaToken * AddBad(const OUString &rStr)
virtual const ScSingleRefData * GetSingleRef() const
bool IsFunction() const
virtual sal_uInt8 GetByte() const
virtual const ScSingleRefData * GetSingleRef2() const
virtual sal_uInt16 GetIndex() const
virtual void SetSheet(sal_Int16 n)
virtual sal_Int16 GetSheet() const
virtual const OUString & GetExternal() const
OpCode GetOpCode() const
StackVar GetType() const
virtual const svl::SharedString & GetString() const
virtual double GetDouble() const
virtual void SetIndex(sal_uInt16 n)
sal_uInt8 GetParamCount() const
virtual FormulaError GetError() const
std::pair< const_iterator, bool > insert(Value &&x)
Temporarily switch on/off auto calculation mode.
Definition: scopetools.hxx:27
void setGrammar(formula::FormulaGrammar::Grammar eGram)
ScAddress getOldPosition(const ScAddress &rPos) const
ScTokenArray * getOldCode()
Abstract base class for vectorised formula group interpreters, plus a global instance factory.
static FormulaGroupInterpreter * getStatic()
load and/or configure the correct formula group interpreter
virtual bool interpret(ScDocument &rDoc, const ScAddress &rTopPos, ScFormulaCellGroupRef &xGroup, ScTokenArray &rCode)=0
void addMessage(const OUString &)
static FormulaLogger get()
GroupScope enterGroup(const ScDocument &, const ScFormulaCell &)
SCROW getEndRow() const
Definition: refhint.cxx:42
SCTAB getTab() const
Definition: refhint.cxx:32
const sc::ColRowReorderMapType & getColMap() const
Definition: refhint.cxx:27
SCROW getStartRow() const
Definition: refhint.cxx:37
Type getType() const
Definition: refhint.cxx:17
@ StartListening
Definition: refhint.hxx:26
@ StopListening
Definition: refhint.hxx:27
@ ColumnReordered
Definition: refhint.hxx:24
@ RowReordered
Definition: refhint.hxx:25
Used to collect positions of formula cells that belong to a formula group.
void add(const ScAddress &rPos)
const sc::ColRowReorderMapType & getRowMap() const
Definition: refhint.cxx:52
SCTAB getTab() const
Definition: refhint.cxx:57
SCCOL getEndColumn() const
Definition: refhint.cxx:67
SCCOL getStartColumn() const
Definition: refhint.cxx:62
Keep track of all named expressions that have been updated during reference update.
void setUpdatedName(SCTAB nTab, sal_uInt16 nIndex)
const OUString & getString() const
bool isEmpty() const
static const SharedString & getEmptyString()
URL aURL
@ CORE
Definition: document.hxx:317
float u
FormulaError
FormulaError GetDoubleErrorValue(double fVal)
static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames &rIndexes, const ScTokenArray *pCode, const ScDocument &rDoc, int nRecursion)
static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef &xGroup, const ScDocument &rDoc, o3tl::sorted_vector< ScFormulaCellGroup * > &rFGSet, std::map< SCCOL, ScFormulaCell * > &rFGMap, bool bLeft)
static void lcl_TransposeReference(ScSingleRefData &rRef)
ScMatrixMode
@ CELLTYPE_FORMULA
Definition: global.hxx:276
@ URM_COPY
Definition: global.hxx:303
@ URM_MOVE
Definition: global.hxx:304
@ URM_INSDEL
Definition: global.hxx:302
ScCloneFlags
Definition: global.hxx:251
@ StartListening
If set, cloned formula cells will start to listen to the document.
@ NoMakeAbsExternal
If set, absolute refs will not transformed to external references.
@ Default
Default cell clone flags: do not start listening, do not adjust 3D refs to old position,...
@ NamesToLocal
If set, global named expressions will be converted to sheet-local named expressions.
SfxHintId
sal_Int32 nIndex
void * p
sal_Int64 n
#define SC_LISTENER_QUERY_FORMULA_GROUP_POS
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
if(aStr !=aBuf) UpdateName_Impl(m_xFollowLb.get()
aStr
aBuf
std::unique_ptr< sal_Int32[]> pData
::boost::intrusive_ptr< FormulaToken > FormulaTokenRef
StackVar
svIndex
svExternalDoubleRef
svDouble
svError
svDoubleRef
svExternalSingleRef
svMatrixCell
svString
svSingleRef
svHybridCell
int i
CAUTION! The following defines must be in the same namespace as the respective type.
Definition: broadcast.cxx:15
MatrixEdge
Definition: types.hxx:65
@ GroupCalcDisabled
Definition: types.hxx:77
@ GroupCalcEnabled
Definition: types.hxx:78
@ GroupCalcRunning
Definition: types.hxx:79
OpCode
ocAggregate
ocTableRef
ocColRowName
ocSubTotal
ocMacro
ocRange
ocDBArea
ocName
const std::function< void()> maFunc
#define N
#define MIN_NO_CODES_PER_PROGRESS_UPDATE
Definition: progress.hxx:37
#define SC_MATRIX_DOUBLEREF
Definition: rechead.hxx:35
@ UR_NOTHING
Reference not affected, no change at all.
Definition: refupdat.hxx:31
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
ScAddress & mrPos
#define MAXRECURSION
constexpr TypedWhichId< SfxUInt32Item > ATTR_VALIDDATA(153)
bool mbActive
ParserContextSharedPtr mpContext
sal_uIntPtr sal_uLong
TOOLS_DLLPUBLIC SvStream & endl(SvStream &rStr)
bool operator<(const AreaListenerKey &r) const
static bool isOpenCLEnabled()
Definition: calcconfig.cxx:69
static ForceCalculationType getForceCalculationType()
Definition: calcconfig.cxx:63
static bool isThreadingEnabled()
Definition: calcconfig.cxx:80
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
SC_DLLPUBLIC ScRange toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
ScSingleRefData Ref2
Definition: refdata.hxx:125
bool IsDeleted() const
Definition: refdata.cxx:580
void SetRange(const ScSheetLimits &rLimits, const ScRange &rRange, const ScAddress &rPos)
Set a new range, assuming that the ordering of the range matches the ordering of the reference data f...
Definition: refdata.cxx:498
ScSingleRefData Ref1
Definition: refdata.hxx:124
std::optional< ScTokenArray > mpCode
Definition: formulacell.hxx:77
sc::FormulaGroupAreaListener * getAreaListener(ScFormulaCell **ppTopCell, const ScRange &rRange, bool bStartFixed, bool bEndFixed)
SvNumFormatType mnFormatType
Definition: formulacell.hxx:81
void compileCode(ScDocument &rDoc, const ScAddress &rPos, formula::FormulaGrammar::Grammar eGram)
ScFormulaCell * mpTopCell
Definition: formulacell.hxx:78
void endAllGroupListening(ScDocument &rDoc)
AreaListenersType m_AreaListeners
Definition: formulacell.hxx:72
void setCode(const ScTokenArray &rCode)
SvNumberFormatter * GetFormatTable() const
std::vector< DelayedSetNumberFormat > maDelayedSetNumberFormat
ScInterpreter * pInterpreter
Instantiate this to ensure that subsequent modification of the document will cause an assertion failu...
Definition: document.hxx:2728
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:108
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
void SetAbsTab(SCTAB nVal)
Definition: refdata.cxx:93
SCCOL Col() const
Definition: refdata.cxx:247
bool IsDeleted() const
Definition: refdata.cxx:125
SCTAB Tab() const
Definition: refdata.cxx:254
bool IsTabRel() const
Definition: refdata.hxx:69
SCROW Row() const
Definition: refdata.cxx:240
void SetRelRow(SCROW nVal)
Definition: refdata.cxx:82
bool IsRowRel() const
Definition: refdata.hxx:67
ScAddress toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
bool IsColRel() const
Definition: refdata.hxx:65
void SetRelCol(SCCOL nVal)
Definition: refdata.cxx:65
void SetFlag3D(bool bVal)
Definition: refdata.hxx:89
void IncTab(SCTAB nInc)
Definition: refdata.cxx:105
bool IsFlag3D() const
Definition: refdata.hxx:90
svl::SharedString maString
Context for reference update during shifting, moving or copying of cell ranges.
SCROW mnRowDelta
Amount and direction of movement in the row direction.
UpdateRefMode meMode
update mode - insert/delete, copy, or move.
SCCOL mnColDelta
Amount and direction of movement in the column direction.
SCTAB mnTabDelta
Amount and direction of movement in the sheet direction.
ScRange maRange
Range of cells that are about to be moved for insert/delete/move modes.
bool mbReferenceModified
This flag indicates whether any reference in the token array has been modified.
bool mbValueChanged
When this flag is true, the result of the formula needs to be re-calculated either because it contain...
bool mbNameModified
When this flag is true, it indicates that the token array contains a range name that's been updated.
Context for creating string from an array of formula tokens, used in ScTokenArray::CreateString().
NUMBER
ScRecalcMode
#define SAL_MAX_INT32
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
sal_Int16 SCTAB
Definition: types.hxx:22
::boost::intrusive_ptr< ScFormulaCellGroup > ScFormulaCellGroupRef
Definition: types.hxx:43
::boost::intrusive_ptr< const ScMatrix > ScConstMatrixRef
Definition: types.hxx:26
sal_Int16 SCCOL
Definition: types.hxx:21
@ FormulaVectorUnknown
Definition: types.hxx:60
@ FormulaVectorDisabledByOpCode
Definition: types.hxx:55
@ FormulaVectorDisabledByStackVariable
Definition: types.hxx:56
@ FormulaVectorEnabled
Definition: types.hxx:58
@ FormulaVectorDisabledNotInSubSet
Definition: types.hxx:54
@ FormulaVectorDisabled
Definition: types.hxx:53
@ FormulaVectorCheckReference
Definition: types.hxx:59
sal_Int32 SCROW
Definition: types.hxx:17
const sal_uInt8 A
Definition: xlformula.cxx:52
sal_Int32 nLength
SvNumFormatType
#define SV_COUNTRY_LANGUAGE_OFFSET
constexpr sal_uInt32 NUMBERFORMAT_ENTRY_NOT_FOUND