diff options
author | Kohei Yoshida <kohei.yoshida@gmail.com> | 2013-06-27 14:57:32 -0400 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@gmail.com> | 2013-06-27 15:50:08 -0400 |
commit | ff5d891b480a0bb3b6cee950d9ea41387fd16392 (patch) | |
tree | 8b1d89e4b2d29480e727dc4ccea96c923e09a3ea /sc | |
parent | b5e8b51f8643eb7576335ca01d7bffa2462e62f5 (diff) |
Some attempt to clean ScFormulaCell::UpdateReference() a bit.
This method is still a monster, however...
Change-Id: I4aaa2d20c8ae6132a75c7c92c0a1b58882d8a261
Diffstat (limited to 'sc')
-rw-r--r-- | sc/inc/formulacell.hxx | 9 | ||||
-rw-r--r-- | sc/source/core/data/formulacell.cxx | 399 |
2 files changed, 208 insertions, 200 deletions
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index b724d0278ce2..1f9b96073f89 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -176,11 +176,10 @@ public: bool HasRelNameReference() const; bool HasColRowName() const; - bool UpdateReference(UpdateRefMode eUpdateRefMode, - const ScRange& r, - SCsCOL nDx, SCsROW nDy, SCsTAB nDz, - ScDocument* pUndoDoc = NULL, - const ScAddress* pUndoCellPos = NULL ); + bool UpdateReference( + UpdateRefMode eUpdateRefMode, const ScRange& rRange, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, ScDocument* pUndoDoc = NULL, + const ScAddress* pUndoCellPos = NULL ); void TransposeReference(); void UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index a3ab73dc31ba..5d471c4e9cee 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -46,8 +46,10 @@ #include "formulagroup.hxx" #include "listenercontext.hxx" #include "types.hxx" +#include "scopetools.hxx" #include <boost/bind.hpp> +#include <boost/scoped_ptr.hpp> using namespace formula; @@ -2039,15 +2041,14 @@ bool ScFormulaCell::HasColRowName() const return (pCode->GetNextColRowName() != NULL); } -bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, - const ScRange& r, - SCsCOL nDx, SCsROW nDy, SCsTAB nDz, - ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ) +bool ScFormulaCell::UpdateReference( + UpdateRefMode eUpdateRefMode, const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ) { bool bCellStateChanged = false; - SCCOL nCol1 = r.aStart.Col(); - SCROW nRow1 = r.aStart.Row(); + SCCOL nCol1 = rRange.aStart.Col(); + SCROW nRow1 = rRange.aStart.Row(); SCCOL nCol = aPos.Col(); SCROW nRow = aPos.Row(); SCTAB nTab = aPos.Tab(); @@ -2055,15 +2056,21 @@ bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, if ( pUndoCellPos ) aUndoPos = *pUndoCellPos; ScAddress aOldPos( aPos ); - bool bIsInsert = (eUpdateRefMode == URM_INSDEL && - nDx >= 0 && nDy >= 0 && nDz >= 0); - if (eUpdateRefMode == URM_INSDEL && r.In( aPos )) + bool bIsInsert = (eUpdateRefMode == URM_INSDEL && nDx >= 0 && nDy >= 0 && nDz >= 0); + + if (eUpdateRefMode == URM_INSDEL && rRange.In(aPos)) { + // This formula cell itself is being shifted during cell range + // insertion or deletion. Update its position. aPos.Move(nDx, nDy, nDz); bCellStateChanged = aPos != aOldPos; } - else if ( r.In( aPos ) ) + else if (rRange.In(aPos)) { + // The cell is being moved or copied to a new position. I guess the + // position has been updated prior to this call? Determine + // its original position before the move which will be used to adjust + // relative references later. aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz ); } @@ -2072,6 +2079,7 @@ bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, bool bOnRefMove = false; if ( !pDocument->IsClipOrUndo() ) { + // Check presence of any references or column row names. pCode->Reset(); bHasRefs = (pCode->GetNextReferenceRPN() != NULL); if ( !bHasRefs || eUpdateRefMode == URM_COPY ) @@ -2082,218 +2090,219 @@ bool ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, } bOnRefMove = pCode->IsRecalcModeOnRefMove(); } - if( bHasRefs || bOnRefMove ) + + if (!bHasRefs && !bOnRefMove) + // This formula cell contains no references, nor needs recalculating + // on reference update. Bail out. + return bCellStateChanged; + + boost::scoped_ptr<ScTokenArray> pOldCode; + if (pUndoDoc) + pOldCode.reset(pCode->Clone()); + + ScRangeData* pRangeData = NULL; + bool bValChanged = false; + bool bRangeModified = false; // any range, not only shared formula + bool bRefSizeChanged = false; + + if (bHasRefs) { - ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; - ScRangeData* pRangeData; - bool bValChanged = false; - bool bRangeModified = false; // any range, not only shared formula - bool bRefSizeChanged = false; - 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; - } + // Update cell or range references. + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, rRange, + nDx, nDy, nDz, + bValChanged, bRefSizeChanged); + bRangeModified = aComp.HasModifiedRange(); + } + + bCellStateChanged |= bValChanged; - bCellStateChanged |= bValChanged; + if (bOnRefMove) + // Cell may reference itself, e.g. ocColumn, ocRow without parameter + bOnRefMove = (bValChanged || (aPos != aOldPos)); - if ( bOnRefMove ) - bOnRefMove = (bValChanged || (aPos != aOldPos)); - // Cell may reference itself, e.g. ocColumn, ocRow without parameter + bool bColRowNameCompile = false; + bool bHasRelName = false; + bool bNewListening = false; + bool bInDeleteUndo = false; - bool bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo; - if ( bHasRefs ) + 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 ) { - // 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 ) { - 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 ) - { // defined - if ( pR->GetRange(1).aStart.Row() == nRow1 ) - bColRowNameCompile = true; - } - else - { // on the fly - if ( rRef.nRow + 1 == nRow1 ) - bColRowNameCompile = true; - } + 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 ) + { // defined + if ( pR->GetRange(1).aStart.Row() == 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 ) - { // defined - if ( pR->GetRange(1).aStart.Col() == nCol1 ) - bColRowNameCompile = true; - } - else - { // on the fly - if ( rRef.nCol + 1 == nCol1 ) - 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 ) + { // defined + 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 ) - { // Recomplie for Move/D&D when ColRowName was moved or this Cell - // points to one and was moved. - bColRowNameCompile = bCompile; // Possibly from Copy ctor - if ( !bColRowNameCompile ) + } + else if ( eUpdateRefMode == URM_MOVE ) + { // Recomplie for Move/D&D when ColRowName was moved or this Cell + // points to one and was moved. + bColRowNameCompile = bCompile; // Possibly from 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 ) { - 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() ) { - 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()); + ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); + if ( rRange.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 + else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged ) { - bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo = - false; + bColRowNameCompile = true; } - bool bNeedDirty = false; - // NeedDirty for changes except for Copy and Move/Insert without 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). + ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack(); + bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo()); - // 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->SetFormulaCell(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 ) + // 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() && rRange.In(aPos)); + + if ( bNewListening ) + EndListeningTo(pDocument, pOldCode.get(), aOldPos); + } + + bool bNeedDirty = false; + // NeedDirty for changes except for Copy and Move/Insert without RelNames + if ( bRangeModified || pRangeData || bColRowNameCompile || + (bValChanged && eUpdateRefMode != URM_COPY && + (eUpdateRefMode != URM_MOVE || bHasRelName) && + (!bIsInsert || bHasRelName || bInDeleteUndo || + bRefSizeChanged)) || bOnRefMove) + bNeedDirty = true; + + 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 ) { - CompileTokenArray( bNewListening ); // no Listening - bNeedDirty = true; + ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos, + pOldCode.get(), eTempGrammar, cMatrixFlag ); + pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) + pUndoDoc->SetFormulaCell(aUndoPos, pFCell); } - if ( !bInDeleteUndo ) - { // In ChangeTrack Delete-Reject listeners are established in - // InsertCol/InsertRow - if ( bNewListening ) + } + + 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, rRange, + nDx, nDy, nDz ); + bValChanged = true; + bNeedDirty = true; + } + + if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 ) + { + CompileTokenArray( bNewListening ); // no Listening + bNeedDirty = true; + } + + if ( !bInDeleteUndo ) + { // In ChangeTrack Delete-Reject listeners are established in + // InsertCol/InsertRow + if ( bNewListening ) + { + if ( eUpdateRefMode == URM_INSDEL ) { - 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 ); + // 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) ) - { // Cut off references, invalid or similar? - bool bOldAutoCalc = pDocument->GetAutoCalc(); - // No Interpret in SubMinimalRecalc because of eventual wrong reference - pDocument->SetAutoCalc( false ); - SetDirty(); - pDocument->SetAutoCalc( bOldAutoCalc ); - } + } - delete pOld; + if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) ) + { // Cut off references, invalid or similar? + sc::AutoCalcSwitch(*pDocument, false); + SetDirty(); } + return bCellStateChanged; } |