sc/inc/address.hxx | 27 +++++++++ sc/inc/refdata.hxx | 10 +++ sc/source/core/tool/address.cxx | 60 +++++++++++++++++++- sc/source/core/tool/refdata.cxx | 58 +++++++++++++++++++ sc/source/core/tool/refupdat.cxx | 14 ++++ sc/source/core/tool/token.cxx | 114 +++++++++++++++++++++++++++++---------- 6 files changed, 251 insertions(+), 32 deletions(-)
New commits: commit cfecdd6199710921f8fd921f615203c9e34c551e Author: Eike Rathke <er...@redhat.com> Date: Wed Dec 9 20:58:22 2015 +0100 sticky end col/row anchor for range references, tdf#92779 In range references referring more than one column/row where the end col/row points to the maximum column or row number that col/row is not decremented when the range is shrunken. Incrementing an end col/row is not done past the maximum column or row number, so such references do not yield #REF! errors anymore. This is also done in named expressions if the end col/row is an absolute reference. Change-Id: Iafa2d62abd3e816a1c56c3166af92807e55b75ce diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx index aa700d9..4bf5ee5 100644 --- a/sc/inc/address.hxx +++ b/sc/inc/address.hxx @@ -542,6 +542,21 @@ public: ScRange Intersection( const ScRange& rOther ) const; + /// If maximum end column should not be adapted during reference update. + inline bool IsEndColSticky() const; + /// If maximum end row should not be adapted during reference update. + inline bool IsEndRowSticky() const; + + /** Increment or decrement end column unless sticky or until it becomes + sticky. Checks if the range encompasses at least two columns so should + be called before adjusting the start column. */ + void IncEndColSticky( SCsCOL nDelta ); + + /** Increment or decrement end row unless sticky or until it becomes + sticky. Checks if the range encompasses at least two rows so should + be called before adjusting the start row. */ + void IncEndRowSticky( SCsROW nDelta ); + inline bool operator==( const ScRange& rRange ) const; inline bool operator!=( const ScRange& rRange ) const; inline bool operator<( const ScRange& rRange ) const; @@ -562,6 +577,18 @@ inline void ScRange::GetVars( SCCOL& nCol1, SCROW& nRow1, SCTAB& nTab1, aEnd.GetVars( nCol2, nRow2, nTab2 ); } +inline bool ScRange::IsEndColSticky() const +{ + // Only in an actual column range, i.e. not if both columns are MAXCOL. + return aEnd.Col() == MAXCOL && aStart.Col() < aEnd.Col(); +} + +inline bool ScRange::IsEndRowSticky() const +{ + // Only in an actual row range, i.e. not if both rows are MAXROW. + return aEnd.Row() == MAXROW && aStart.Row() < aEnd.Row(); +} + inline bool ScRange::operator==( const ScRange& rRange ) const { return ( (aStart == rRange.aStart) && (aEnd == rRange.aEnd) ); diff --git a/sc/inc/refdata.hxx b/sc/inc/refdata.hxx index b96acb7..5f3e762 100644 --- a/sc/inc/refdata.hxx +++ b/sc/inc/refdata.hxx @@ -180,6 +180,16 @@ struct ScComplexRefData ScComplexRefData& Extend( const ScSingleRefData & rRef, const ScAddress & rPos ); ScComplexRefData& Extend( const ScComplexRefData & rRef, const ScAddress & rPos ); + /** Increment or decrement end column unless or until sticky. + @see ScRange::IncEndColSticky() + @return TRUE if changed. */ + bool IncEndColSticky( SCCOL nDelta, const ScAddress& rPos ); + + /** Increment or decrement end row unless or until sticky. + @see ScRange::IncEndRowSticky() + @return TRUE if changed. */ + bool IncEndRowSticky( SCROW nDelta, const ScAddress& rPos ); + #if DEBUG_FORMULA_COMPILER void Dump( int nIndent = 0 ) const; #endif diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx index f69a822..b7f9a5b 100644 --- a/sc/source/core/tool/address.cxx +++ b/sc/source/core/tool/address.cxx @@ -2125,13 +2125,67 @@ bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc ) { + bool bColRange = (aStart.Col() < aEnd.Col()); + bool bRowRange = (aStart.Row() < aEnd.Row()); if (dy && aStart.Row() == 0 && aEnd.Row() == MAXROW) dy = 0; // Entire column not to be moved. if (dx && aStart.Col() == 0 && aEnd.Col() == MAXCOL) dx = 0; // Entire row not to be moved. - bool b = aStart.Move( dx, dy, dz, pDoc ); - b &= aEnd.Move( dx, dy, dz, pDoc ); - return b; + bool b1 = aStart.Move( dx, dy, dz, pDoc ); + if (dx && aEnd.Col() == MAXCOL) + dx = 0; // End column sticky. + if (dy && aEnd.Row() == MAXROW) + dy = 0; // End row sticky. + SCTAB nOldTab = aEnd.Tab(); + bool b2 = aEnd.Move( dx, dy, dz, pDoc ); + if (!b2) + { + // End column or row of a range may have become sticky. + bColRange = (!dx || (bColRange && aEnd.Col() == MAXCOL)); + bRowRange = (!dy || (bRowRange && aEnd.Row() == MAXROW)); + b2 = bColRange && bRowRange && (aEnd.Tab() - nOldTab == dz); + } + return b1 && b2; +} + +void ScRange::IncEndColSticky( SCsCOL nDelta ) +{ + SCCOL nCol = aEnd.Col(); + if (aStart.Col() >= nCol) + { + // Less than two columns => not sticky. + aEnd.IncCol( nDelta); + return; + } + + if (nCol == MAXCOL) + // already sticky + return; + + if (nCol < MAXCOL) + aEnd.SetCol( ::std::min( static_cast<SCCOL>(nCol + nDelta), MAXCOL)); + else + aEnd.IncCol( nDelta); // was greater than MAXCOL, caller should know.. +} + +void ScRange::IncEndRowSticky( SCsROW nDelta ) +{ + SCROW nRow = aEnd.Row(); + if (aStart.Row() >= nRow) + { + // Less than two rows => not sticky. + aEnd.IncRow( nDelta); + return; + } + + if (nRow == MAXROW) + // already sticky + return; + + if (nRow < MAXROW) + aEnd.SetRow( ::std::min( static_cast<SCROW>(nRow + nDelta), MAXROW)); + else + aEnd.IncRow( nDelta); // was greater than MAXROW, caller should know.. } OUString ScAddress::GetColRowString( bool bAbsolute, diff --git a/sc/source/core/tool/refdata.cxx b/sc/source/core/tool/refdata.cxx index 0878cb1..9811c08 100644 --- a/sc/source/core/tool/refdata.cxx +++ b/sc/source/core/tool/refdata.cxx @@ -478,6 +478,64 @@ void ScComplexRefData::PutInOrder( const ScAddress& rPos ) ScSingleRefData::PutInOrder( Ref1, Ref2, rPos); } +bool ScComplexRefData::IncEndColSticky( SCCOL nDelta, const ScAddress& rPos ) +{ + SCCOL nCol1 = Ref1.IsColRel() ? Ref1.Col() + rPos.Col() : Ref1.Col(); + SCCOL nCol2 = Ref2.IsColRel() ? Ref2.Col() + rPos.Col() : Ref2.Col(); + if (nCol1 >= nCol2) + { + // Less than two columns => not sticky. + Ref2.IncCol( nDelta); + return true; + } + + if (nCol2 == MAXCOL) + // already sticky + return false; + + if (nCol2 < MAXCOL) + { + SCCOL nCol = ::std::min( static_cast<SCCOL>(nCol2 + nDelta), MAXCOL); + if (Ref2.IsColRel()) + Ref2.SetRelCol( nCol - rPos.Col()); + else + Ref2.SetAbsCol( nCol); + } + else + Ref2.IncCol( nDelta); // was greater than MAXCOL, caller should know.. + + return true; +} + +bool ScComplexRefData::IncEndRowSticky( SCROW nDelta, const ScAddress& rPos ) +{ + SCROW nRow1 = Ref1.IsRowRel() ? Ref1.Row() + rPos.Row() : Ref1.Row(); + SCROW nRow2 = Ref2.IsRowRel() ? Ref2.Row() + rPos.Row() : Ref2.Row(); + if (nRow1 >= nRow2) + { + // Less than two rows => not sticky. + Ref2.IncRow( nDelta); + return true; + } + + if (nRow2 == MAXROW) + // already sticky + return false; + + if (nRow2 < MAXROW) + { + SCROW nRow = ::std::min( static_cast<SCROW>(nRow2 + nDelta), MAXROW); + if (Ref2.IsRowRel()) + Ref2.SetRelRow( nRow - rPos.Row()); + else + Ref2.SetAbsRow( nRow); + } + else + Ref2.IncRow( nDelta); // was greater than MAXROW, caller should know.. + + return true; +} + #if DEBUG_FORMULA_COMPILER void ScComplexRefData::Dump( int nIndent ) const { diff --git a/sc/source/core/tool/refupdat.cxx b/sc/source/core/tool/refupdat.cxx index 8ca5373..602a636 100644 --- a/sc/source/core/tool/refupdat.cxx +++ b/sc/source/core/tool/refupdat.cxx @@ -231,6 +231,13 @@ ScRefUpdateRes ScRefUpdate::Update( ScDocument* pDoc, UpdateRefMode eUpdateRefMo theCol1 = oldCol1; theCol2 = oldCol2; } + else if (oldCol2 == MAXCOL && oldCol1 < MAXCOL) + { + // End was sticky, but start may have been moved. Only on range. + theCol2 = oldCol2; + } + // Else, if (bCut2 && theCol2 == MAXCOL) then end becomes sticky, + // but currently there's nothing to do. } if ( nDy && (theCol1 >= nCol1) && (theCol2 <= nCol2) && (theTab1 >= nTab1) && (theTab2 <= nTab2)) @@ -256,6 +263,13 @@ ScRefUpdateRes ScRefUpdate::Update( ScDocument* pDoc, UpdateRefMode eUpdateRefMo theRow1 = oldRow1; theRow2 = oldRow2; } + else if (oldRow2 == MAXROW && oldRow1 < MAXROW) + { + // End was sticky, but start may have been moved. Only on range. + theRow2 = oldRow2; + } + // Else, if (bCut2 && theRow2 == MAXROW) then end becomes sticky, + // but currently there's nothing to do. } if ( nDz && (theCol1 >= nCol1) && (theCol2 <= nCol2) && (theRow1 >= nRow1) && (theRow2 <= nRow2) ) diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index 41be86a..49bbe2e 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -2599,22 +2599,30 @@ bool shrinkRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const Sc // The reference range is truncated on the left. SCCOL nOffset = rDeletedRange.aStart.Col() - rRefRange.aStart.Col(); SCCOL nDelta = rRefRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1; + rRefRange.IncEndColSticky(nDelta+nOffset); rRefRange.aStart.IncCol(nOffset); - rRefRange.aEnd.IncCol(nDelta+nOffset); } } else if (rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col()) { + if (rRefRange.IsEndColSticky()) + // Sticky end not affected. + return false; + // Reference is deleted in the middle. Move the last column // position to the left. SCCOL nDelta = rDeletedRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1; - rRefRange.aEnd.IncCol(nDelta); + rRefRange.IncEndColSticky(nDelta); } else { + if (rRefRange.IsEndColSticky()) + // Sticky end not affected. + return false; + // The reference range is truncated on the right. SCCOL nDelta = rDeletedRange.aStart.Col() - rRefRange.aEnd.Col() - 1; - rRefRange.aEnd.IncCol(nDelta); + rRefRange.IncEndColSticky(nDelta); } return true; } @@ -2642,22 +2650,30 @@ bool shrinkRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const Sc // The reference range is truncated on the top. SCCOL nOffset = rDeletedRange.aStart.Row() - rRefRange.aStart.Row(); SCCOL nDelta = rRefRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1; + rRefRange.IncEndRowSticky(nDelta+nOffset); rRefRange.aStart.IncRow(nOffset); - rRefRange.aEnd.IncRow(nDelta+nOffset); } } else if (rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row()) { + if (rRefRange.IsEndRowSticky()) + // Sticky end not affected. + return false; + // Reference is deleted in the middle. Move the last row // position upward. SCCOL nDelta = rDeletedRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1; - rRefRange.aEnd.IncRow(nDelta); + rRefRange.IncEndRowSticky(nDelta); } else { + if (rRefRange.IsEndRowSticky()) + // Sticky end not affected. + return false; + // The reference range is truncated on the bottom. SCCOL nDelta = rDeletedRange.aStart.Row() - rRefRange.aEnd.Row() - 1; - rRefRange.aEnd.IncRow(nDelta); + rRefRange.IncEndRowSticky(nDelta); } return true; } @@ -2695,9 +2711,13 @@ bool expandRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const Sc return false; } + if (rRefRange.IsEndColSticky()) + // Sticky end not affected. + return false; + // Move the last column position to the right. SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1; - rRefRange.aEnd.IncCol(nDelta); + rRefRange.IncEndColSticky(nDelta); return true; } else if (rCxt.mnRowDelta > 0) @@ -2724,9 +2744,13 @@ bool expandRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const Sc return false; } + if (rRefRange.IsEndRowSticky()) + // Sticky end not affected. + return false; + // Move the last row position down. SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1; - rRefRange.aEnd.IncRow(nDelta); + rRefRange.IncEndRowSticky(nDelta); return true; } return false; @@ -2767,9 +2791,13 @@ bool expandRangeByEdge( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, co // Selected range is not immediately adjacent. Bail out. return false; + if (rRefRange.IsEndColSticky()) + // Sticky end not affected. + return false; + // Move the last column position to the right. SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1; - rRefRange.aEnd.IncCol(nDelta); + rRefRange.IncEndColSticky(nDelta); return true; } else if (rCxt.mnRowDelta > 0) @@ -2790,9 +2818,13 @@ bool expandRangeByEdge( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, co // Selected range is not immediately adjacent. Bail out. return false; + if (rRefRange.IsEndRowSticky()) + // Sticky end not affected. + return false; + // Move the last row position down. SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1; - rRefRange.aEnd.IncRow(nDelta); + rRefRange.IncEndRowSticky(nDelta); return true; } @@ -3272,7 +3304,8 @@ void ScTokenArray::MoveReferenceRowReorder( const ScAddress& rPos, SCTAB nTab, S namespace { bool adjustSingleRefInName( - ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos ) + ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos, + ScComplexRefData* pEndOfComplex ) { ScAddress aAbs = rRef.toAbs(rPos); @@ -3292,8 +3325,16 @@ bool adjustSingleRefInName( // Adjust absolute column reference. if (rCxt.maRange.aStart.Col() <= rRef.Col() && rRef.Col() <= rCxt.maRange.aEnd.Col()) { - rRef.IncCol(rCxt.mnColDelta); - bChanged = true; + if (pEndOfComplex) + { + if (pEndOfComplex->IncEndColSticky( rCxt.mnColDelta, rPos)) + bChanged = true; + } + else + { + rRef.IncCol(rCxt.mnColDelta); + bChanged = true; + } } } @@ -3302,8 +3343,16 @@ bool adjustSingleRefInName( // Adjust absolute row reference. if (rCxt.maRange.aStart.Row() <= rRef.Row() && rRef.Row() <= rCxt.maRange.aEnd.Row()) { - rRef.IncRow(rCxt.mnRowDelta); - bChanged = true; + if (pEndOfComplex) + { + if (pEndOfComplex->IncEndRowSticky( rCxt.mnRowDelta, rPos)) + bChanged = true; + } + else + { + rRef.IncRow(rCxt.mnRowDelta); + bChanged = true; + } } } @@ -3330,20 +3379,21 @@ bool adjustDoubleRefInName( { // Selection intersects the referenced range. Only expand the // bottom position. - rRef.Ref2.IncRow(rCxt.mnRowDelta); + rRef.IncEndRowSticky(rCxt.mnRowDelta, rPos); return true; } } if ((rCxt.mnRowDelta && rRef.IsEntireCol()) || (rCxt.mnColDelta && rRef.IsEntireRow())) { - // References to entire col/row are not to be adjusted in the other axis. sc::RefUpdateContext aCxt( rCxt.mrDoc); // We only need a few parameters of RefUpdateContext. aCxt.maRange = rCxt.maRange; aCxt.mnColDelta = rCxt.mnColDelta; aCxt.mnRowDelta = rCxt.mnRowDelta; aCxt.mnTabDelta = rCxt.mnTabDelta; + + // References to entire col/row are not to be adjusted in the other axis. if (aCxt.mnRowDelta && rRef.IsEntireCol()) aCxt.mnRowDelta = 0; if (aCxt.mnColDelta && rRef.IsEntireRow()) @@ -3352,18 +3402,20 @@ bool adjustDoubleRefInName( // early bailout return bRefChanged; - if (adjustSingleRefInName(rRef.Ref1, aCxt, rPos)) + // Ref2 before Ref1 for sticky ends. + if (adjustSingleRefInName(rRef.Ref2, aCxt, rPos, &rRef)) bRefChanged = true; - if (adjustSingleRefInName(rRef.Ref2, aCxt, rPos)) + if (adjustSingleRefInName(rRef.Ref1, aCxt, rPos, nullptr)) bRefChanged = true; } else { - if (adjustSingleRefInName(rRef.Ref1, rCxt, rPos)) + // Ref2 before Ref1 for sticky ends. + if (adjustSingleRefInName(rRef.Ref2, rCxt, rPos, &rRef)) bRefChanged = true; - if (adjustSingleRefInName(rRef.Ref2, rCxt, rPos)) + if (adjustSingleRefInName(rRef.Ref1, rCxt, rPos, nullptr)) bRefChanged = true; } @@ -3396,7 +3448,7 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceInName( case svSingleRef: { ScSingleRefData& rRef = *p->GetSingleRef(); - if (adjustSingleRefInName(rRef, rCxt, rPos)) + if (adjustSingleRefInName(rRef, rCxt, rPos, nullptr)) aRes.mbReferenceModified = true; } break; @@ -3449,19 +3501,23 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceInName( if (aAbs.aStart.Row() < aDeleted.aStart.Row()) { - if (aDeleted.aEnd.Row() < aAbs.aEnd.Row()) - // Deleted in the middle. Make the reference shorter. - rRef.Ref2.IncRow(rCxt.mnRowDelta); - else - // Deleted at tail end. Cut off the lower part. - rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1); + if (!aAbs.IsEndRowSticky()) + { + if (aDeleted.aEnd.Row() < aAbs.aEnd.Row()) + // Deleted in the middle. Make the reference shorter. + rRef.Ref2.IncRow(rCxt.mnRowDelta); + else + // Deleted at tail end. Cut off the lower part. + rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1); + } } else { // Deleted at the top. Cut the top off and shift up. rRef.Ref1.SetAbsRow(aDeleted.aEnd.Row()+1); rRef.Ref1.IncRow(rCxt.mnRowDelta); - rRef.Ref2.IncRow(rCxt.mnRowDelta); + if (!aAbs.IsEndRowSticky()) + rRef.Ref2.IncRow(rCxt.mnRowDelta); } aRes.mbReferenceModified = true; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits