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

Reply via email to