summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2015-12-09 20:58:22 +0100
committerEike Rathke <erack@redhat.com>2015-12-10 00:04:55 +0100
commitb35bfb9f439910c14dc6161534d66a5a51cb1121 (patch)
treebbc781ba90a65a3c8d495388a859edf127316830
parent6158def661f8281c4fc3493c7e2a1753d3891437 (diff)
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 (cherry picked from commit cfecdd6199710921f8fd921f615203c9e34c551e)
-rw-r--r--sc/inc/address.hxx27
-rw-r--r--sc/inc/refdata.hxx10
-rw-r--r--sc/source/core/tool/address.cxx60
-rw-r--r--sc/source/core/tool/refdata.cxx58
-rw-r--r--sc/source/core/tool/refupdat.cxx14
-rw-r--r--sc/source/core/tool/token.cxx114
6 files changed, 251 insertions, 32 deletions
diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx
index aa700d99b6df..4bf5ee5404e4 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 b96acb7a7eec..5f3e7626940a 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 f69a8221e3eb..b7f9a5b526fb 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 0878cb12d431..9811c08b72c1 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 8ca5373eb7e1..602a636c6118 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 41be86a84fe0..49bbe2eccad9 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;