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