merge the formfield patch from ooo-build
[ooovba.git] / sc / source / core / data / cell2.cxx
blob4b8c7dade5d7d64e7aa94edc6237bef15fad0553
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cell2.cxx,v $
10 * $Revision: 1.34.102.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 // INCLUDE ---------------------------------------------------------------
37 #include <algorithm>
38 #include <deque>
40 #include <boost/bind.hpp>
42 #include <vcl/mapmod.hxx>
43 #include <svx/editobj.hxx>
44 #include <svx/editstat.hxx>
46 #include "cell.hxx"
47 #include "compiler.hxx"
48 #include "formula/errorcodes.hxx"
49 #include "document.hxx"
50 #include "rangenam.hxx"
51 #include "rechead.hxx"
52 #include "refupdat.hxx"
53 #include "scmatrix.hxx"
54 #include "editutil.hxx"
55 #include "chgtrack.hxx"
56 #include "externalrefmgr.hxx"
57 #include "scitems.hxx"
58 #include "patattr.hxx"
60 using namespace formula;
62 // STATIC DATA -----------------------------------------------------------
64 #ifdef USE_MEMPOOL
65 const USHORT nMemPoolEditCell = (0x1000 - 64) / sizeof(ScNoteCell);
66 IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell, nMemPoolEditCell, nMemPoolEditCell )
67 #endif
69 // ============================================================================
71 ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP,
72 const SfxItemPool* pFromPool ) :
73 ScBaseCell( CELLTYPE_EDIT ),
74 pString( NULL ),
75 pDoc( pDocP )
77 SetTextObject( pObject, pFromPool );
80 ScEditCell::ScEditCell( const ScEditCell& rCell, ScDocument& rDoc ) :
81 ScBaseCell( rCell ),
82 pString( NULL ),
83 pDoc( &rDoc )
85 SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() );
88 ScEditCell::ScEditCell( const String& rString, ScDocument* pDocP ) :
89 ScBaseCell( CELLTYPE_EDIT ),
90 pString( NULL ),
91 pDoc( pDocP )
93 DBG_ASSERT( rString.Search('\n') != STRING_NOTFOUND ||
94 rString.Search(CHAR_CR) != STRING_NOTFOUND,
95 "EditCell mit einfachem Text !?!?" );
97 EditEngine& rEngine = pDoc->GetEditEngine();
98 rEngine.SetText( rString );
99 pData = rEngine.CreateTextObject();
102 ScEditCell::~ScEditCell()
104 delete pData;
105 delete pString;
107 #ifdef DBG_UTIL
108 eCellType = CELLTYPE_DESTROYED;
109 #endif
112 void ScEditCell::SetData( const EditTextObject* pObject,
113 const SfxItemPool* pFromPool )
115 if ( pString )
117 delete pString;
118 pString = NULL;
120 delete pData;
121 SetTextObject( pObject, pFromPool );
124 void ScEditCell::GetData( const EditTextObject*& rpObject ) const
126 rpObject = pData;
129 void ScEditCell::GetString( String& rString ) const
131 if ( pString )
132 rString = *pString;
133 else if ( pData )
135 // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine
136 EditEngine& rEngine = pDoc->GetEditEngine();
137 rEngine.SetText( *pData );
138 rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs
139 // cache short strings for formulas
140 if ( rString.Len() < 256 )
141 ((ScEditCell*)this)->pString = new String( rString ); //! non-const
143 else
144 rString.Erase();
147 void ScEditCell::RemoveCharAttribs( const ScPatternAttr& rAttr )
149 const struct {
150 USHORT nAttrType;
151 USHORT nCharType;
152 } AttrTypeMap[] = {
153 { ATTR_FONT, EE_CHAR_FONTINFO },
154 { ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT },
155 { ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT },
156 { ATTR_FONT_COLOR, EE_CHAR_COLOR }
158 USHORT nMapCount = sizeof(AttrTypeMap) / sizeof(AttrTypeMap[0]);
160 const SfxItemSet& rSet = rAttr.GetItemSet();
161 const SfxPoolItem* pItem;
162 for (USHORT i = 0; i < nMapCount; ++i)
164 if ( rSet.GetItemState(AttrTypeMap[i].nAttrType, false, &pItem) == SFX_ITEM_SET )
165 pData->RemoveCharAttribs(AttrTypeMap[i].nCharType);
169 void ScEditCell::SetTextObject( const EditTextObject* pObject,
170 const SfxItemPool* pFromPool )
172 if ( pObject )
174 if ( pFromPool && pDoc->GetEditPool() == pFromPool )
175 pData = pObject->Clone();
176 else
177 { //! anderer Pool
178 // Leider gibt es keinen anderen Weg, um den Pool umzuhaengen,
179 // als das Object durch eine entsprechende Engine zu schleusen..
180 EditEngine& rEngine = pDoc->GetEditEngine();
181 if ( pObject->HasOnlineSpellErrors() )
183 ULONG nControl = rEngine.GetControlWord();
184 const ULONG nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS;
185 BOOL bNewControl = ( (nControl & nSpellControl) != nSpellControl );
186 if ( bNewControl )
187 rEngine.SetControlWord( nControl | nSpellControl );
188 rEngine.SetText( *pObject );
189 pData = rEngine.CreateTextObject();
190 if ( bNewControl )
191 rEngine.SetControlWord( nControl );
193 else
195 rEngine.SetText( *pObject );
196 pData = rEngine.CreateTextObject();
200 else
201 pData = NULL;
204 ScEditDataArray::ScEditDataArray()
208 ScEditDataArray::~ScEditDataArray()
212 void ScEditDataArray::AddItem(SCTAB nTab, SCCOL nCol, SCROW nRow,
213 EditTextObject* pOldData, EditTextObject* pNewData)
215 maArray.push_back(Item(nTab, nCol, nRow, pOldData, pNewData));
218 const ScEditDataArray::Item* ScEditDataArray::First()
220 maIter = maArray.begin();
221 if (maIter == maArray.end())
222 return NULL;
223 return &(*maIter++);
226 const ScEditDataArray::Item* ScEditDataArray::Next()
228 if (maIter == maArray.end())
229 return NULL;
230 return &(*maIter++);
233 // ============================================================================
235 ScEditDataArray::Item::Item(SCTAB nTab, SCCOL nCol, SCROW nRow,
236 EditTextObject* pOldData, EditTextObject* pNewData) :
237 mnTab(nTab),
238 mnCol(nCol),
239 mnRow(nRow)
241 mpOldData.reset(pOldData);
242 mpNewData.reset(pNewData);
245 ScEditDataArray::Item::~Item()
249 const EditTextObject* ScEditDataArray::Item::GetOldData() const
251 return mpOldData.get();
254 const EditTextObject* ScEditDataArray::Item::GetNewData() const
256 return mpNewData.get();
259 SCTAB ScEditDataArray::Item::GetTab() const
261 return mnTab;
264 SCCOL ScEditDataArray::Item::GetCol() const
266 return mnCol;
269 SCROW ScEditDataArray::Item::GetRow() const
271 return mnRow;
274 // ============================================================================
276 namespace
279 using std::deque;
281 typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&);
284 static SCCOLROW lcl_GetCol(const ScSingleRefData& rData)
286 return rData.nCol;
290 static SCCOLROW lcl_GetRow(const ScSingleRefData& rData)
292 return rData.nRow;
296 static SCCOLROW lcl_GetTab(const ScSingleRefData& rData)
298 return rData.nTab;
302 /** Check if both references span the same range in selected dimension.
304 static bool
305 lcl_checkRangeDimension(
306 const SingleDoubleRefProvider& rRef1,
307 const SingleDoubleRefProvider& rRef2,
308 const DimensionSelector aWhich)
310 return
311 aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1)
312 && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2);
316 static bool
317 lcl_checkRangeDimensions(
318 const SingleDoubleRefProvider& rRef1,
319 const SingleDoubleRefProvider& rRef2,
320 bool& bCol, bool& bRow, bool& bTab)
322 const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol));
323 const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow));
324 const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab));
326 // Test if exactly two dimensions are equal
327 if (!(bSameCols ^ bSameRows ^ bSameTabs)
328 && (bSameCols || bSameRows || bSameTabs))
330 bCol = !bSameCols;
331 bRow = !bSameRows;
332 bTab = !bSameTabs;
333 return true;
335 return false;
339 /** Check if references in given reference list can possibly
340 form a range. To do that, two of their dimensions must be the same.
342 static bool
343 lcl_checkRangeDimensions(
344 const deque<ScToken*>::const_iterator aBegin,
345 const deque<ScToken*>::const_iterator aEnd,
346 bool& bCol, bool& bRow, bool& bTab)
348 deque<ScToken*>::const_iterator aCur(aBegin);
349 ++aCur;
350 const SingleDoubleRefProvider aRef(**aBegin);
351 bool bOk(false);
353 const SingleDoubleRefProvider aRefCur(**aCur);
354 bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab);
356 while (bOk && aCur != aEnd)
358 const SingleDoubleRefProvider aRefCur(**aCur);
359 bool bColTmp(false);
360 bool bRowTmp(false);
361 bool bTabTmp(false);
362 bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
363 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
364 ++aCur;
367 if (bOk && aCur == aEnd)
369 bCol = bCol;
370 bRow = bRow;
371 bTab = bTab;
372 return true;
374 return false;
378 bool
379 lcl_lessReferenceBy(
380 const ScToken* const pRef1, const ScToken* const pRef2,
381 const DimensionSelector aWhich)
383 const SingleDoubleRefProvider rRef1(*pRef1);
384 const SingleDoubleRefProvider rRef2(*pRef2);
385 return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1);
389 /** Returns true if range denoted by token pRef2 starts immediately after
390 range denoted by token pRef1. Dimension, in which the comparison takes
391 place, is given by aWhich.
393 bool
394 lcl_isImmediatelyFollowing(
395 const ScToken* const pRef1, const ScToken* const pRef2,
396 const DimensionSelector aWhich)
398 const SingleDoubleRefProvider rRef1(*pRef1);
399 const SingleDoubleRefProvider rRef2(*pRef2);
400 return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1;
404 static bool
405 lcl_checkIfAdjacent(
406 const deque<ScToken*>& rReferences,
407 const DimensionSelector aWhich)
409 typedef deque<ScToken*>::const_iterator Iter;
410 Iter aBegin(rReferences.begin());
411 Iter aEnd(rReferences.end());
412 Iter aBegin1(aBegin);
413 ++aBegin1, --aEnd;
414 return std::equal(
415 aBegin, aEnd, aBegin1,
416 boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich));
420 static void
421 lcl_fillRangeFromRefList(
422 const deque<ScToken*>& rReferences, ScRange& rRange)
424 const ScSingleRefData aStart(
425 SingleDoubleRefProvider(*rReferences.front()).Ref1);
426 rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab);
427 const ScSingleRefData aEnd(
428 SingleDoubleRefProvider(*rReferences.back()).Ref2);
429 rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab);
433 static bool
434 lcl_refListFormsOneRange(
435 const ScAddress& aPos, deque<ScToken*>& rReferences,
436 ScRange& rRange)
438 std::for_each(
439 rReferences.begin(), rReferences.end(),
440 bind(&ScToken::CalcAbsIfRel, _1, aPos))
442 if (rReferences.size() == 1) {
443 lcl_fillRangeFromRefList(rReferences, rRange);
444 return true;
447 bool bCell(false);
448 bool bRow(false);
449 bool bTab(false);
450 if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(),
451 bCell, bRow, bTab))
453 DimensionSelector aWhich;
454 if (bCell)
456 aWhich = lcl_GetCol;
458 else if (bRow)
460 aWhich = lcl_GetRow;
462 else if (bTab)
464 aWhich = lcl_GetTab;
466 else
468 OSL_ENSURE(false, "lcl_checkRangeDimensions shouldn't allow that!");
469 aWhich = lcl_GetRow; // initialize to avoid warning
471 // Sort the references by start of range
472 std::sort(rReferences.begin(), rReferences.end(),
473 boost::bind(lcl_lessReferenceBy, _1, _2, aWhich));
474 if (lcl_checkIfAdjacent(rReferences, aWhich))
476 lcl_fillRangeFromRefList(rReferences, rRange);
477 return true;
480 return false;
484 bool lcl_isReference(const FormulaToken& rToken)
486 return
487 rToken.GetType() == svSingleRef ||
488 rToken.GetType() == svDoubleRef;
493 BOOL ScFormulaCell::IsEmpty()
495 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
496 Interpret();
497 return aResult.GetCellResultType() == formula::svEmptyCell;
500 BOOL ScFormulaCell::IsEmptyDisplayedAsString()
502 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
503 Interpret();
504 return aResult.IsEmptyDisplayedAsString();
507 BOOL ScFormulaCell::IsValue()
509 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
510 Interpret();
511 return aResult.IsValue();
514 double ScFormulaCell::GetValue()
516 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
517 Interpret();
518 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
519 !aResult.GetResultError())
520 return aResult.GetDouble();
521 return 0.0;
524 double ScFormulaCell::GetValueAlways()
526 // for goal seek: return result value even if error code is set
528 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
529 Interpret();
530 return aResult.GetDouble();
533 void ScFormulaCell::GetString( String& rString )
535 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
536 Interpret();
537 if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) &&
538 !aResult.GetResultError())
539 rString = aResult.GetString();
540 else
541 rString.Erase();
544 const ScMatrix* ScFormulaCell::GetMatrix()
546 if ( pDocument->GetAutoCalc() )
548 // Was stored !bDirty but an accompanying matrix cell was bDirty?
549 // => we need to get the matrix.
550 if (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is())
551 bDirty = TRUE;
552 if ( IsDirtyOrInTableOpDirty() )
553 Interpret();
555 return aResult.GetMatrix();
558 BOOL ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const
560 switch ( cMatrixFlag )
562 case MM_FORMULA :
563 rPos = aPos;
564 return TRUE;
565 // break;
566 case MM_REFERENCE :
568 pCode->Reset();
569 ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
570 if( t )
572 ScSingleRefData& rRef = t->GetSingleRef();
573 rRef.CalcAbsIfRel( aPos );
574 if ( rRef.Valid() )
576 rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab );
577 return TRUE;
581 break;
583 return FALSE;
588 Edge-Values:
591 4 16
594 innerhalb: 1
595 ausserhalb: 0
596 (reserviert: offen: 32)
599 USHORT ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos )
601 switch ( cMatrixFlag )
603 case MM_FORMULA :
604 case MM_REFERENCE :
606 static SCCOL nC;
607 static SCROW nR;
608 ScAddress aOrg;
609 if ( !GetMatrixOrigin( aOrg ) )
610 return 0; // dumm gelaufen..
611 if ( aOrg != rOrgPos )
612 { // erstes Mal oder andere Matrix als letztes Mal
613 rOrgPos = aOrg;
614 ScFormulaCell* pFCell;
615 if ( cMatrixFlag == MM_REFERENCE )
616 pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg );
617 else
618 pFCell = this; // this MM_FORMULA
619 // this gibt's nur einmal, kein Vergleich auf pFCell==this
620 if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA
621 && pFCell->cMatrixFlag == MM_FORMULA )
623 pFCell->GetMatColsRows( nC, nR );
624 if ( nC == 0 || nR == 0 )
625 { // aus altem Dokument geladen, neu erzeugen
626 nC = 1;
627 nR = 1;
628 ScAddress aTmpOrg;
629 ScBaseCell* pCell;
630 ScAddress aAdr( aOrg );
631 aAdr.IncCol();
632 BOOL bCont = TRUE;
635 pCell = pDocument->GetCell( aAdr );
636 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
637 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
638 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg )
640 nC++;
641 aAdr.IncCol();
643 else
644 bCont = FALSE;
645 } while ( bCont );
646 aAdr = aOrg;
647 aAdr.IncRow();
648 bCont = TRUE;
651 pCell = pDocument->GetCell( aAdr );
652 if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA
653 && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE
654 && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg )
656 nR++;
657 aAdr.IncRow();
659 else
660 bCont = FALSE;
661 } while ( bCont );
662 pFCell->SetMatColsRows( nC, nR );
665 else
667 #ifndef PRODUCT
668 String aTmp;
669 ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " );
670 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
671 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
672 aMsg += ", MatOrg: ";
673 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
674 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
675 DBG_ERRORFILE( aMsg.GetBuffer() );
676 #endif
677 return 0; // bad luck ...
680 // here we are, healthy and clean, somewhere in between
681 SCsCOL dC = aPos.Col() - aOrg.Col();
682 SCsROW dR = aPos.Row() - aOrg.Row();
683 USHORT nEdges = 0;
684 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
686 if ( dC == 0 )
687 nEdges |= 4; // linke Kante
688 if ( dC+1 == nC )
689 nEdges |= 16; // rechte Kante
690 if ( dR == 0 )
691 nEdges |= 8; // obere Kante
692 if ( dR+1 == nR )
693 nEdges |= 2; // untere Kante
694 if ( !nEdges )
695 nEdges = 1; // mittendrin
697 #ifndef PRODUCT
698 else
700 String aTmp;
701 ByteString aMsg( "broken Matrix, Pos: " );
702 aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
703 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
704 aMsg += ", MatOrg: ";
705 aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument );
706 aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US );
707 aMsg += ", MatCols: ";
708 aMsg += ByteString::CreateFromInt32( nC );
709 aMsg += ", MatRows: ";
710 aMsg += ByteString::CreateFromInt32( nR );
711 aMsg += ", DiffCols: ";
712 aMsg += ByteString::CreateFromInt32( dC );
713 aMsg += ", DiffRows: ";
714 aMsg += ByteString::CreateFromInt32( dR );
715 DBG_ERRORFILE( aMsg.GetBuffer() );
717 #endif
718 return nEdges;
719 // break;
721 default:
722 return 0;
726 USHORT ScFormulaCell::GetErrCode()
728 if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc())
729 Interpret();
730 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
731 * and not also abused for signaling other error conditions we could bail
732 * out even before attempting to interpret broken code. */
733 USHORT nErr = pCode->GetCodeError();
734 if (nErr)
735 return nErr;
736 return aResult.GetResultError();
739 USHORT ScFormulaCell::GetRawError()
741 USHORT nErr = pCode->GetCodeError();
742 if (nErr)
743 return nErr;
744 return aResult.GetResultError();
747 BOOL ScFormulaCell::HasOneReference( ScRange& r ) const
749 pCode->Reset();
750 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
751 if( p && !pCode->GetNextReferenceRPN() ) // nur eine!
753 p->CalcAbsIfRel( aPos );
754 SingleDoubleRefProvider aProv( *p );
755 r.aStart.Set( aProv.Ref1.nCol,
756 aProv.Ref1.nRow,
757 aProv.Ref1.nTab );
758 r.aEnd.Set( aProv.Ref2.nCol,
759 aProv.Ref2.nRow,
760 aProv.Ref2.nTab );
761 return TRUE;
763 else
764 return FALSE;
767 bool
768 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const
770 /* If there appears just one reference in the formula, it's the same
771 as HasOneReference(). If there are more of them, they can denote
772 one range if they are (sole) arguments of one function.
773 Union of these references must form one range and their
774 intersection must be empty set.
776 pCode->Reset();
777 // Get first reference, if any
778 ScToken* const pFirstReference(
779 dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN()));
780 if (pFirstReference)
782 // Collect all consecutive references, starting by the one
783 // already found
784 std::deque<ScToken*> aReferences;
785 aReferences.push_back(pFirstReference);
786 FormulaToken* pToken(pCode->NextRPN());
787 FormulaToken* pFunction(0);
788 while (pToken)
790 if (lcl_isReference(*pToken))
792 aReferences.push_back(dynamic_cast<ScToken*>(pToken));
793 pToken = pCode->NextRPN();
795 else
797 if (pToken->IsFunction())
799 pFunction = pToken;
801 break;
804 if (pFunction && !pCode->GetNextReferenceRPN()
805 && (pFunction->GetParamCount() == aReferences.size()))
807 return lcl_refListFormsOneRange(aPos, aReferences, rRange);
810 return false;
813 BOOL ScFormulaCell::HasRelNameReference() const
815 pCode->Reset();
816 ScToken* t;
817 while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
819 if ( t->GetSingleRef().IsRelName() ||
820 (t->GetType() == formula::svDoubleRef &&
821 t->GetDoubleRef().Ref2.IsRelName()) )
822 return TRUE;
824 return FALSE;
827 BOOL ScFormulaCell::HasColRowName() const
829 pCode->Reset();
830 return (pCode->GetNextColRowName() != NULL);
833 void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode,
834 const ScRange& r,
835 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
836 ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
838 SCCOL nCol1 = r.aStart.Col();
839 SCROW nRow1 = r.aStart.Row();
840 SCTAB nTab1 = r.aStart.Tab();
841 SCCOL nCol2 = r.aEnd.Col();
842 SCROW nRow2 = r.aEnd.Row();
843 SCTAB nTab2 = r.aEnd.Tab();
844 SCCOL nCol = aPos.Col();
845 SCROW nRow = aPos.Row();
846 SCTAB nTab = aPos.Tab();
847 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
848 if ( pUndoCellPos )
849 aUndoPos = *pUndoCellPos;
850 ScAddress aOldPos( aPos );
851 // BOOL bPosChanged = FALSE; // ob diese Zelle bewegt wurde
852 BOOL bIsInsert = FALSE;
853 if (eUpdateRefMode == URM_INSDEL)
855 bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0);
856 if ( nDx && nRow >= nRow1 && nRow <= nRow2 &&
857 nTab >= nTab1 && nTab <= nTab2 )
859 if (nCol >= nCol1)
861 nCol = sal::static_int_cast<SCCOL>( nCol + nDx );
862 if ((SCsCOL) nCol < 0)
863 nCol = 0;
864 else if ( nCol > MAXCOL )
865 nCol = MAXCOL;
866 aPos.SetCol( nCol );
867 // bPosChanged = TRUE;
870 if ( nDy && nCol >= nCol1 && nCol <= nCol2 &&
871 nTab >= nTab1 && nTab <= nTab2 )
873 if (nRow >= nRow1)
875 nRow = sal::static_int_cast<SCROW>( nRow + nDy );
876 if ((SCsROW) nRow < 0)
877 nRow = 0;
878 else if ( nRow > MAXROW )
879 nRow = MAXROW;
880 aPos.SetRow( nRow );
881 // bPosChanged = TRUE;
884 if ( nDz && nCol >= nCol1 && nCol <= nCol2 &&
885 nRow >= nRow1 && nRow <= nRow2 )
887 if (nTab >= nTab1)
889 SCTAB nMaxTab = pDocument->GetTableCount() - 1;
890 nTab = sal::static_int_cast<SCTAB>( nTab + nDz );
891 if ((SCsTAB) nTab < 0)
892 nTab = 0;
893 else if ( nTab > nMaxTab )
894 nTab = nMaxTab;
895 aPos.SetTab( nTab );
896 // bPosChanged = TRUE;
900 else if ( r.In( aPos ) )
902 aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz );
903 // bPosChanged = TRUE;
906 BOOL bHasRefs = FALSE;
907 BOOL bHasColRowNames = FALSE;
908 BOOL bOnRefMove = FALSE;
909 if ( !pDocument->IsClipOrUndo() )
911 pCode->Reset();
912 bHasRefs = (pCode->GetNextReferenceRPN() != NULL);
913 if ( !bHasRefs || eUpdateRefMode == URM_COPY )
915 pCode->Reset();
916 bHasColRowNames = (pCode->GetNextColRowName() != NULL);
917 bHasRefs = bHasRefs || bHasColRowNames;
919 bOnRefMove = pCode->IsRecalcModeOnRefMove();
921 if( bHasRefs || bOnRefMove )
923 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
924 BOOL bValChanged;
925 ScRangeData* pRangeData;
926 BOOL bRangeModified; // any range, not only shared formula
927 BOOL bRefSizeChanged;
928 if ( bHasRefs )
930 ScCompiler aComp(pDocument, aPos, *pCode);
931 aComp.SetGrammar(pDocument->GetGrammar());
932 pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r,
933 nDx, nDy, nDz,
934 bValChanged, bRefSizeChanged);
935 bRangeModified = aComp.HasModifiedRange();
937 else
939 bValChanged = FALSE;
940 pRangeData = NULL;
941 bRangeModified = FALSE;
942 bRefSizeChanged = FALSE;
944 if ( bOnRefMove )
945 bOnRefMove = (bValChanged || (aPos != aOldPos));
946 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
948 BOOL bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo;
949 if ( bHasRefs )
951 // Upon Insert ColRowNames have to be recompiled in case the
952 // insertion occurs right in front of the range.
953 bColRowNameCompile =
954 (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0));
955 if ( bColRowNameCompile )
957 bColRowNameCompile = FALSE;
958 ScToken* t;
959 ScRangePairList* pColList = pDocument->GetColNameRanges();
960 ScRangePairList* pRowList = pDocument->GetRowNameRanges();
961 pCode->Reset();
962 while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL )
964 ScSingleRefData& rRef = t->GetSingleRef();
965 if ( nDy > 0 && rRef.IsColRel() )
966 { // ColName
967 rRef.CalcAbsIfRel( aPos );
968 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
969 ScRangePair* pR = pColList->Find( aAdr );
970 if ( pR )
971 { // definiert
972 if ( pR->GetRange(1).aStart.Row() == nRow1 )
973 bColRowNameCompile = TRUE;
975 else
976 { // on the fly
977 if ( rRef.nRow + 1 == nRow1 )
978 bColRowNameCompile = TRUE;
981 if ( nDx > 0 && rRef.IsRowRel() )
982 { // RowName
983 rRef.CalcAbsIfRel( aPos );
984 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
985 ScRangePair* pR = pRowList->Find( aAdr );
986 if ( pR )
987 { // definiert
988 if ( pR->GetRange(1).aStart.Col() == nCol1 )
989 bColRowNameCompile = TRUE;
991 else
992 { // on the fly
993 if ( rRef.nCol + 1 == nCol1 )
994 bColRowNameCompile = TRUE;
999 else if ( eUpdateRefMode == URM_MOVE )
1000 { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde
1001 // oder diese Zelle auf einen zeigt und verschoben wurde
1002 bColRowNameCompile = bCompile; // evtl. aus Copy-ctor
1003 if ( !bColRowNameCompile )
1005 BOOL bMoved = (aPos != aOldPos);
1006 pCode->Reset();
1007 ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName());
1008 if ( t && bMoved )
1009 bColRowNameCompile = TRUE;
1010 while ( t && !bColRowNameCompile )
1012 ScSingleRefData& rRef = t->GetSingleRef();
1013 rRef.CalcAbsIfRel( aPos );
1014 if ( rRef.Valid() )
1016 ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab );
1017 if ( r.In( aAdr ) )
1018 bColRowNameCompile = TRUE;
1020 t = static_cast<ScToken*>(pCode->GetNextColRowName());
1024 else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged )
1026 bColRowNameCompile = TRUE;
1028 ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack();
1029 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1030 bInDeleteUndo = TRUE;
1031 else
1032 bInDeleteUndo = FALSE;
1033 // RelNameRefs are always moved
1034 bHasRelName = HasRelNameReference();
1035 // Reference changed and new listening needed?
1036 // Except in Insert/Delete without specialties.
1037 bNewListening = (bRangeModified || pRangeData || bColRowNameCompile
1038 || (bValChanged && (eUpdateRefMode != URM_INSDEL ||
1039 bInDeleteUndo || bRefSizeChanged)) ||
1040 (bHasRelName && eUpdateRefMode != URM_COPY))
1041 // #i36299# Don't duplicate action during cut&paste / drag&drop
1042 // on a cell in the range moved, start/end listeners is done
1043 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
1044 && !(eUpdateRefMode == URM_MOVE &&
1045 pDocument->IsInsertingFromOtherDoc() && r.In(aPos));
1046 if ( bNewListening )
1047 EndListeningTo( pDocument, pOld, aOldPos );
1049 else
1051 bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo =
1052 FALSE;
1055 BOOL bNeedDirty;
1056 // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames
1057 if ( bRangeModified || pRangeData || bColRowNameCompile ||
1058 (bValChanged && eUpdateRefMode != URM_COPY &&
1059 (eUpdateRefMode != URM_MOVE || bHasRelName) &&
1060 (!bIsInsert || bHasRelName || bInDeleteUndo ||
1061 bRefSizeChanged)) || bOnRefMove)
1062 bNeedDirty = TRUE;
1063 else
1064 bNeedDirty = FALSE;
1065 if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove))
1067 // Copy the cell to aUndoPos, which is its current position in the document,
1068 // so this works when UpdateReference is called before moving the cells
1069 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
1070 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
1072 // If there is already a formula cell in the undo document, don't overwrite it,
1073 // the first (oldest) is the important cell.
1074 if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA )
1076 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos,
1077 pOld, eTempGrammar, cMatrixFlag );
1078 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
1079 pUndoDoc->PutCell( aUndoPos, pFCell );
1082 bValChanged = FALSE;
1083 if ( pRangeData )
1084 { // Replace shared formula with own formula
1085 pDocument->RemoveFromFormulaTree( this ); // update formula count
1086 delete pCode;
1087 pCode = pRangeData->GetCode()->Clone();
1088 ScCompiler aComp2(pDocument, aPos, *pCode);
1089 aComp2.SetGrammar(pDocument->GetGrammar());
1090 aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r,
1091 nDx, nDy, nDz );
1092 bValChanged = TRUE;
1093 bNeedDirty = TRUE;
1095 if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 )
1097 CompileTokenArray( bNewListening ); // kein Listening
1098 bNeedDirty = TRUE;
1100 if ( !bInDeleteUndo )
1101 { // In ChangeTrack Delete-Reject listeners are established in
1102 // InsertCol/InsertRow
1103 if ( bNewListening )
1105 if ( eUpdateRefMode == URM_INSDEL )
1107 // Inserts/Deletes re-establish listeners after all
1108 // UpdateReference calls.
1109 // All replaced shared formula listeners have to be
1110 // established after an Insert or Delete. Do nothing here.
1111 SetNeedsListening( TRUE);
1113 else
1114 StartListeningTo( pDocument );
1117 if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) )
1118 { // Referenzen abgeschnitten, ungueltig o.ae.?
1119 BOOL bOldAutoCalc = pDocument->GetAutoCalc();
1120 // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen
1121 pDocument->SetAutoCalc( FALSE );
1122 SetDirty();
1123 pDocument->SetAutoCalc( bOldAutoCalc );
1126 delete pOld;
1129 pCode->Reset();
1130 for ( formula::FormulaToken* t = pCode->GetNextReferenceOrName(); t; t = pCode->GetNextReferenceOrName() )
1132 StackVar sv = t->GetType();
1133 if (sv == svExternalSingleRef || sv == svExternalDoubleRef || sv == svExternalName)
1135 pDocument->GetExternalRefManager()->updateRefCell(aOldPos, aPos, eUpdateRefMode == URM_COPY);
1136 break;
1141 void ScFormulaCell::UpdateInsertTab(SCTAB nTable)
1143 BOOL bPosChanged = ( aPos.Tab() >= nTable ? TRUE : FALSE );
1144 pCode->Reset();
1145 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1147 EndListeningTo( pDocument );
1148 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab !
1149 if ( bPosChanged )
1150 aPos.IncTab();
1151 ScRangeData* pRangeData;
1152 ScCompiler aComp(pDocument, aPos, *pCode);
1153 aComp.SetGrammar(pDocument->GetGrammar());
1154 pRangeData = aComp.UpdateInsertTab( nTable, FALSE );
1155 if (pRangeData) // Shared Formula gegen echte Formel
1156 { // austauschen
1157 BOOL bRefChanged;
1158 pDocument->RemoveFromFormulaTree( this ); // update formula count
1159 delete pCode;
1160 pCode = new ScTokenArray( *pRangeData->GetCode() );
1161 ScCompiler aComp2(pDocument, aPos, *pCode);
1162 aComp2.SetGrammar(pDocument->GetGrammar());
1163 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1164 aComp2.UpdateInsertTab( nTable, FALSE );
1165 // If the shared formula contained a named range/formula containing
1166 // an absolute reference to a sheet, those have to be readjusted.
1167 aComp2.UpdateDeleteTab( nTable, FALSE, TRUE, bRefChanged );
1168 bCompile = TRUE;
1170 // kein StartListeningTo weil pTab[nTab] noch nicht existiert!
1172 else if ( bPosChanged )
1173 aPos.IncTab();
1176 BOOL ScFormulaCell::UpdateDeleteTab(SCTAB nTable, BOOL bIsMove)
1178 BOOL bRefChanged = FALSE;
1179 BOOL bPosChanged = ( aPos.Tab() > nTable ? TRUE : FALSE );
1180 pCode->Reset();
1181 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1183 EndListeningTo( pDocument );
1184 // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab !
1185 if ( bPosChanged )
1186 aPos.IncTab(-1);
1187 ScRangeData* pRangeData;
1188 ScCompiler aComp(pDocument, aPos, *pCode);
1189 aComp.SetGrammar(pDocument->GetGrammar());
1190 pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, FALSE, bRefChanged);
1191 if (pRangeData) // Shared Formula gegen echte Formel
1192 { // austauschen
1193 pDocument->RemoveFromFormulaTree( this ); // update formula count
1194 delete pCode;
1195 pCode = pRangeData->GetCode()->Clone();
1196 ScCompiler aComp2(pDocument, aPos, *pCode);
1197 aComp2.SetGrammar(pDocument->GetGrammar());
1198 aComp2.CompileTokenArray();
1199 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1200 aComp2.UpdateDeleteTab( nTable, FALSE, FALSE, bRefChanged );
1201 // If the shared formula contained a named range/formula containing
1202 // an absolute reference to a sheet, those have to be readjusted.
1203 aComp2.UpdateInsertTab( nTable,TRUE );
1204 // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein
1205 bRefChanged = TRUE;
1206 bCompile = TRUE;
1208 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt!
1210 else if ( bPosChanged )
1211 aPos.IncTab(-1);
1213 return bRefChanged;
1216 void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
1218 pCode->Reset();
1219 if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() )
1221 EndListeningTo( pDocument );
1222 // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab !
1223 aPos.SetTab( nTabNo );
1224 ScRangeData* pRangeData;
1225 ScCompiler aComp(pDocument, aPos, *pCode);
1226 aComp.SetGrammar(pDocument->GetGrammar());
1227 pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, FALSE );
1228 if (pRangeData) // Shared Formula gegen echte Formel
1229 { // austauschen
1230 pDocument->RemoveFromFormulaTree( this ); // update formula count
1231 delete pCode;
1232 pCode = pRangeData->GetCode()->Clone();
1233 ScCompiler aComp2(pDocument, aPos, *pCode);
1234 aComp2.SetGrammar(pDocument->GetGrammar());
1235 aComp2.CompileTokenArray();
1236 aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
1237 aComp2.UpdateMoveTab( nOldPos, nNewPos, TRUE );
1238 bCompile = TRUE;
1240 // kein StartListeningTo weil pTab[nTab] noch nicht korrekt!
1242 else
1243 aPos.SetTab( nTabNo );
1246 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable)
1248 if( !pDocument->IsClipOrUndo() )
1250 pCode->Reset();
1251 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1252 while( p )
1254 ScSingleRefData& rRef1 = p->GetSingleRef();
1255 if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab )
1256 rRef1.nTab++;
1257 if( p->GetType() == formula::svDoubleRef )
1259 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
1260 if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab )
1261 rRef2.nTab++;
1263 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1268 BOOL ScFormulaCell::TestTabRefAbs(SCTAB nTable)
1270 BOOL bRet = FALSE;
1271 if( !pDocument->IsClipOrUndo() )
1273 pCode->Reset();
1274 ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1275 while( p )
1277 ScSingleRefData& rRef1 = p->GetSingleRef();
1278 if( !rRef1.IsTabRel() )
1280 if( (SCsTAB) nTable != rRef1.nTab )
1281 bRet = TRUE;
1282 else if (nTable != aPos.Tab())
1283 rRef1.nTab = aPos.Tab();
1285 if( p->GetType() == formula::svDoubleRef )
1287 ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2;
1288 if( !rRef2.IsTabRel() )
1290 if( (SCsTAB) nTable != rRef2.nTab )
1291 bRet = TRUE;
1292 else if (nTable != aPos.Tab())
1293 rRef2.nTab = aPos.Tab();
1296 p = static_cast<ScToken*>(pCode->GetNextReferenceRPN());
1299 return bRet;
1302 void ScFormulaCell::UpdateCompile( BOOL bForceIfNameInUse )
1304 if ( bForceIfNameInUse && !bCompile )
1305 bCompile = pCode->HasNameOrColRowName();
1306 if ( bCompile )
1307 pCode->SetCodeError( 0 ); // make sure it will really be compiled
1308 CompileTokenArray();
1311 // Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen
1313 void ScFormulaCell::TransposeReference()
1315 BOOL bFound = FALSE;
1316 pCode->Reset();
1317 ScToken* t;
1318 while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL )
1320 ScSingleRefData& rRef1 = t->GetSingleRef();
1321 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
1323 BOOL bDouble = (t->GetType() == formula::svDoubleRef);
1324 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1);
1325 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
1327 INT16 nTemp;
1329 nTemp = rRef1.nRelCol;
1330 rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow);
1331 rRef1.nRelRow = static_cast<SCROW>(nTemp);
1333 if ( bDouble )
1335 nTemp = rRef2.nRelCol;
1336 rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow);
1337 rRef2.nRelRow = static_cast<SCROW>(nTemp);
1340 bFound = TRUE;
1345 if (bFound)
1346 bCompile = TRUE;
1349 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
1350 ScDocument* pUndoDoc )
1352 EndListeningTo( pDocument );
1354 ScAddress aOldPos = aPos;
1355 BOOL bPosChanged = FALSE; // ob diese Zelle bewegt wurde
1357 ScRange aDestRange( rDest, ScAddress(
1358 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
1359 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
1360 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
1361 if ( aDestRange.In( aOldPos ) )
1363 // Position zurueckrechnen
1364 SCsCOL nRelPosX = aOldPos.Col();
1365 SCsROW nRelPosY = aOldPos.Row();
1366 SCsTAB nRelPosZ = aOldPos.Tab();
1367 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
1368 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
1369 bPosChanged = TRUE;
1372 ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL;
1373 BOOL bRefChanged = FALSE;
1374 ScToken* t;
1376 ScRangeData* pShared = NULL;
1377 pCode->Reset();
1378 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
1380 if( t->GetOpCode() == ocName )
1382 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
1383 if (pName)
1385 if (pName->IsModified())
1386 bRefChanged = TRUE;
1387 if (pName->HasType(RT_SHAREDMOD))
1388 pShared = pName;
1391 else if( t->GetType() != svIndex )
1393 t->CalcAbsIfRel( aOldPos );
1394 BOOL bMod;
1395 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1396 SingleDoubleRefModifier aMod( *t );
1397 ScComplexRefData& rRef = aMod.Ref();
1398 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
1399 rDest, rRef ) != UR_NOTHING || bPosChanged);
1401 if ( bMod )
1403 t->CalcRelFromAbs( aPos );
1404 bRefChanged = TRUE;
1409 if (pShared) // Shared Formula gegen echte Formel austauschen
1411 pDocument->RemoveFromFormulaTree( this ); // update formula count
1412 delete pCode;
1413 pCode = new ScTokenArray( *pShared->GetCode() );
1414 bRefChanged = TRUE;
1415 pCode->Reset();
1416 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
1418 if( t->GetType() != svIndex )
1420 t->CalcAbsIfRel( aOldPos );
1421 BOOL bMod;
1422 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1423 SingleDoubleRefModifier aMod( *t );
1424 ScComplexRefData& rRef = aMod.Ref();
1425 bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource,
1426 rDest, rRef ) != UR_NOTHING || bPosChanged);
1428 if ( bMod )
1429 t->CalcRelFromAbs( aPos );
1434 if (bRefChanged)
1436 if (pUndoDoc)
1438 ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld,
1439 eTempGrammar, cMatrixFlag);
1440 pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!)
1441 pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell );
1444 bCompile = TRUE;
1445 CompileTokenArray(); // ruft auch StartListeningTo
1446 SetDirty();
1448 else
1449 StartListeningTo( pDocument ); // Listener wie vorher
1451 delete pOld;
1454 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
1456 EndListeningTo( pDocument );
1458 BOOL bRefChanged = FALSE;
1459 ScToken* t;
1460 ScRangeData* pShared = NULL;
1462 pCode->Reset();
1463 while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL )
1465 if( t->GetOpCode() == ocName )
1467 ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
1468 if (pName)
1470 if (pName->IsModified())
1471 bRefChanged = TRUE;
1472 if (pName->HasType(RT_SHAREDMOD))
1473 pShared = pName;
1476 else if( t->GetType() != svIndex )
1478 t->CalcAbsIfRel( aPos );
1479 BOOL bMod;
1480 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1481 SingleDoubleRefModifier aMod( *t );
1482 ScComplexRefData& rRef = aMod.Ref();
1483 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
1484 rRef ) != UR_NOTHING);
1486 if ( bMod )
1488 t->CalcRelFromAbs( aPos );
1489 bRefChanged = TRUE;
1494 if (pShared) // Shared Formula gegen echte Formel austauschen
1496 pDocument->RemoveFromFormulaTree( this ); // update formula count
1497 delete pCode;
1498 pCode = new ScTokenArray( *pShared->GetCode() );
1499 bRefChanged = TRUE;
1500 pCode->Reset();
1501 while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL )
1503 if( t->GetType() != svIndex )
1505 t->CalcAbsIfRel( aPos );
1506 BOOL bMod;
1507 { // own scope for SingleDoubleRefModifier dtor if SingleRef
1508 SingleDoubleRefModifier aMod( *t );
1509 ScComplexRefData& rRef = aMod.Ref();
1510 bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY,
1511 rRef ) != UR_NOTHING);
1513 if ( bMod )
1514 t->CalcRelFromAbs( aPos );
1519 if (bRefChanged)
1521 bCompile = TRUE;
1522 CompileTokenArray(); // ruft auch StartListeningTo
1523 SetDirty();
1525 else
1526 StartListeningTo( pDocument ); // Listener wie vorher
1529 BOOL lcl_IsRangeNameInUse(USHORT nIndex, ScTokenArray* pCode, ScRangeName* pNames)
1531 for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
1533 if (p->GetOpCode() == ocName)
1535 if (p->GetIndex() == nIndex)
1536 return TRUE;
1537 else
1539 // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#)
1540 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex());
1541 if (pSubName && lcl_IsRangeNameInUse(nIndex,
1542 pSubName->GetCode(), pNames))
1543 return TRUE;
1547 return FALSE;
1550 BOOL ScFormulaCell::IsRangeNameInUse(USHORT nIndex) const
1552 return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() );
1555 void lcl_FindRangeNamesInUse(std::set<USHORT>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames)
1557 for (FormulaToken* p = pCode->First(); p; p = pCode->Next())
1559 if (p->GetOpCode() == ocName)
1561 USHORT nTokenIndex = p->GetIndex();
1562 rIndexes.insert( nTokenIndex );
1564 ScRangeData* pSubName = pNames->FindIndex(p->GetIndex());
1565 if (pSubName)
1566 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames);
1571 void ScFormulaCell::FindRangeNamesInUse(std::set<USHORT>& rIndexes) const
1573 lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() );
1576 void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap )
1578 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1580 if( p->GetOpCode() == ocName )
1582 sal_uInt16 nIndex = p->GetIndex();
1583 ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex);
1584 sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second;
1585 if ( nIndex != nNewIndex )
1587 p->SetIndex( nNewIndex );
1588 bCompile = TRUE;
1592 if( bCompile )
1593 CompileTokenArray();
1596 void ScFormulaCell::CompileDBFormula()
1598 for( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1600 if ( p->GetOpCode() == ocDBArea
1601 || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) )
1603 bCompile = TRUE;
1604 CompileTokenArray();
1605 SetDirty();
1606 break;
1611 void ScFormulaCell::CompileDBFormula( BOOL bCreateFormulaString )
1613 // zwei Phasen, muessen (!) nacheinander aufgerufen werden:
1614 // 1. FormelString mit alten Namen erzeugen
1615 // 2. FormelString mit neuen Namen kompilieren
1616 if ( bCreateFormulaString )
1618 BOOL bRecompile = FALSE;
1619 pCode->Reset();
1620 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
1622 switch ( p->GetOpCode() )
1624 case ocBad: // DB-Bereich evtl. zugefuegt
1625 case ocColRowName: // #36762# falls Namensgleichheit
1626 case ocDBArea: // DB-Bereich
1627 bRecompile = TRUE;
1628 break;
1629 case ocName:
1630 if ( p->GetIndex() >= SC_START_INDEX_DB_COLL )
1631 bRecompile = TRUE; // DB-Bereich
1632 break;
1633 default:
1634 ; // nothing
1637 if ( bRecompile )
1639 String aFormula;
1640 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1641 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() )
1643 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' )
1644 aFormula.Erase( aFormula.Len()-1 , 1 );
1645 if ( aFormula.GetChar(0) == '{' )
1646 aFormula.Erase( 0, 1 );
1648 EndListeningTo( pDocument );
1649 pDocument->RemoveFromFormulaTree( this );
1650 pCode->Clear();
1651 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1654 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1656 Compile( aResult.GetHybridFormula(), FALSE, eTempGrammar );
1657 aResult.SetToken( NULL);
1658 SetDirty();
1662 void ScFormulaCell::CompileNameFormula( BOOL bCreateFormulaString )
1664 // zwei Phasen, muessen (!) nacheinander aufgerufen werden:
1665 // 1. FormelString mit alten RangeNames erzeugen
1666 // 2. FormelString mit neuen RangeNames kompilieren
1667 if ( bCreateFormulaString )
1669 BOOL bRecompile = FALSE;
1670 pCode->Reset();
1671 for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() )
1673 switch ( p->GetOpCode() )
1675 case ocBad: // RangeName evtl. zugefuegt
1676 case ocColRowName: // #36762# falls Namensgleichheit
1677 bRecompile = TRUE;
1678 break;
1679 default:
1680 if ( p->GetType() == svIndex )
1681 bRecompile = TRUE; // RangeName
1684 if ( bRecompile )
1686 String aFormula;
1687 GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1688 if ( GetMatrixFlag() != MM_NONE && aFormula.Len() )
1690 if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' )
1691 aFormula.Erase( aFormula.Len()-1 , 1 );
1692 if ( aFormula.GetChar(0) == '{' )
1693 aFormula.Erase( 0, 1 );
1695 EndListeningTo( pDocument );
1696 pDocument->RemoveFromFormulaTree( this );
1697 pCode->Clear();
1698 SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE);
1701 else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() )
1703 Compile( aResult.GetHybridFormula(), FALSE, eTempGrammar );
1704 aResult.SetToken( NULL);
1705 SetDirty();
1709 void ScFormulaCell::CompileColRowNameFormula()
1711 pCode->Reset();
1712 for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() )
1714 if ( p->GetOpCode() == ocColRowName )
1716 bCompile = TRUE;
1717 CompileTokenArray();
1718 SetDirty();
1719 break;
1724 // ============================================================================