summaryrefslogtreecommitdiff
path: root/sc/source/core
diff options
context:
space:
mode:
authorscito <info@scito.ch>2021-05-26 21:00:33 +0200
committerEike Rathke <erack@redhat.com>2021-05-29 22:46:11 +0200
commitbda9929821de620f1f85528abd1c4bba46d937d6 (patch)
treec6506a78d6388190aeea455fe7a752f4929b3b94 /sc/source/core
parente0c4cdf9a874bb40f028a26562e6399d39cb2c0f (diff)
tdf#142201 tdf#142065 fix cut paste transpose references, incl. undo
For cut paste transposed, references are not anymore updated during UpdateReference(), but all these references will be updated during UpdateTranspose(). In previous implementation, some references were moved (i.e. updated). This caused problems in UpdateTranspose(). A transposed flag is added to the clipparam. This flag is checked during UpdateReference(). UpdateTranspose() must only operate on (i.e. transpose) references pointing to the source area. That's why updates must not be made during UpdateReference(). Otherwise references pointing to the destination range will be transposed wrongly during UpdateTranspose(). References in formulas as well as in named ranges are fixed and tested. I've added unit tests for all these cases and I've enhanced my previous copy/paste test framework (6491c205acb3c166d93ef6a41199d344e21d98ac) for cut/paste tests, incl. undo. Before LibreOffice 7.2, adjusting of references in cut paste transposed was completely broken (tdf#71058, tdf#68976, tdf#142065 and tdf#142201). Change-Id: I8a8d295f1cc683572905ae9633e16d010cfb8792 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116073 Tested-by: Jenkins Reviewed-by: Eike Rathke <erack@redhat.com>
Diffstat (limited to 'sc/source/core')
-rw-r--r--sc/source/core/data/clipparam.cxx2
-rw-r--r--sc/source/core/data/document.cxx2
-rw-r--r--sc/source/core/data/formulacell.cxx15
-rw-r--r--sc/source/core/data/refupdatecontext.cxx15
-rw-r--r--sc/source/core/tool/rangenam.cxx2
-rw-r--r--sc/source/core/tool/refupdat.cxx6
-rw-r--r--sc/source/core/tool/token.cxx30
7 files changed, 65 insertions, 7 deletions
diff --git a/sc/source/core/data/clipparam.cxx b/sc/source/core/data/clipparam.cxx
index 1e8039deb4f5..c2255adee348 100644
--- a/sc/source/core/data/clipparam.cxx
+++ b/sc/source/core/data/clipparam.cxx
@@ -114,6 +114,8 @@ ScRange ScClipParam::getWholeRange() const
void ScClipParam::transpose(const ScDocument& rSrcDoc, bool bIncludeFiltered,
bool bIsMultiRangeRowFilteredTranspose)
{
+ mbTransposed = true;
+
switch (meDirection)
{
case Column:
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index cbce2c2a824b..c9a30aedc012 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2692,7 +2692,7 @@ void ScDocument::CopyBlockFromClip(
&& rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] )
++nFollow;
- sc::RefUpdateContext aRefCxt(*this);
+ sc::RefUpdateContext aRefCxt(*this, rCxt.getClipDoc());
aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow);
aRefCxt.mnColDelta = nDx;
aRefCxt.mnRowDelta = nDy;
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 2e4d81c362d7..da592434598b 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3799,12 +3799,16 @@ void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rD
ScAddress aOldPos = aPos;
bool bPosChanged = false; // Whether this cell has been moved
+ // Dest range is transposed
ScRange aDestRange( rDest, ScAddress(
static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
+
+ // cell within range
if ( aDestRange.In( aOldPos ) )
{
+ // References of these cells were not changed by ScTokenArray::AdjustReferenceOnMove()
// Count back Positions
SCCOL nRelPosX = aOldPos.Col();
SCROW nRelPosY = aOldPos.Row();
@@ -3839,6 +3843,15 @@ void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rD
{
rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
bRefChanged = true;
+
+ // Absolute sheet reference => set 3D flag.
+ // More than one sheet referenced => has to have both 3D flags.
+ // If end part has 3D flag => start part must have it too.
+ // The same behavior as in ScTokenArray::AdjustReferenceOnMove() is used for 3D-Flags.
+ rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
+ rRef.Ref1.SetFlag3D(
+ (rSource.aStart.Tab() != rDest.Tab() && !bPosChanged)
+ || !rRef.Ref1.IsTabRel() || rRef.Ref2.IsFlag3D());
}
}
}
@@ -3847,6 +3860,8 @@ void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rD
{
if (pUndoDoc)
{
+ // Similar to setOldCodeToUndo(), but it cannot be used due to the check
+ // pUndoDoc->GetCellType(aPos) == CELLTYPE_FORMULA
ScFormulaCell* pFCell = new ScFormulaCell(
*pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
diff --git a/sc/source/core/data/refupdatecontext.cxx b/sc/source/core/data/refupdatecontext.cxx
index ea1f8e5484ee..8faf1f105505 100644
--- a/sc/source/core/data/refupdatecontext.cxx
+++ b/sc/source/core/data/refupdatecontext.cxx
@@ -9,6 +9,7 @@
#include <refupdatecontext.hxx>
#include <algorithm>
+#include <clipparam.hxx>
#include <mtvelements.hxx>
namespace sc {
@@ -63,9 +64,17 @@ bool UpdatedRangeNames::isEmpty(SCTAB nTab) const
return it == maUpdatedNames.end();
}
-
-RefUpdateContext::RefUpdateContext(ScDocument& rDoc) :
- mrDoc(rDoc), meMode(URM_INSDEL), mnColDelta(0), mnRowDelta(0), mnTabDelta(0), mpBlockPos( nullptr ) {}
+RefUpdateContext::RefUpdateContext(ScDocument& rDoc, ScDocument* pClipdoc)
+ : mrDoc(rDoc)
+ , meMode(URM_INSDEL)
+ , mbTransposed(pClipdoc != nullptr && pClipdoc->GetClipParam().isTransposed())
+ , mnColDelta(0)
+ , mnRowDelta(0)
+ , mnTabDelta(0)
+ , mpBlockPos(nullptr)
+{
+ assert((pClipdoc == nullptr || pClipdoc->IsClipboard()) && "only nullptr or clipdoc allowed");
+}
bool RefUpdateContext::isInserted() const
{
diff --git a/sc/source/core/tool/rangenam.cxx b/sc/source/core/tool/rangenam.cxx
index c276f30bc783..7987b8a313a4 100644
--- a/sc/source/core/tool/rangenam.cxx
+++ b/sc/source/core/tool/rangenam.cxx
@@ -283,6 +283,7 @@ void ScRangeData::UpdateTranspose( const ScRange& rSource, const ScAddress& rDes
{
SingleDoubleRefModifier aMod( *t );
ScComplexRefData& rRef = aMod.Ref();
+ // Update only absolute references
if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() &&
(!rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel()) &&
( t->GetType() == svSingleRef ||
@@ -290,6 +291,7 @@ void ScRangeData::UpdateTranspose( const ScRange& rSource, const ScAddress& rDes
(!rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel()))))
{
ScRange aAbs = rRef.toAbs(rDoc, aPos);
+ // Check if the absolute reference of this range is pointing to the transposed source
if (ScRefUpdate::UpdateTranspose(rDoc, rSource, rDest, aAbs) != UR_NOTHING)
{
rRef.SetRange(rDoc.GetSheetLimits(), aAbs, aPos);
diff --git a/sc/source/core/tool/refupdat.cxx b/sc/source/core/tool/refupdat.cxx
index 9b587ef5732d..80229cf03dde 100644
--- a/sc/source/core/tool/refupdat.cxx
+++ b/sc/source/core/tool/refupdat.cxx
@@ -533,9 +533,9 @@ ScRefUpdateRes ScRefUpdate::UpdateTranspose(
const ScDocument& rDoc, const ScRange& rSource, const ScAddress& rDest, ScRange& rRef )
{
ScRefUpdateRes eRet = UR_NOTHING;
- if (rRef.aStart.Col() >= rSource.aStart.Col() && rRef.aEnd.Col() <= rSource.aEnd.Col() &&
- rRef.aStart.Row() >= rSource.aStart.Row() && rRef.aEnd.Row() <= rSource.aEnd.Row() &&
- rRef.aStart.Tab() >= rSource.aStart.Tab() && rRef.aEnd.Tab() <= rSource.aEnd.Tab())
+ // Only references in source range must be updated, i.e. no references in destination area.
+ // Otherwise existing references pointing to destination area will be wrongly transposed.
+ if (rSource.In(rRef))
{
// Source range contains the reference range.
SCCOL nCol1 = rRef.aStart.Col(), nCol2 = rRef.aEnd.Col();
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 86b6406c8098..04355b86a8ec 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -3314,6 +3314,15 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
{
ScSingleRefData& rRef = *p->GetSingleRef();
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ // Additionally, do not update the references from cells within the moved
+ // range as they lead to #REF! errors here. These #REF! cannot by fixed
+ // later in UpdateTranspose().
+ if (rCxt.mbTransposed && (aOldRange.In(rOldPos) || aOldRange.In(aAbs)))
+ break;
+
if (aOldRange.In(aAbs))
{
ScAddress aErrorPos( ScAddress::UNINITIALIZED );
@@ -3335,6 +3344,15 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
{
ScComplexRefData& rRef = *p->GetDoubleRef();
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ // Additionally, do not update the references from cells within the moved
+ // range as they lead to #REF! errors here. These #REF! cannot by fixed
+ // later in UpdateTranspose().
+ if (rCxt.mbTransposed && (aOldRange.In(rOldPos) || aOldRange.In(aAbs)))
+ break;
+
if (aOldRange.In(aAbs))
{
ScRange aErrorRange( ScAddress::UNINITIALIZED );
@@ -3984,6 +4002,12 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceInMovedName( const sc::RefUpdat
continue;
ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ if (rCxt.mbTransposed && aOldRange.In(aAbs))
+ break;
+
if (aOldRange.In(aAbs))
{
ScAddress aErrorPos( ScAddress::UNINITIALIZED );
@@ -4003,6 +4027,12 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceInMovedName( const sc::RefUpdat
continue;
ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
+
+ // Do not update the reference in transposed case (cut paste transposed).
+ // The reference will be updated in UpdateTranspose().
+ if (rCxt.mbTransposed && aOldRange.In(aAbs))
+ break;
+
if (aOldRange.In(aAbs))
{
ScRange aErrorRange( ScAddress::UNINITIALIZED );