summaryrefslogtreecommitdiff
path: root/sc/source/core/data/cell2.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/cell2.cxx')
-rw-r--r--sc/source/core/data/cell2.cxx1638
1 files changed, 1638 insertions, 0 deletions
diff --git a/sc/source/core/data/cell2.cxx b/sc/source/core/data/cell2.cxx
new file mode 100644
index 000000000000..4de78c71fe79
--- /dev/null
+++ b/sc/source/core/data/cell2.cxx
@@ -0,0 +1,1638 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sc.hxx"
+
+
+
+// INCLUDE ---------------------------------------------------------------
+#include <algorithm>
+#include <deque>
+
+#include <boost/bind.hpp>
+
+#include <vcl/mapmod.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+
+#include "cell.hxx"
+#include "compiler.hxx"
+#include "formula/errorcodes.hxx"
+#include "document.hxx"
+#include "rangenam.hxx"
+#include "rechead.hxx"
+#include "refupdat.hxx"
+#include "scmatrix.hxx"
+#include "editutil.hxx"
+#include "chgtrack.hxx"
+#include "externalrefmgr.hxx"
+
+using namespace formula;
+
+// STATIC DATA -----------------------------------------------------------
+
+#ifdef USE_MEMPOOL
+const USHORT nMemPoolEditCell = (0x1000 - 64) / sizeof(ScNoteCell);
+IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell, nMemPoolEditCell, nMemPoolEditCell )
+#endif
+
+// ============================================================================
+
+ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP,
+ const SfxItemPool* pFromPool ) :
+ ScBaseCell( CELLTYPE_EDIT ),
+ pString( NULL ),
+ pDoc( pDocP )
+{
+ SetTextObject( pObject, pFromPool );
+}
+
+ScEditCell::ScEditCell( const ScEditCell& rCell, ScDocument& rDoc ) :
+ ScBaseCell( rCell ),
+ pString( NULL ),
+ pDoc( &rDoc )
+{
+ SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() );
+}
+
+ScEditCell::ScEditCell( const String& rString, ScDocument* pDocP ) :
+ ScBaseCell( CELLTYPE_EDIT ),
+ pString( NULL ),
+ pDoc( pDocP )
+{
+ DBG_ASSERT( rString.Search('\n') != STRING_NOTFOUND ||
+ rString.Search(CHAR_CR) != STRING_NOTFOUND,
+ "EditCell mit einfachem Text !?!?" );
+
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText( rString );
+ pData = rEngine.CreateTextObject();
+}
+
+ScEditCell::~ScEditCell()
+{
+ delete pData;
+ delete pString;
+
+#ifdef DBG_UTIL
+ eCellType = CELLTYPE_DESTROYED;
+#endif
+}
+
+void ScEditCell::SetData( const EditTextObject* pObject,
+ const SfxItemPool* pFromPool )
+{
+ if ( pString )
+ {
+ delete pString;
+ pString = NULL;
+ }
+ delete pData;
+ SetTextObject( pObject, pFromPool );
+}
+
+void ScEditCell::GetData( const EditTextObject*& rpObject ) const
+{
+ rpObject = pData;
+}
+
+void ScEditCell::GetString( String& rString ) const
+{
+ if ( pString )
+ rString = *pString;
+ else if ( pData )
+ {
+ // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText( *pData );
+ rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs
+ // cache short strings for formulas
+ if ( rString.Len() < 256 )
+ ((ScEditCell*)this)->pString = new String( rString ); //! non-const
+ }
+ else
+ rString.Erase();
+}
+
+void ScEditCell::SetTextObject( const EditTextObject* pObject,
+ const SfxItemPool* pFromPool )
+{
+ if ( pObject )
+ {
+ if ( pFromPool && pDoc->GetEditPool() == pFromPool )
+ pData = pObject->Clone();
+ else
+ { //! anderer Pool
+ // Leider gibt es keinen anderen Weg, um den Pool umzuhaengen,
+ // als das Object durch eine entsprechende Engine zu schleusen..
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ if ( pObject->HasOnlineSpellErrors() )
+ {
+ ULONG nControl = rEngine.GetControlWord();
+ const ULONG nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS;
+ BOOL bNewControl = ( (nControl & nSpellControl) != nSpellControl );
+ if ( bNewControl )
+ rEngine.SetControlWord( nControl | nSpellControl );
+ rEngine.SetText( *pObject );
+ pData = rEngine.CreateTextObject();
+ if ( bNewControl )
+ rEngine.SetControlWord( nControl );
+ }
+ else
+ {
+ rEngine.SetText( *pObject );
+ pData = rEngine.CreateTextObject();
+ }
+ }
+ }
+ else
+ pData = NULL;
+}
+
+// ============================================================================
+
+namespace
+{
+
+using std::deque;
+
+typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&);
+
+
+static SCCOLROW lcl_GetCol(const ScSingleRefData& rData)
+{
+ return rData.nCol;
+}
+
+
+static SCCOLROW lcl_GetRow(const ScSingleRefData& rData)
+{
+ return rData.nRow;
+}
+
+
+static SCCOLROW lcl_GetTab(const ScSingleRefData& rData)
+{
+ return rData.nTab;
+}
+
+
+/** Check if both references span the same range in selected dimension.
+ */
+static bool
+lcl_checkRangeDimension(
+ const SingleDoubleRefProvider& rRef1,
+ const SingleDoubleRefProvider& rRef2,
+ const DimensionSelector aWhich)
+{
+ return
+ aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1)
+ && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2);
+}
+
+
+static bool
+lcl_checkRangeDimensions(
+ const SingleDoubleRefProvider& rRef1,
+ const SingleDoubleRefProvider& rRef2,
+ bool& bCol, bool& bRow, bool& bTab)
+{
+ const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol));
+ const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow));
+ const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab));
+
+ // Test if exactly two dimensions are equal
+ if (!(bSameCols ^ bSameRows ^ bSameTabs)
+ && (bSameCols || bSameRows || bSameTabs))
+ {
+ bCol = !bSameCols;
+ bRow = !bSameRows;
+ bTab = !bSameTabs;
+ return true;
+ }
+ return false;
+}
+
+
+/** Check if references in given reference list can possibly
+ form a range. To do that, two of their dimensions must be the same.
+ */
+static bool
+lcl_checkRangeDimensions(
+ const deque<ScToken*>::const_iterator aBegin,
+ const deque<ScToken*>::const_iterator aEnd,
+ bool& bCol, bool& bRow, bool& bTab)
+{
+ deque<ScToken*>::const_iterator aCur(aBegin);
+ ++aCur;
+ const SingleDoubleRefProvider aRef(**aBegin);
+ bool bOk(false);
+ {
+ const SingleDoubleRefProvider aRefCur(**aCur);
+ bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab);
+ }
+ while (bOk && aCur != aEnd)
+ {
+ const SingleDoubleRefProvider aRefCur(**aCur);
+ bool bColTmp(false);
+ bool bRowTmp(false);
+ bool bTabTmp(false);
+ bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
+ bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
+ ++aCur;
+ }
+
+ if (bOk && aCur == aEnd)
+ {
+ bCol = bCol;
+ bRow = bRow;
+ bTab = bTab;
+ return true;
+ }
+ return false;
+}
+
+
+bool
+lcl_lessReferenceBy(
+ const ScToken* const pRef1, const ScToken* const pRef2,
+ const DimensionSelector aWhich)
+{
+ const SingleDoubleRefProvider rRef1(*pRef1);
+ const SingleDoubleRefProvider rRef2(*pRef2);
+ return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1);
+}
+
+
+/** Returns true if range denoted by token pRef2 starts immediately after
+ range denoted by token pRef1. Dimension, in which the comparison takes
+ place, is given by aWhich.
+ */
+bool
+lcl_isImmediatelyFollowing(
+ const ScToken* const pRef1, const ScToken* const pRef2,
+ const DimensionSelector aWhich)
+{
+ const SingleDoubleRefProvider rRef1(*pRef1);
+ const SingleDoubleRefProvider rRef2(*pRef2);
+ return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1;
+}
+
+
+static bool
+lcl_checkIfAdjacent(
+ const deque<ScToken*>& rReferences,
+ const DimensionSelector aWhich)
+{
+ typedef deque<ScToken*>::const_iterator Iter;
+ Iter aBegin(rReferences.begin());
+ Iter aEnd(rReferences.end());
+ Iter aBegin1(aBegin);
+ ++aBegin1, --aEnd;
+ return std::equal(
+ aBegin, aEnd, aBegin1,
+ boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich));
+}
+
+
+static void
+lcl_fillRangeFromRefList(
+ const deque<ScToken*>& rReferences, ScRange& rRange)
+{
+ const ScSingleRefData aStart(
+ SingleDoubleRefProvider(*rReferences.front()).Ref1);
+ rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab);
+ const ScSingleRefData aEnd(
+ SingleDoubleRefProvider(*rReferences.back()).Ref2);
+ rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab);
+}
+
+
+static bool
+lcl_refListFormsOneRange(
+ const ScAddress& aPos, deque<ScToken*>& rReferences,
+ ScRange& rRange)
+{
+ std::for_each(
+ rReferences.begin(), rReferences.end(),
+ bind(&ScToken::CalcAbsIfRel, _1, aPos))
+ ;
+ if (rReferences.size() == 1) {
+ lcl_fillRangeFromRefList(rReferences, rRange);
+ return true;
+ }
+
+ bool bCell(false);
+ bool bRow(false);
+ bool bTab(false);
+ if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(),
+ bCell, bRow, bTab))
+ {
+ DimensionSelector aWhich;
+ if (bCell)
+ {
+ aWhich = lcl_GetCol;
+ }
+ else if (bRow)
+ {
+ aWhich = lcl_GetRow;
+ }
+ else if (bTab)
+ {
+ aWhich = lcl_GetTab;
+ }
+ else
+ {
+ OSL_ENSURE(false, "lcl_checkRangeDimensions shouldn't allow that!");
+ aWhich = lcl_GetRow; // initialize to avoid warning
+ }
+ // Sort the references by start of range
+ std::sort(rReferences.begin(), rReferences.end(),
+ boost::bind(lcl_lessReferenceBy, _1, _2, aWhich));
+ if (lcl_checkIfAdjacent(rReferences, aWhich))
+ {
+ lcl_fillRangeFromRefList(rReferences, rRange);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool lcl_isReference(const FormulaToken& rToken)
+{
+ return
+ rToken.GetType() == svSingleRef ||
+ rToken.GetType() == svDoubleRef;
+}
+
+}
+
+BOOL ScFormulaCell::IsEmpty()
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ return aResult.GetCellResultType() == formula::svEmptyCell;
+}
+
+BOOL ScFormulaCell::IsEmptyDisplayedAsString()
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ return aResult.IsEmptyDisplayedAsString();
+}
+
+BOOL ScFormulaCell::IsValue()
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ return aResult.IsValue();
+}
+
+double ScFormulaCell::GetValue()
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
+ !aResult.GetResultError())
+ return aResult.GetDouble();
+ return 0.0;
+}
+
+double ScFormulaCell::GetValueAlways()
+{
+ // for goal seek: return result value even if error code is set
+
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ return aResult.GetDouble();
+}
+
+void ScFormulaCell::GetString( String& rString )
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
+ !aResult.GetResultError())
+ rString = aResult.GetString();
+ else
+ rString.Erase();
+}
+
+const ScMatrix* ScFormulaCell::GetMatrix()
+{
+ if ( pDocument->GetAutoCalc() )
+ {
+ // Was stored !bDirty but an accompanying matrix cell was bDirty?
+ // => we need to get the matrix.
+ if (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is())
+ bDirty = TRUE;
+ if ( IsDirtyOrInTableOpDirty() )
+ Interpret();
+ }
+ return aResult.GetMatrix();
+}
+
+BOOL ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
+{
+ switch ( cMatrixFlag )
+ {
+ case MM_FORMULA :
+ rPos = aPos;
+ return TRUE;
+// break;
+ case MM_REFERENCE :
+ {
+ pCode->Reset();
+ ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if( t )
+ {
+ ScSingleRefData& rRef = t->GetSingleRef();
+ rRef.CalcAbsIfRel( aPos );
+ if ( rRef.Valid() )
+ {
+ rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+
+/*
+ Edge-Values:
+
+ 8
+ 4 16
+ 2
+
+ innerhalb: 1
+ ausserhalb: 0
+ (reserviert: offen: 32)
+ */
+
+USHORT ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos )
+{
+ switch ( cMatrixFlag )
+ {
+ case MM_FORMULA :
+ case MM_REFERENCE :
+ {
+ static SCCOL nC;
+ static SCROW nR;
+ ScAddress aOrg;
+ if ( !GetMatrixOrigin( aOrg ) )
+ return 0; // dumm gelaufen..
+ if ( aOrg != rOrgPos )
+ { // erstes Mal oder andere Matrix als letztes Mal
+ rOrgPos = aOrg;
+ ScFormulaCell* pFCell;
+ if ( cMatrixFlag == MM_REFERENCE )
+ pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg );
+ else
+ pFCell = this; // this MM_FORMULA
+ // this gibt's nur einmal, kein Vergleich auf pFCell==this
+ if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA
+ && pFCell->cMatrixFlag == MM_FORMULA )
+ {
+ pFCell->GetMatColsRows( nC, nR );
+ if ( nC == 0 || nR == 0 )
+ { // aus altem Dokument geladen, neu erzeugen
+ nC = 1;
+ nR = 1;
+ ScAddress aTmpOrg;
+ ScBaseCell* pCell;
+ ScAddress aAdr( aOrg );
+ aAdr.IncCol();
+ BOOL bCont = TRUE;
+ do
+ {
+ pCell = pDocument->GetCell( aAdr );
+ if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
+ && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
+ && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg )
+ {
+ nC++;
+ aAdr.IncCol();
+ }
+ else
+ bCont = FALSE;
+ } while ( bCont );
+ aAdr = aOrg;
+ aAdr.IncRow();
+ bCont = TRUE;
+ do
+ {
+ pCell = pDocument->GetCell( aAdr );
+ if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
+ && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
+ && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg )
+ {
+ nR++;
+ aAdr.IncRow();
+ }
+ else
+ bCont = FALSE;
+ } while ( bCont );
+ pFCell->SetMatColsRows( nC, nR );
+ }
+ }
+ else
+ {
+#ifdef DBG_UTIL
+ String aTmp;
+ ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " );
+ aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
+ aMsg += ", MatOrg: ";
+ aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
+ DBG_ERRORFILE( aMsg.GetBuffer() );
+#endif
+ return 0; // bad luck ...
+ }
+ }
+ // here we are, healthy and clean, somewhere in between
+ SCsCOL dC = aPos.Col() - aOrg.Col();
+ SCsROW dR = aPos.Row() - aOrg.Row();
+ USHORT nEdges = 0;
+ if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
+ {
+ if ( dC == 0 )
+ nEdges |= 4; // linke Kante
+ if ( dC+1 == nC )
+ nEdges |= 16; // rechte Kante
+ if ( dR == 0 )
+ nEdges |= 8; // obere Kante
+ if ( dR+1 == nR )
+ nEdges |= 2; // untere Kante
+ if ( !nEdges )
+ nEdges = 1; // mittendrin
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ String aTmp;
+ ByteString aMsg( "broken Matrix, Pos: " );
+ aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
+ aMsg += ", MatOrg: ";
+ aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
+ aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
+ aMsg += ", MatCols: ";
+ aMsg += ByteString::CreateFromInt32( nC );
+ aMsg += ", MatRows: ";
+ aMsg += ByteString::CreateFromInt32( nR );
+ aMsg += ", DiffCols: ";
+ aMsg += ByteString::CreateFromInt32( dC );
+ aMsg += ", DiffRows: ";
+ aMsg += ByteString::CreateFromInt32( dR );
+ DBG_ERRORFILE( aMsg.GetBuffer() );
+ }
+#endif
+ return nEdges;
+// break;
+ }
+ default:
+ return 0;
+ }
+}
+
+USHORT ScFormulaCell::GetErrCode()
+{
+ if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
+ Interpret();
+ /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
+ * and not also abused for signaling other error conditions we could bail
+ * out even before attempting to interpret broken code. */
+ USHORT nErr = pCode->GetCodeError();
+ if (nErr)
+ return nErr;
+ return aResult.GetResultError();
+}
+
+USHORT ScFormulaCell::GetRawError()
+{
+ USHORT nErr = pCode->GetCodeError();
+ if (nErr)
+ return nErr;
+ return aResult.GetResultError();
+}
+
+BOOL ScFormulaCell::HasOneReference( ScRange& r ) const
+{
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ if( p && !pCode->GetNextReferenceRPN() ) // nur eine!
+ {
+ p->CalcAbsIfRel( aPos );
+ SingleDoubleRefProvider aProv( *p );
+ r.aStart.Set( aProv.Ref1.nCol,
+ aProv.Ref1.nRow,
+ aProv.Ref1.nTab );
+ r.aEnd.Set( aProv.Ref2.nCol,
+ aProv.Ref2.nRow,
+ aProv.Ref2.nTab );
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+bool
+ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
+{
+ /* If there appears just one reference in the formula, it's the same
+ as HasOneReference(). If there are more of them, they can denote
+ one range if they are (sole) arguments of one function.
+ Union of these references must form one range and their
+ intersection must be empty set.
+ */
+
+ // Detect the simple case of exactly one reference in advance without all
+ // overhead.
+ // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
+ // work again, where the function does not have only references.
+ if (HasOneReference( rRange))
+ return true;
+
+ pCode->Reset();
+ // Get first reference, if any
+ ScToken* const pFirstReference(
+ dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN()));
+ if (pFirstReference)
+ {
+ // Collect all consecutive references, starting by the one
+ // already found
+ std::deque<ScToken*> aReferences;
+ aReferences.push_back(pFirstReference);
+ FormulaToken* pToken(pCode->NextRPN());
+ FormulaToken* pFunction(0);
+ while (pToken)
+ {
+ if (lcl_isReference(*pToken))
+ {
+ aReferences.push_back(dynamic_cast<ScToken*>(pToken));
+ pToken = pCode->NextRPN();
+ }
+ else
+ {
+ if (pToken->IsFunction())
+ {
+ pFunction = pToken;
+ }
+ break;
+ }
+ }
+ if (pFunction && !pCode->GetNextReferenceRPN()
+ && (pFunction->GetParamCount() == aReferences.size()))
+ {
+ return lcl_refListFormsOneRange(aPos, aReferences, rRange);
+ }
+ }
+ return false;
+}
+
+BOOL ScFormulaCell::HasRelNameReference() const
+{
+ pCode->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
+ {
+ if ( t->GetSingleRef().IsRelName() ||
+ (t->GetType() == formula::svDoubleRef &&
+ t->GetDoubleRef().Ref2.IsRelName()) )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL ScFormulaCell::HasColRowName() const
+{
+ pCode->Reset();
+ return (pCode->GetNextColRowName() != NULL);
+}
+
+void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode,
+ const ScRange& r,
+ SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
+ ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
+{
+ SCCOL nCol1 = r.aStart.Col();
+ SCROW nRow1 = r.aStart.Row();
+ SCTAB nTab1 = r.aStart.Tab();
+ SCCOL nCol2 = r.aEnd.Col();
+ SCROW nRow2 = r.aEnd.Row();
+ SCTAB nTab2 = r.aEnd.Tab();
+ SCCOL nCol = aPos.Col();
+ SCROW nRow = aPos.Row();
+ SCTAB nTab = aPos.Tab();
+ ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
+ if ( pUndoCellPos )
+ aUndoPos = *pUndoCellPos;
+ ScAddress aOldPos( aPos );
+// BOOL bPosChanged = FALSE; // ob diese Zelle bewegt wurde
+ BOOL bIsInsert = FALSE;
+ if (eUpdateRefMode == URM_INSDEL)
+ {
+ bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0);
+ if ( nDx && nRow >= nRow1 && nRow <= nRow2 &&
+ nTab >= nTab1 && nTab <= nTab2 )
+ {
+ if (nCol >= nCol1)
+ {
+ nCol = sal::static_int_cast<SCCOL>( nCol + nDx );
+ if ((SCsCOL) nCol < 0)
+ nCol = 0;
+ else if ( nCol > MAXCOL )
+ nCol = MAXCOL;
+ aPos.SetCol( nCol );
+// bPosChanged = TRUE;
+ }
+ }
+ if ( nDy && nCol >= nCol1 && nCol <= nCol2 &&
+ nTab >= nTab1 && nTab <= nTab2 )
+ {
+ if (nRow >= nRow1)
+ {
+ nRow = sal::static_int_cast<SCROW>( nRow + nDy );
+ if ((SCsROW) nRow < 0)
+ nRow = 0;
+ else if ( nRow > MAXROW )
+ nRow = MAXROW;
+ aPos.SetRow( nRow );
+// bPosChanged = TRUE;
+ }
+ }
+ if ( nDz && nCol >= nCol1 && nCol <= nCol2 &&
+ nRow >= nRow1 && nRow <= nRow2 )
+ {
+ if (nTab >= nTab1)
+ {
+ SCTAB nMaxTab = pDocument->GetTableCount() - 1;
+ nTab = sal::static_int_cast<SCTAB>( nTab + nDz );
+ if ((SCsTAB) nTab < 0)
+ nTab = 0;
+ else if ( nTab > nMaxTab )
+ nTab = nMaxTab;
+ aPos.SetTab( nTab );
+// bPosChanged = TRUE;
+ }
+ }
+ }
+ else if ( r.In( aPos ) )
+ {
+ aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
+// bPosChanged = TRUE;
+ }
+
+ BOOL bHasRefs = FALSE;
+ BOOL bHasColRowNames = FALSE;
+ BOOL bOnRefMove = FALSE;
+ if ( !pDocument->IsClipOrUndo() )
+ {
+ pCode->Reset();
+ bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
+ if ( !bHasRefs || eUpdateRefMode == URM_COPY )
+ {
+ pCode->Reset();
+ bHasColRowNames = (pCode->GetNextColRowName() != NULL);
+ bHasRefs = bHasRefs || bHasColRowNames;
+ }
+ bOnRefMove = pCode->IsRecalcModeOnRefMove();
+ }
+ if( bHasRefs || bOnRefMove )
+ {
+ ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
+ BOOL bValChanged;
+ ScRangeData* pRangeData;
+ BOOL bRangeModified; // any range, not only shared formula
+ BOOL bRefSizeChanged;
+ if ( bHasRefs )
+ {
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r,
+ nDx, nDy, nDz,
+ bValChanged, bRefSizeChanged);
+ bRangeModified = aComp.HasModifiedRange();
+ }
+ else
+ {
+ bValChanged = FALSE;
+ pRangeData = NULL;
+ bRangeModified = FALSE;
+ bRefSizeChanged = FALSE;
+ }
+ if ( bOnRefMove )
+ bOnRefMove = (bValChanged || (aPos != aOldPos));
+ // Cell may reference itself, e.g. ocColumn, ocRow without parameter
+
+ BOOL bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo;
+ if ( bHasRefs )
+ {
+ // Upon Insert ColRowNames have to be recompiled in case the
+ // insertion occurs right in front of the range.
+ bColRowNameCompile =
+ (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0));
+ if ( bColRowNameCompile )
+ {
+ bColRowNameCompile = FALSE;
+ ScToken* t;
+ ScRangePairList* pColList = pDocument->GetColNameRanges();
+ ScRangePairList* pRowList = pDocument->GetRowNameRanges();
+ pCode->Reset();
+ while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL )
+ {
+ ScSingleRefData& rRef = t->GetSingleRef();
+ if ( nDy > 0 && rRef.IsColRel() )
+ { // ColName
+ rRef.CalcAbsIfRel( aPos );
+ ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
+ ScRangePair* pR = pColList->Find( aAdr );
+ if ( pR )
+ { // definiert
+ if ( pR->GetRange(1).aStart.Row() == nRow1 )
+ bColRowNameCompile = TRUE;
+ }
+ else
+ { // on the fly
+ if ( rRef.nRow + 1 == nRow1 )
+ bColRowNameCompile = TRUE;
+ }
+ }
+ if ( nDx > 0 && rRef.IsRowRel() )
+ { // RowName
+ rRef.CalcAbsIfRel( aPos );
+ ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
+ ScRangePair* pR = pRowList->Find( aAdr );
+ if ( pR )
+ { // definiert
+ if ( pR->GetRange(1).aStart.Col() == nCol1 )
+ bColRowNameCompile = TRUE;
+ }
+ else
+ { // on the fly
+ if ( rRef.nCol + 1 == nCol1 )
+ bColRowNameCompile = TRUE;
+ }
+ }
+ }
+ }
+ else if ( eUpdateRefMode == URM_MOVE )
+ { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde
+ // oder diese Zelle auf einen zeigt und verschoben wurde
+ bColRowNameCompile = bCompile; // evtl. aus Copy-ctor
+ if ( !bColRowNameCompile )
+ {
+ BOOL bMoved = (aPos != aOldPos);
+ pCode->Reset();
+ ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
+ if ( t && bMoved )
+ bColRowNameCompile = TRUE;
+ while ( t && !bColRowNameCompile )
+ {
+ ScSingleRefData& rRef = t->GetSingleRef();
+ rRef.CalcAbsIfRel( aPos );
+ if ( rRef.Valid() )
+ {
+ ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
+ if ( r.In( aAdr ) )
+ bColRowNameCompile = TRUE;
+ }
+ t = static_cast<ScToken*>(pCode->GetNextColRowName());
+ }
+ }
+ }
+ else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
+ {
+ bColRowNameCompile = TRUE;
+ }
+ ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
+ if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
+ bInDeleteUndo = TRUE;
+ else
+ bInDeleteUndo = FALSE;
+ // RelNameRefs are always moved
+ bHasRelName = HasRelNameReference();
+ // Reference changed and new listening needed?
+ // Except in Insert/Delete without specialties.
+ bNewListening = (bRangeModified || pRangeData || bColRowNameCompile
+ || (bValChanged && (eUpdateRefMode != URM_INSDEL ||
+ bInDeleteUndo || bRefSizeChanged)) ||
+ (bHasRelName && eUpdateRefMode != URM_COPY))
+ // #i36299# Don't duplicate action during cut&paste / drag&drop
+ // on a cell in the range moved, start/end listeners is done
+ // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
+ && !(eUpdateRefMode == URM_MOVE &&
+ pDocument->IsInsertingFromOtherDoc() && r.In(aPos));
+ if ( bNewListening )
+ EndListeningTo( pDocument, pOld, aOldPos );
+ }
+ else
+ {
+ bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo =
+ FALSE;
+ }
+
+ BOOL bNeedDirty;
+ // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames
+ if ( bRangeModified || pRangeData || bColRowNameCompile ||
+ (bValChanged && eUpdateRefMode != URM_COPY &&
+ (eUpdateRefMode != URM_MOVE || bHasRelName) &&
+ (!bIsInsert || bHasRelName || bInDeleteUndo ||
+ bRefSizeChanged)) || bOnRefMove)
+ bNeedDirty = TRUE;
+ else
+ bNeedDirty = FALSE;
+ if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove))
+ {
+ // Copy the cell to aUndoPos, which is its current position in the document,
+ // so this works when UpdateReference is called before moving the cells
+ // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
+ // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
+
+ // If there is already a formula cell in the undo document, don't overwrite it,
+ // the first (oldest) is the important cell.
+ if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA )
+ {
+ ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos,
+ pOld, eTempGrammar, cMatrixFlag );
+ pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
+ pUndoDoc->PutCell( aUndoPos, pFCell );
+ }
+ }
+ bValChanged = FALSE;
+ if ( pRangeData )
+ { // Replace shared formula with own formula
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = pRangeData->GetCode()->Clone();
+ // #i18937# #i110008# call MoveRelWrap, but with the old position
+ ScCompiler::MoveRelWrap(*pCode, pDocument, aOldPos, pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
+ ScCompiler aComp2(pDocument, aPos, *pCode);
+ aComp2.SetGrammar(pDocument->GetGrammar());
+ aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r,
+ nDx, nDy, nDz );
+ bValChanged = TRUE;
+ bNeedDirty = TRUE;
+ }
+ if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
+ {
+ CompileTokenArray( bNewListening ); // kein Listening
+ bNeedDirty = TRUE;
+ }
+ if ( !bInDeleteUndo )
+ { // In ChangeTrack Delete-Reject listeners are established in
+ // InsertCol/InsertRow
+ if ( bNewListening )
+ {
+ if ( eUpdateRefMode == URM_INSDEL )
+ {
+ // Inserts/Deletes re-establish listeners after all
+ // UpdateReference calls.
+ // All replaced shared formula listeners have to be
+ // established after an Insert or Delete. Do nothing here.
+ SetNeedsListening( TRUE);
+ }
+ else
+ StartListeningTo( pDocument );
+ }
+ }
+ if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) )
+ { // Referenzen abgeschnitten, ungueltig o.ae.?
+ BOOL bOldAutoCalc = pDocument->GetAutoCalc();
+ // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen
+ pDocument->SetAutoCalc( FALSE );
+ SetDirty();
+ pDocument->SetAutoCalc( bOldAutoCalc );
+ }
+
+ delete pOld;
+ }
+
+ pCode->Reset();
+ for ( formula::FormulaToken* t = pCode->GetNextReferenceOrName(); t; t = pCode->GetNextReferenceOrName() )
+ {
+ StackVar sv = t->GetType();
+ if (sv == svExternalSingleRef || sv == svExternalDoubleRef || sv == svExternalName)
+ {
+ pDocument->GetExternalRefManager()->updateRefCell(aOldPos, aPos, eUpdateRefMode == URM_COPY);
+ break;
+ }
+ }
+}
+
+void ScFormulaCell::UpdateInsertTab(SCTAB nTable)
+{
+ BOOL bPosChanged = ( aPos.Tab() >= nTable ? TRUE : FALSE );
+ pCode->Reset();
+ if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
+ {
+ EndListeningTo( pDocument );
+ // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab !
+ if ( bPosChanged )
+ aPos.IncTab();
+ ScRangeData* pRangeData;
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateInsertTab( nTable, FALSE );
+ if (pRangeData) // Shared Formula gegen echte Formel
+ { // austauschen
+ BOOL bRefChanged;
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = new ScTokenArray( *pRangeData->GetCode() );
+ ScCompiler aComp2(pDocument, aPos, *pCode);
+ aComp2.SetGrammar(pDocument->GetGrammar());
+ aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
+ aComp2.UpdateInsertTab( nTable, FALSE );
+ // If the shared formula contained a named range/formula containing
+ // an absolute reference to a sheet, those have to be readjusted.
+ aComp2.UpdateDeleteTab( nTable, FALSE, TRUE, bRefChanged );
+ bCompile = TRUE;
+ }
+ // kein StartListeningTo weil pTab[nTab] noch nicht existiert!
+ }
+ else if ( bPosChanged )
+ aPos.IncTab();
+}
+
+BOOL ScFormulaCell::UpdateDeleteTab(SCTAB nTable, BOOL bIsMove)
+{
+ BOOL bRefChanged = FALSE;
+ BOOL bPosChanged = ( aPos.Tab() > nTable ? TRUE : FALSE );
+ pCode->Reset();
+ if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
+ {
+ EndListeningTo( pDocument );
+ // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab !
+ if ( bPosChanged )
+ aPos.IncTab(-1);
+ ScRangeData* pRangeData;
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, FALSE, bRefChanged);
+ if (pRangeData) // Shared Formula gegen echte Formel
+ { // austauschen
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = pRangeData->GetCode()->Clone();
+ ScCompiler aComp2(pDocument, aPos, *pCode);
+ aComp2.SetGrammar(pDocument->GetGrammar());
+ aComp2.CompileTokenArray();
+ aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
+ aComp2.UpdateDeleteTab( nTable, FALSE, FALSE, bRefChanged );
+ // If the shared formula contained a named range/formula containing
+ // an absolute reference to a sheet, those have to be readjusted.
+ aComp2.UpdateInsertTab( nTable,TRUE );
+ // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein
+ bRefChanged = TRUE;
+ bCompile = TRUE;
+ }
+ // kein StartListeningTo weil pTab[nTab] noch nicht korrekt!
+ }
+ else if ( bPosChanged )
+ aPos.IncTab(-1);
+
+ return bRefChanged;
+}
+
+void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
+{
+ pCode->Reset();
+ if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
+ {
+ EndListeningTo( pDocument );
+ // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab !
+ aPos.SetTab( nTabNo );
+ ScRangeData* pRangeData;
+ ScCompiler aComp(pDocument, aPos, *pCode);
+ aComp.SetGrammar(pDocument->GetGrammar());
+ pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, FALSE );
+ if (pRangeData) // Shared Formula gegen echte Formel
+ { // austauschen
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = pRangeData->GetCode()->Clone();
+ ScCompiler aComp2(pDocument, aPos, *pCode);
+ aComp2.SetGrammar(pDocument->GetGrammar());
+ aComp2.CompileTokenArray();
+ aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
+ aComp2.UpdateMoveTab( nOldPos, nNewPos, TRUE );
+ bCompile = TRUE;
+ }
+ // kein StartListeningTo weil pTab[nTab] noch nicht korrekt!
+ }
+ else
+ aPos.SetTab( nTabNo );
+}
+
+void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
+{
+ if( !pDocument->IsClipOrUndo() )
+ {
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ while( p )
+ {
+ ScSingleRefData& rRef1 = p->GetSingleRef();
+ if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab )
+ rRef1.nTab++;
+ if( p->GetType() == formula::svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
+ if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab )
+ rRef2.nTab++;
+ }
+ p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ }
+ }
+}
+
+BOOL ScFormulaCell::TestTabRefAbs(SCTAB nTable)
+{
+ BOOL bRet = FALSE;
+ if( !pDocument->IsClipOrUndo() )
+ {
+ pCode->Reset();
+ ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ while( p )
+ {
+ ScSingleRefData& rRef1 = p->GetSingleRef();
+ if( !rRef1.IsTabRel() )
+ {
+ if( (SCsTAB) nTable != rRef1.nTab )
+ bRet = TRUE;
+ else if (nTable != aPos.Tab())
+ rRef1.nTab = aPos.Tab();
+ }
+ if( p->GetType() == formula::svDoubleRef )
+ {
+ ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
+ if( !rRef2.IsTabRel() )
+ {
+ if( (SCsTAB) nTable != rRef2.nTab )
+ bRet = TRUE;
+ else if (nTable != aPos.Tab())
+ rRef2.nTab = aPos.Tab();
+ }
+ }
+ p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
+ }
+ }
+ return bRet;
+}
+
+void ScFormulaCell::UpdateCompile( BOOL bForceIfNameInUse )
+{
+ if ( bForceIfNameInUse && !bCompile )
+ bCompile = pCode->HasNameOrColRowName();
+ if ( bCompile )
+ pCode->SetCodeError( 0 ); // make sure it will really be compiled
+ CompileTokenArray();
+}
+
+// Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen
+
+void ScFormulaCell::TransposeReference()
+{
+ BOOL bFound = FALSE;
+ pCode->Reset();
+ ScToken* t;
+ while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
+ {
+ ScSingleRefData& rRef1 = t->GetSingleRef();
+ if ( rRef1.IsColRel() && rRef1.IsRowRel() )
+ {
+ BOOL bDouble = (t->GetType() == formula::svDoubleRef);
+ ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1);
+ if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
+ {
+ INT16 nTemp;
+
+ nTemp = rRef1.nRelCol;
+ rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow);
+ rRef1.nRelRow = static_cast<SCROW>(nTemp);
+
+ if ( bDouble )
+ {
+ nTemp = rRef2.nRelCol;
+ rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
+ rRef2.nRelRow = static_cast<SCROW>(nTemp);
+ }
+
+ bFound = TRUE;
+ }
+ }
+ }
+
+ if (bFound)
+ bCompile = TRUE;
+}
+
+void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
+ ScDocument* pUndoDoc )
+{
+ EndListeningTo( pDocument );
+
+ ScAddress aOldPos = aPos;
+ BOOL bPosChanged = FALSE; // ob diese Zelle bewegt wurde
+
+ ScRange aDestRange( rDest, ScAddress(
+ static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
+ static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
+ rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
+ if ( aDestRange.In( aOldPos ) )
+ {
+ // Position zurueckrechnen
+ SCsCOL nRelPosX = aOldPos.Col();
+ SCsROW nRelPosY = aOldPos.Row();
+ SCsTAB nRelPosZ = aOldPos.Tab();
+ ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
+ aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
+ bPosChanged = TRUE;
+ }
+
+ ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
+ BOOL bRefChanged = FALSE;
+ ScToken* t;
+
+ ScRangeData* pShared = NULL;
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
+ {
+ if( t->GetOpCode() == ocName )
+ {
+ ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
+ if (pName)
+ {
+ if (pName->IsModified())
+ bRefChanged = TRUE;
+ if (pName->HasType(RT_SHAREDMOD))
+ pShared = pName;
+ }
+ }
+ else if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aOldPos );
+ BOOL bMod;
+ { // own scope for SingleDoubleRefModifier dtor if SingleRef
+ SingleDoubleRefModifier aMod( *t );
+ ScComplexRefData& rRef = aMod.Ref();
+ bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
+ rDest, rRef ) != UR_NOTHING || bPosChanged);
+ }
+ if ( bMod )
+ {
+ t->CalcRelFromAbs( aPos );
+ bRefChanged = TRUE;
+ }
+ }
+ }
+
+ if (pShared) // Shared Formula gegen echte Formel austauschen
+ {
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = new ScTokenArray( *pShared->GetCode() );
+ bRefChanged = TRUE;
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
+ {
+ if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aOldPos );
+ BOOL bMod;
+ { // own scope for SingleDoubleRefModifier dtor if SingleRef
+ SingleDoubleRefModifier aMod( *t );
+ ScComplexRefData& rRef = aMod.Ref();
+ bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
+ rDest, rRef ) != UR_NOTHING || bPosChanged);
+ }
+ if ( bMod )
+ t->CalcRelFromAbs( aPos );
+ }
+ }
+ }
+
+ if (bRefChanged)
+ {
+ if (pUndoDoc)
+ {
+ ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld,
+ eTempGrammar, cMatrixFlag);
+ pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
+ pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell );
+ }
+
+ bCompile = TRUE;
+ CompileTokenArray(); // ruft auch StartListeningTo
+ SetDirty();
+ }
+ else
+ StartListeningTo( pDocument ); // Listener wie vorher
+
+ delete pOld;
+}
+
+void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ EndListeningTo( pDocument );
+
+ BOOL bRefChanged = FALSE;
+ ScToken* t;
+ ScRangeData* pShared = NULL;
+
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
+ {
+ if( t->GetOpCode() == ocName )
+ {
+ ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
+ if (pName)
+ {
+ if (pName->IsModified())
+ bRefChanged = TRUE;
+ if (pName->HasType(RT_SHAREDMOD))
+ pShared = pName;
+ }
+ }
+ else if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aPos );
+ BOOL bMod;
+ { // own scope for SingleDoubleRefModifier dtor if SingleRef
+ SingleDoubleRefModifier aMod( *t );
+ ScComplexRefData& rRef = aMod.Ref();
+ bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
+ rRef ) != UR_NOTHING);
+ }
+ if ( bMod )
+ {
+ t->CalcRelFromAbs( aPos );
+ bRefChanged = TRUE;
+ }
+ }
+ }
+
+ if (pShared) // Shared Formula gegen echte Formel austauschen
+ {
+ pDocument->RemoveFromFormulaTree( this ); // update formula count
+ delete pCode;
+ pCode = new ScTokenArray( *pShared->GetCode() );
+ bRefChanged = TRUE;
+ pCode->Reset();
+ while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
+ {
+ if( t->GetType() != svIndex )
+ {
+ t->CalcAbsIfRel( aPos );
+ BOOL bMod;
+ { // own scope for SingleDoubleRefModifier dtor if SingleRef
+ SingleDoubleRefModifier aMod( *t );
+ ScComplexRefData& rRef = aMod.Ref();
+ bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
+ rRef ) != UR_NOTHING);
+ }
+ if ( bMod )
+ t->CalcRelFromAbs( aPos );
+ }
+ }
+ }
+
+ if (bRefChanged)
+ {
+ bCompile = TRUE;
+ CompileTokenArray(); // ruft auch StartListeningTo
+ SetDirty();
+ }
+ else
+ StartListeningTo( pDocument ); // Listener wie vorher
+}
+
+BOOL lcl_IsRangeNameInUse(USHORT nIndex, ScTokenArray* pCode, ScRangeName* pNames)
+{
+ for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ if (p->GetIndex() == nIndex)
+ return TRUE;
+ else
+ {
+ // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#)
+ ScRangeData* pSubName = pNames->FindIndex(p->GetIndex());
+ if (pSubName && lcl_IsRangeNameInUse(nIndex,
+ pSubName->GetCode(), pNames))
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+BOOL ScFormulaCell::IsRangeNameInUse(USHORT nIndex) const
+{
+ return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() );
+}
+
+void lcl_FindRangeNamesInUse(std::set<USHORT>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
+{
+ for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
+ {
+ if (p->GetOpCode() == ocName)
+ {
+ USHORT nTokenIndex = p->GetIndex();
+ rIndexes.insert( nTokenIndex );
+
+ ScRangeData* pSubName = pNames->FindIndex(p->GetIndex());
+ if (pSubName)
+ lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
+ }
+ }
+}
+
+void ScFormulaCell::FindRangeNamesInUse(std::set<USHORT>& rIndexes) const
+{
+ lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
+}
+
+void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap )
+{
+ for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
+ {
+ if( p->GetOpCode() == ocName )
+ {
+ sal_uInt16 nIndex = p->GetIndex();
+ ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex);
+ sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second;
+ if ( nIndex != nNewIndex )
+ {
+ p->SetIndex( nNewIndex );
+ bCompile = TRUE;
+ }
+ }
+ }
+ if( bCompile )
+ CompileTokenArray();
+}
+
+void ScFormulaCell::CompileDBFormula()
+{
+ for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
+ {
+ if ( p->GetOpCode() == ocDBArea
+ || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) )
+ {
+ bCompile = TRUE;
+ CompileTokenArray();
+ SetDirty();
+ break;
+ }
+ }
+}
+
+void ScFormulaCell::CompileDBFormula( BOOL bCreateFormulaString )
+{
+ // zwei Phasen, muessen (!) nacheinander aufgerufen werden:
+ // 1. FormelString mit alten Namen erzeugen
+ // 2. FormelString mit neuen Namen kompilieren
+ if ( bCreateFormulaString )
+ {
+ BOOL bRecompile = FALSE;
+ pCode->Reset();
+ for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
+ {
+ switch ( p->GetOpCode() )
+ {
+ case ocBad: // DB-Bereich evtl. zugefuegt
+ case ocColRowName: // #36762# falls Namensgleichheit
+ case ocDBArea: // DB-Bereich
+ bRecompile = TRUE;
+ break;
+ case ocName:
+ if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
+ bRecompile = TRUE; // DB-Bereich
+ break;
+ default:
+ ; // nothing
+ }
+ }
+ if ( bRecompile )
+ {
+ String aFormula;
+ GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ if ( GetMatrixFlag() != MM_NONE && aFormula.Len() )
+ {
+ if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' )
+ aFormula.Erase( aFormula.Len()-1 , 1 );
+ if ( aFormula.GetChar(0) == '{' )
+ aFormula.Erase( 0, 1 );
+ }
+ EndListeningTo( pDocument );
+ pDocument->RemoveFromFormulaTree( this );
+ pCode->Clear();
+ SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ }
+ }
+ else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
+ {
+ Compile( aResult.GetHybridFormula(), FALSE, eTempGrammar );
+ aResult.SetToken( NULL);
+ SetDirty();
+ }
+}
+
+void ScFormulaCell::CompileNameFormula( BOOL bCreateFormulaString )
+{
+ // zwei Phasen, muessen (!) nacheinander aufgerufen werden:
+ // 1. FormelString mit alten RangeNames erzeugen
+ // 2. FormelString mit neuen RangeNames kompilieren
+ if ( bCreateFormulaString )
+ {
+ BOOL bRecompile = FALSE;
+ pCode->Reset();
+ for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
+ {
+ switch ( p->GetOpCode() )
+ {
+ case ocBad: // RangeName evtl. zugefuegt
+ case ocColRowName: // #36762# falls Namensgleichheit
+ bRecompile = TRUE;
+ break;
+ default:
+ if ( p->GetType() == svIndex )
+ bRecompile = TRUE; // RangeName
+ }
+ }
+ if ( bRecompile )
+ {
+ String aFormula;
+ GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ if ( GetMatrixFlag() != MM_NONE && aFormula.Len() )
+ {
+ if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' )
+ aFormula.Erase( aFormula.Len()-1 , 1 );
+ if ( aFormula.GetChar(0) == '{' )
+ aFormula.Erase( 0, 1 );
+ }
+ EndListeningTo( pDocument );
+ pDocument->RemoveFromFormulaTree( this );
+ pCode->Clear();
+ SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
+ }
+ }
+ else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
+ {
+ Compile( aResult.GetHybridFormula(), FALSE, eTempGrammar );
+ aResult.SetToken( NULL);
+ SetDirty();
+ }
+}
+
+void ScFormulaCell::CompileColRowNameFormula()
+{
+ pCode->Reset();
+ for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
+ {
+ if ( p->GetOpCode() == ocColRowName )
+ {
+ bCompile = TRUE;
+ CompileTokenArray();
+ SetDirty();
+ break;
+ }
+ }
+}
+
+// ============================================================================
+