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