sc/inc/drwlayer.hxx | 5 sc/qa/unit/data/ods/tdf139083_copy_without_resize.ods |binary sc/qa/unit/data/ods/tdf155093_double_names.ods |binary sc/qa/unit/scshapetest.cxx | 47 + sc/source/core/data/document.cxx | 23 sc/source/core/data/drwlayer.cxx | 444 ++++++++++++------ sc/source/core/data/table7.cxx | 8 7 files changed, 378 insertions(+), 149 deletions(-)
New commits: commit dfb0d118f6b23730bc632885eb4703a37eeaec16 Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Sat Apr 8 18:38:04 2023 +0200 Commit: Regina Henschel <rb.hensc...@t-online.de> CommitDate: Sun May 7 10:57:04 2023 +0200 tdf#139083 Only resize if 'resize with cell' is set The copy&paste of ranges with shapes had the following further bugs: * For cell anchored shapes the position was taken from the source rectangle instead of the anchor. * Resizing was calculated from source and destination rectangle, but should only depend on size of object range. * tdf#125938 Shapes were moved without adapting the anchor. * tdf#155091 Source with filtered rows produced doubled objects in paste. * The CopyToClip method has a useless NbcMove(size(0,0)). NbcMove is a move 'by', not a move 'to'. * tdf#155093 Pasted object has same name as source object and thus is not accessible via Navigator. * tdf#155094 Transposed pasted objects have wrong position * tdf#155095 Objects over collapsed group are incorrectly resized * tdf#141437, tdf#141436 transposed objects have wrong size Only objects, which can really resize, are now resized. In case of transposing no objects are resized. Transposing would need to transpose the object geometry, but that is missing. The offset inside the start anchor cell is adapted to the size of the destination cell to keep the anchor in this cell. Object resizing considers that filtered rows are removed whereas collapsed or hidden rows are shown in pasted area. Object resizing does no longer use global factors but calculate the desired snap rectangle and fits the object into it. Change-Id: I42924b28a2d652d8b70cb8e1a1d7ca4324b09cf6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150161 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.hensc...@t-online.de> diff --git a/sc/inc/drwlayer.hxx b/sc/inc/drwlayer.hxx index c127f597bab2..eea0b118b521 100644 --- a/sc/inc/drwlayer.hxx +++ b/sc/inc/drwlayer.hxx @@ -156,8 +156,9 @@ public: void CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rectangle& rRange ); void CopyFromClip( ScDrawLayer* pClipModel, - SCTAB nSourceTab, const tools::Rectangle& rSourceRange, - const ScAddress& rDestPos, const tools::Rectangle& rDestRange ); + SCTAB nSourceTab, const ScRange& rSourceRange, + const ScAddress& rDestPos, const ScRange& rDestRange, + bool bTransposing = false); void SetPageSize(sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos, const ScObjectHandling eObjectHandling = ScObjectHandling::RecalcPosMode); diff --git a/sc/qa/unit/data/ods/tdf139083_copy_without_resize.ods b/sc/qa/unit/data/ods/tdf139083_copy_without_resize.ods new file mode 100644 index 000000000000..ea3b8908ede2 Binary files /dev/null and b/sc/qa/unit/data/ods/tdf139083_copy_without_resize.ods differ diff --git a/sc/qa/unit/data/ods/tdf155093_double_names.ods b/sc/qa/unit/data/ods/tdf155093_double_names.ods new file mode 100644 index 000000000000..6dd7a69554c4 Binary files /dev/null and b/sc/qa/unit/data/ods/tdf155093_double_names.ods differ diff --git a/sc/qa/unit/scshapetest.cxx b/sc/qa/unit/scshapetest.cxx index 814384effe83..6f9a39c8eafa 100644 --- a/sc/qa/unit/scshapetest.cxx +++ b/sc/qa/unit/scshapetest.cxx @@ -999,6 +999,53 @@ CPPUNIT_TEST_FIXTURE(ScShapeTest, testLargeAnchorOffset) CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aOldPos, aNewPos, 1); } +CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf139083_copy_without_resize) +{ + // Load a document, which has a shape anchored to cell B2, but without 'resize with cell'. + // When the range B2:B3 is copied and pasted to D5, then the copied shape should keep its size. + createScDoc("ods/tdf139083_copy_without_resize.ods"); + + // Get document + ScDocument* pDoc = getScDoc(); + + // Copy cells B2:B3. They have row height 2cm and column width 3cm. + goToCell("$B$2:$B$3"); + dispatchCommand(mxComponent, ".uno:Copy", {}); + + // Paste to D5. There are row height 0.5cm and column width 1cm. + goToCell("$D$5"); + dispatchCommand(mxComponent, ".uno:Paste", {}); + + // Make sure original and pasted shape have the same size. + // Size of original shape is 2001x3002, without fix size of pasted shape was 668x750. + SdrObject* pObjOrig = lcl_getSdrObjectWithAssert(*pDoc, 0); // original shape + SdrObject* pObjPasted = lcl_getSdrObjectWithAssert(*pDoc, 1); // pasted shape + CPPUNIT_ASSERT_DOUBLES_EQUAL(2001, pObjOrig->GetSnapRect().GetWidth(), 1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3002, pObjOrig->GetSnapRect().GetHeight(), 1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(2001, pObjPasted->GetSnapRect().GetWidth(), 1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3002, pObjPasted->GetSnapRect().GetHeight(), 1); +} + +CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf155093_double_names) +{ + // Load a document, which has a shape in range B6:C14 with name "myArrow". When the range was + // copied and pasted, then the copied shape got the same name and thus was not accessible with + // Navigator. + createScDoc("ods/tdf155093_double_names.ods"); + ScDocument* pDoc = getScDoc(); + + // Copy and paste + goToCell("$B$6:$C$14"); + dispatchCommand(mxComponent, ".uno:Copy", {}); + goToCell("$D$16"); + dispatchCommand(mxComponent, ".uno:Paste", {}); + + // Make sure original and pasted shape have different names. + SdrObject* pObjOrig = lcl_getSdrObjectWithAssert(*pDoc, 0); // original shape + SdrObject* pObjPasted = lcl_getSdrObjectWithAssert(*pDoc, 1); // pasted shape + CPPUNIT_ASSERT(pObjOrig->GetName() != pObjPasted->GetName()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index fdf300a6e229..b39dcc8756cb 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -2409,13 +2409,14 @@ void ScDocument::TransposeClip(ScDocument* pTransClip, InsertDeleteFlags nFlags, // (mpDrawLayer in the original clipboard document is set only if there // are drawing objects to copy) + // ToDo: Loop over blocks of non-filtered rows in case of filtered rows exist. pTransClip->InitDrawLayer(); - tools::Rectangle aSourceRect = GetMMRect( aClipRange.aStart.Col(), aClipRange.aStart.Row(), - aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i ); - tools::Rectangle aDestRect = pTransClip->GetMMRect( 0, 0, - static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row()), - static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col()), i ); - pTransClip->mpDrawLayer->CopyFromClip( mpDrawLayer.get(), i, aSourceRect, ScAddress(0,0,i), aDestRect ); + ScAddress aTransposedEnd( + static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row() + aClipRange.aStart.Col()), + static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col() + aClipRange.aStart.Row()), i); + ScRange aDestRange(aClipRange.aStart, aTransposedEnd); + ScAddress aDestStart = aClipRange.aStart; + pTransClip->mpDrawLayer->CopyFromClip(mpDrawLayer.get(), i, aClipRange, aDestStart, aDestRange, true); } } } @@ -2690,12 +2691,10 @@ void ScDocument::CopyBlockFromClip( // For GetMMRect, the row heights in the target document must already be valid // (copied in an extra step before pasting, or updated after pasting cells, but // before pasting objects). - - tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect( - nCol1-nDx, nRow1-nDy, nCol2-nDx, nRow2-nDy, nClipTab ); - tools::Rectangle aDestRect = GetMMRect( nCol1, nRow1, nCol2, nRow2, i ); - mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRect, - ScAddress( nCol1, nRow1, i ), aDestRect ); + ScRange aSourceRange(nCol1 - nDx, nRow1 - nDy, nClipTab, nCol2 - nDx, nRow2 - nDy, nClipTab); + ScRange aDestRange(nCol1, nRow1, i, nCol2, nRow2, i); + mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRange, + ScAddress( nCol1, nRow1, i ), aDestRange); } } diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx index 8191dfcede66..134c7258ed4a 100644 --- a/sc/source/core/data/drwlayer.cxx +++ b/sc/source/core/data/drwlayer.cxx @@ -87,6 +87,7 @@ #include <docpool.hxx> #include <detfunc.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> +#include <clipparam.hxx> #include <memory> #include <algorithm> @@ -1785,19 +1786,18 @@ void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rec ScDrawLayer* pDestModel = nullptr; SdrPage* pDestPage = nullptr; + ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nTab); + SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); SdrObject* pOldObject = aIter.Next(); while (pOldObject) { - tools::Rectangle aObjRect = pOldObject->GetCurrentBoundRect(); - - bool bObjectInArea = rRange.Contains(aObjRect); + // Catch objects where the object itself is inside the rectangle to be copied. + bool bObjectInArea = rRange.Contains(pOldObject->GetCurrentBoundRect()); + // Catch objects whose anchor is inside the rectangle to be copied. const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); if (pObjData) - { - ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nTab); bObjectInArea = bObjectInArea || aClipRange.Contains(pObjData->maStart); - } // do not copy internal objects (detective) and note captions if (bObjectInArea && pOldObject->GetLayer() != SC_LAYER_INTERN @@ -1822,10 +1822,35 @@ void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rec { // Clone to target SdrModel rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*pDestModel)); - uno::Reference< chart2::XChartDocument > xOldChart( ScChartHelper::GetChartFromSdrObject( pOldObject ) ); if(!xOldChart.is())//#i110034# do not move charts as they lose all their data references otherwise - pNewObject->NbcMove(Size(0,0)); + { + if (pObjData) + { + // The object is anchored to cell. The position is determined by the start + // address. Copying into the clipboard does not change the anchor. + // ToDo: Adapt Offset relative to anchor cell size for cell anchored. + // ToDo: Adapt Offset and size for cell-anchored with resize objects. + // ToDo: Exclude object from resize if disallowed at object. + } + else + { + // The object is anchored to page. We make its position so, that the + // cell behind the object will have the same address in clipboard document as + // in source document. So we will be able to reconstruct the original cell + // address from position when pasting the object. + tools::Rectangle aObjRect = pOldObject->GetSnapRect(); + ScRange aPseudoAnchor + = pDoc->GetRange(nTab, aObjRect, true /*bHiddenAsZero*/); + tools::Rectangle aSourceCellRect + = GetCellRect(*pDoc, aPseudoAnchor.aStart, false /*bMergedCell*/); + tools::Rectangle aDestCellRect + = GetCellRect(*pClipDoc, aPseudoAnchor.aStart, false); + Point aMove = aDestCellRect.TopLeft() - aSourceCellRect.TopLeft(); + pNewObject->NbcMove(Size(aMove.getX(), aMove.getY())); + } + } + pDestPage->InsertObject( pNewObject.get() ); // no undo needed in clipboard document @@ -1884,8 +1909,9 @@ static bool lcl_MoveRanges( ::std::vector< ScRangeList >& rRangesVector, const S return bChanged; } -void ScDrawLayer::CopyFromClip( ScDrawLayer* pClipModel, SCTAB nSourceTab, const tools::Rectangle& rSourceRange, - const ScAddress& rDestPos, const tools::Rectangle& rDestRange ) +void ScDrawLayer::CopyFromClip(ScDrawLayer* pClipModel, SCTAB nSourceTab, + const ScRange& rSourceRange, const ScAddress& rDestPos, + const ScRange& rDestRange, bool bTransposing) { OSL_ENSURE( pDoc, "ScDrawLayer::CopyFromClip without document" ); if ( !pDoc ) @@ -1900,14 +1926,6 @@ void ScDrawLayer::CopyFromClip( ScDrawLayer* pClipModel, SCTAB nSourceTab, const return; } - bool bMirrorObj = ( rSourceRange.Left() < 0 && rSourceRange.Right() < 0 && - rDestRange.Left() > 0 && rDestRange.Right() > 0 ) || - ( rSourceRange.Left() > 0 && rSourceRange.Right() > 0 && - rDestRange.Left() < 0 && rDestRange.Right() < 0 ); - tools::Rectangle aMirroredSource = rSourceRange; - if ( bMirrorObj ) - MirrorRectRTL( aMirroredSource ); - SCTAB nDestTab = rDestPos.Tab(); SdrPage* pSrcPage = pClipModel->GetPage(static_cast<sal_uInt16>(nSourceTab)); @@ -1916,168 +1934,334 @@ void ScDrawLayer::CopyFromClip( ScDrawLayer* pClipModel, SCTAB nSourceTab, const if ( !pSrcPage || !pDestPage ) return; + ScDocument* pClipDoc = pClipModel->GetDocument(); + if (!pClipDoc) + return; // Can this happen? And if yes, what to do? + SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); SdrObject* pOldObject = aIter.Next(); + if (!pOldObject) + return; // no objects at all. Nothing to do. - ScDocument* pClipDoc = pClipModel->GetDocument(); // a clipboard document and its source share the same document item pool, // so the pointers can be compared to see if this is copy&paste within // the same document - bool bSameDoc = pDoc && pClipDoc && pDoc->GetPool() == pClipDoc->GetPool(); - bool bDestClip = pDoc && pDoc->IsClipboard(); + bool bSameDoc = pDoc->GetPool() == pClipDoc->GetPool(); + bool bDestClip = pDoc->IsClipboard(); // Happens while transposing. ToDo: Other cases? //#i110034# charts need correct sheet names for xml range conversion during load //so the target sheet name is temporarily renamed (if we have any SdrObjects) OUString aDestTabName; bool bRestoreDestTabName = false; - if( pOldObject && !bSameDoc && !bDestClip ) + if (!bSameDoc && !bDestClip) { - if( pDoc && pClipDoc ) + OUString aSourceTabName; + if (pClipDoc->GetName(nSourceTab, aSourceTabName) && pDoc->GetName(nDestTab, aDestTabName) + && aSourceTabName != aDestTabName && pDoc->ValidNewTabName(aSourceTabName)) { - OUString aSourceTabName; - if( pClipDoc->GetName( nSourceTab, aSourceTabName ) - && pDoc->GetName( nDestTab, aDestTabName ) ) - { - if( aSourceTabName != aDestTabName && - pDoc->ValidNewTabName(aSourceTabName) ) - { - bRestoreDestTabName = pDoc->RenameTab( nDestTab, aSourceTabName ); - } - } + bRestoreDestTabName = pDoc->RenameTab( nDestTab, aSourceTabName ); } } - // first mirror, then move - Size aMove( rDestRange.Left() - aMirroredSource.Left(), rDestRange.Top() - aMirroredSource.Top() ); - - tools::Long nDestWidth = rDestRange.GetWidth(); - tools::Long nDestHeight = rDestRange.GetHeight(); - tools::Long nSourceWidth = rSourceRange.GetWidth(); - tools::Long nSourceHeight = rSourceRange.GetHeight(); - - tools::Long nWidthDiff = nDestWidth - nSourceWidth; - tools::Long nHeightDiff = nDestHeight - nSourceHeight; + SCTAB nClipTab = bRestoreDestTabName ? nDestTab : nSourceTab; + ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nClipTab); - Fraction aHorFract(1,1); - Fraction aVerFract(1,1); - bool bResize = false; - // sizes can differ by 1 from twips->1/100mm conversion for equal cell sizes, - // don't resize to empty size when pasting into hidden columns or rows - if ( std::abs(nWidthDiff) > 1 && nDestWidth > 1 && nSourceWidth > 1 ) - { - aHorFract = Fraction( nDestWidth, nSourceWidth ); - bResize = true; - } - if ( std::abs(nHeightDiff) > 1 && nDestHeight > 1 && nSourceHeight > 1 ) - { - aVerFract = Fraction( nDestHeight, nSourceHeight ); - bResize = true; - } - Point aRefPos = rDestRange.TopLeft(); // for resizing (after moving) + // We are going to make all rectangle calculations on LTR, so determine whether doc is RTL. + bool bSourceRTL = pClipDoc->IsLayoutRTL(nSourceTab); + bool bDestRTL = pDoc->IsLayoutRTL(nDestTab); while (pOldObject) { - tools::Rectangle aObjRect = pOldObject->GetCurrentBoundRect(); + // ToDO: Can this happen? Such objects should not be in the clipboard document. // do not copy internal objects (detective) and note captions + if ((pOldObject->GetLayer() == SC_LAYER_INTERN) || IsNoteCaption(pOldObject)) + { + pOldObject = aIter.Next(); + continue; + } - SCTAB nClipTab = bRestoreDestTabName ? nDestTab : nSourceTab; - ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nClipTab); - - bool bObjectInArea = rSourceRange.Contains(aObjRect); + // 'aIter' considers all objects on pSrcPage. But ScDocument::CopyBlockFromClip, which is used + // for filtered data, acts not on the total range but only on parts of it. So we need to look, + // whether an object is really contained in the current rSourceRange. + // For cell anchored objects we use the start address of the anchor, for page anchored objects + // we use the cell range behind the bounding box of the shape. + ScAddress aSrcObjStart; const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); - if (pObjData) // Consider images anchored to the copied cell - bObjectInArea = bObjectInArea || aClipRange.Contains(pObjData->maStart); - if (bObjectInArea && (pOldObject->GetLayer() != SC_LAYER_INTERN) - && !IsNoteCaption(pOldObject)) + if (pObjData) // Object is anchored to cell. + { + aSrcObjStart = (*pObjData).maStart; + } + else // Object is anchored to page. + { + aSrcObjStart = pClipDoc->GetRange(nClipTab, pOldObject->GetCurrentBoundRect()).aStart; + } + if (!rSourceRange.Contains(aSrcObjStart)) + { + pOldObject = aIter.Next(); + continue; + } + // If object is anchored to a filtered cell, we will not copy it, because filtered rows are + // eliminated in paste. Copying would produce hidden objects which can only be accessed per + // macro. + if (pObjData && pClipDoc->RowFiltered((*pObjData).maStart.Row(), nSourceTab)) { - // Copy style sheet - auto pStyleSheet = pOldObject->GetStyleSheet(); - if (pStyleSheet && !bSameDoc) - pDoc->GetStyleSheetPool()->CopyStyleFrom(pClipModel->GetStyleSheetPool(), - pStyleSheet->GetName(), pStyleSheet->GetFamily(), true); + pOldObject = aIter.Next(); + continue; + } - // Clone to target SdrModel - rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this)); + // Copy style sheet + auto pStyleSheet = pOldObject->GetStyleSheet(); + if (pStyleSheet && !bSameDoc) + pDoc->GetStyleSheetPool()->CopyStyleFrom(pClipModel->GetStyleSheetPool(), + pStyleSheet->GetName(), + pStyleSheet->GetFamily(), true); - if ( bMirrorObj ) - MirrorRTL( pNewObject.get() ); // first mirror, then move + rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this)); + tools::Rectangle aObjRect = pOldObject->GetSnapRect(); + if (bSourceRTL) + { + MirrorRTL(pNewObject.get()); // We make the calculations in LTR. + MirrorRectRTL(aObjRect); + } - pNewObject->NbcMove( aMove ); - if ( bResize ) - pNewObject->NbcResize( aRefPos, aHorFract, aVerFract ); + bool bCanResize = IsResizeWithCell(*pOldObject) && !pOldObject->IsResizeProtect(); + // We do not resize charts or other OLE objects and do not resize when transposing. + bCanResize &= pOldObject->GetObjIdentifier() != SdrObjKind::OLE2; + bCanResize &= !bTransposing && !pClipDoc->GetClipParam().isTransposed(); + if (bCanResize) + { + // Filtered rows are eliminated on paste. Filtered cols do not exist as of May 2023. + // Collapsed or hidden rows/cols are shown on paste. + // Idea: First calculate top left cell and bottom right cell of pasted object. Then + // calculate the object corners inside these cell and from that build new snap rectangle. + // We assume that pObjData is valid and pObjData and aObjRect correspond to each other + // in the source document. + + // Start cell of object in source and destination. The case of a filtered start cell is + // already excluded above. aSrcObjStart = (*pObjData).maStart is already done above. + // If filtered rows exist in the total range, the range was divided into blocks which + // do not contain filtered rows. So the rows between start of aSourceRange and object + // start row do not contain filtered rows. + SCROW nStartRowDiff = aSrcObjStart.Row() - rSourceRange.aStart.Row(); + SCCOL nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col(); + ScAddress aDestObjStart = rDestRange.aStart; + aDestObjStart.IncCol(nStartColDiff); + aDestObjStart.IncRow(nStartRowDiff); + + // End cell of object in source and destination. We look at the amount of rows/cols to be + // added to get object end cell from object start cell. + ScAddress aSrcObjEnd = (*pObjData).maEnd; + SCCOL nColsToAdd = aSrcObjEnd.Col() - aSrcObjStart.Col(); + SCROW nRowsToAdd + = pClipDoc->CountNonFilteredRows(aSrcObjStart.Row(), aSrcObjEnd.Row(), nSourceTab) + - 1; + ScAddress aDestObjEnd = aDestObjStart; + aDestObjEnd.IncCol(nColsToAdd); + aDestObjEnd.IncRow(nRowsToAdd); + + // Position of object inside start and end cell in source. We describe the distance from + // cell corner to object corner as ratio of offset to cell width/height. + // We cannot use GetCellRect method, because that uses bHiddenAsZero=true. + Point aSrcObjTopLeftOffset = (*pObjData).maStartOffset; + tools::Rectangle aSrcStartRect + = pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(), + aSrcObjStart.Row(), nSourceTab, false /*bHiddenAsZero*/); + if (bSourceRTL) + MirrorRectRTL(aSrcStartRect); + double fStartXRatio + = aSrcStartRect.getOpenWidth() == 0 + ? 1.0 + : double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth()); + double fStartYRatio + = aSrcStartRect.getOpenHeight() == 0 + ? 1.0 + : double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight()); + + Point aSrcObjBottomRightOffset = (*pObjData).maEndOffset; + tools::Rectangle aSrcEndRect + = pClipDoc->GetMMRect(aSrcObjEnd.Col(), aSrcObjEnd.Row(), aSrcObjEnd.Col(), + aSrcObjEnd.Row(), nSourceTab, false /*bHiddenAsZero*/); + if (bSourceRTL) + MirrorRectRTL(aSrcEndRect); + double fEndXRatio + = aSrcEndRect.getOpenWidth() == 0 + ? 1.0 + : double(aSrcObjBottomRightOffset.X()) / double(aSrcEndRect.getOpenWidth()); + double fEndYRatio + = aSrcEndRect.getOpenHeight() == 0 + ? 1.0 + : double(aSrcObjBottomRightOffset.Y()) / double(aSrcEndRect.getOpenHeight()); + // The end cell given in pObjData might be filtered. In that case the object is cut at + // the lower cell edge. The offset is as large as the cell. + if (pClipDoc->RowFiltered(aSrcObjEnd.Row(), nSourceTab)) + fEndYRatio = 1.0; + + // Position of object inside start and end cell in destination + tools::Rectangle aDestStartRect + = GetCellRect(*pDoc, aDestObjStart, false /*bMergedCell*/); + if (bDestRTL) + MirrorRectRTL(aDestStartRect); + Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(), + fStartYRatio * aDestStartRect.getOpenHeight()); + Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset; + + tools::Rectangle aDestEndRect = GetCellRect(*pDoc, aDestObjEnd, false /*bMergedCell*/); + if (bDestRTL) + MirrorRectRTL(aDestEndRect); + Point aDestObjBottomRightOffset(fEndXRatio * aDestEndRect.getOpenWidth(), + fEndYRatio * aDestEndRect.getOpenHeight()); + Point aDestObjBottomRight = aDestEndRect.TopLeft() + aDestObjBottomRightOffset; + + // Fit new object into destination rectangle + tools::Rectangle aNewObjRect(aDestObjTopLeft, aDestObjBottomRight); + aNewObjRect = lcl_makeSafeRectangle(aNewObjRect); + if (pNewObject->GetObjIdentifier() == SdrObjKind::CustomShape) + pNewObject->AdjustToMaxRect(aNewObjRect); + else + pNewObject->SetSnapRect(aNewObjRect); + } + else + { + // We determine the MM-distance of the new object from its start cell in destination from + // the ratio of offset to cell width/height. Thus the object still starts in this cell + // even if the destination cell has different size. Otherwise we might lose objects when + // transposing. + + // Start Cell address in source and destination + SCCOLROW nStartRowDiff = pClipDoc->CountNonFilteredRows(rSourceRange.aStart.Row(), + aSrcObjStart.Row(), nSourceTab) + - 1; + SCCOLROW nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col(); + if (bTransposing) + std::swap(nStartRowDiff, nStartColDiff); + ScAddress aDestObjStart = rDestRange.aStart; + aDestObjStart.IncCol(nStartColDiff); + aDestObjStart.IncRow(nStartRowDiff); + + // Position of object inside start cell in source. + tools::Rectangle aSrcStartRect + = pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(), + aSrcObjStart.Row(), nSourceTab, false /*bHiddenAsZero*/); + if (bSourceRTL) + MirrorRectRTL(aSrcStartRect); + Point aSrcObjTopLeftOffset = pObjData ? (*pObjData).maStartOffset + : aObjRect.TopLeft() - aSrcStartRect.TopLeft(); + + double fStartXRatio + = aSrcStartRect.getOpenWidth() == 0 + ? 1.0 + : double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth()); + double fStartYRatio + = aSrcStartRect.getOpenHeight() == 0 + ? 1.0 + : double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight()); + + // Position of object inside start cell in destination + tools::Rectangle aDestStartRect + = GetCellRect(*pDoc, aDestObjStart, false /*bMergedCell*/); + if (bDestRTL) + MirrorRectRTL(aDestStartRect); + Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(), + fStartYRatio * aDestStartRect.getOpenHeight()); + Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset; + + // Move new object to new position + Point aMoveBy = aDestObjTopLeft - aObjRect.TopLeft(); + pNewObject->NbcMove(Size(aMoveBy.getX(), aMoveBy.getY())); + } + + if (bDestRTL) + MirrorRTL(pNewObject.get()); - pDestPage->InsertObject( pNewObject.get() ); + // Changing object position or size does not automatically change its anchor. + if (IsCellAnchored(*pOldObject)) + SetCellAnchoredFromPosition(*pNewObject, *pDoc, nDestTab, + IsResizeWithCell(*pOldObject)); + + // InsertObject includes broadcasts + // MakeNameUnique makes the pasted objects accessible via Navigator. + if (bDestClip) + pDestPage->InsertObject(pNewObject.get()); + else + { if (bRecording) - AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) ); + pDoc->EnableUndo(false); + pDestPage->InsertObjectThenMakeNameUnique(pNewObject.get()); + if (bRecording) + pDoc->EnableUndo(true); + } - //#i110034# handle chart data references (after InsertObject) + if (bRecording) + AddCalcUndo(std::make_unique<SdrUndoInsertObj>(*pNewObject)); - if ( pNewObject->GetObjIdentifier() == SdrObjKind::OLE2 ) + //#i110034# handle chart data references (after InsertObject) + if (pNewObject->GetObjIdentifier() == SdrObjKind::OLE2) + { + uno::Reference<embed::XEmbeddedObject> xIPObj + = static_cast<SdrOle2Obj*>(pNewObject.get())->GetObjRef(); + uno::Reference<embed::XClassifiedObject> xClassified = xIPObj; + SvGlobalName aObjectClassName; + if (xClassified.is()) { - uno::Reference< embed::XEmbeddedObject > xIPObj = static_cast<SdrOle2Obj*>(pNewObject.get())->GetObjRef(); - uno::Reference< embed::XClassifiedObject > xClassified = xIPObj; - SvGlobalName aObjectClassName; - if ( xClassified.is() ) + try { - try { - aObjectClassName = SvGlobalName( xClassified->getClassID() ); - } catch( uno::Exception& ) - { - // TODO: handle error? - } + aObjectClassName = SvGlobalName(xClassified->getClassID()); } + catch (uno::Exception&) + { + // TODO: handle error? + } + } - if ( xIPObj.is() && SotExchange::IsChart( aObjectClassName ) ) + if (xIPObj.is() && SotExchange::IsChart(aObjectClassName)) + { + uno::Reference<chart2::XChartDocument> xNewChart( + ScChartHelper::GetChartFromSdrObject(pNewObject.get())); + if (xNewChart.is() && !xNewChart->hasInternalDataProvider()) { - uno::Reference< chart2::XChartDocument > xNewChart( ScChartHelper::GetChartFromSdrObject( pNewObject.get() ) ); - if( xNewChart.is() && !xNewChart->hasInternalDataProvider() ) + OUString aChartName + = static_cast<SdrOle2Obj*>(pNewObject.get())->GetPersistName(); + ::std::vector<ScRangeList> aRangesVector; + pDoc->GetChartRanges(aChartName, aRangesVector, *pDoc); + if (!aRangesVector.empty()) { - OUString aChartName = static_cast<SdrOle2Obj*>(pNewObject.get())->GetPersistName(); - ::std::vector< ScRangeList > aRangesVector; - pDoc->GetChartRanges( aChartName, aRangesVector, *pDoc ); - if( !aRangesVector.empty() ) - { - bool bInSourceRange = false; - if ( pClipDoc ) - { - bInSourceRange = lcl_IsAllInRange( aRangesVector, aClipRange ); - } + bool bInSourceRange = false; + bInSourceRange = lcl_IsAllInRange(aRangesVector, aClipRange); - // always lose references when pasting into a clipboard document (transpose) - if ( ( bInSourceRange || bSameDoc ) && !bDestClip ) + // always lose references when pasting into a clipboard document (transpose) + if ((bInSourceRange || bSameDoc) && !bDestClip) + { + if (bInSourceRange) { - if ( bInSourceRange ) - { - if ( rDestPos != aClipRange.aStart ) - { - // update the data ranges to the new (copied) position - if ( lcl_MoveRanges( aRangesVector, aClipRange, rDestPos, *pDoc ) ) - pDoc->SetChartRanges( aChartName, aRangesVector ); - } - } - else + if (rDestPos != aClipRange.aStart) { - // leave the ranges unchanged + // update the data ranges to the new (copied) position + if (lcl_MoveRanges(aRangesVector, aClipRange, rDestPos, *pDoc)) + pDoc->SetChartRanges(aChartName, aRangesVector); } } else { - // pasting into a new document without the complete source data - // -> break connection to source data and switch to own data - - uno::Reference< chart::XChartDocument > xOldChartDoc( ScChartHelper::GetChartFromSdrObject( pOldObject ), uno::UNO_QUERY ); - uno::Reference< chart::XChartDocument > xNewChartDoc( xNewChart, uno::UNO_QUERY ); - if( xOldChartDoc.is() && xNewChartDoc.is() ) - xNewChartDoc->attachData( xOldChartDoc->getData() ); - - // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc) + // leave the ranges unchanged } } + else + { + // pasting into a new document without the complete source data + // -> break connection to source data and switch to own data + uno::Reference<chart::XChartDocument> xOldChartDoc( + ScChartHelper::GetChartFromSdrObject(pOldObject), uno::UNO_QUERY); + uno::Reference<chart::XChartDocument> xNewChartDoc(xNewChart, + uno::UNO_QUERY); + if (xOldChartDoc.is() && xNewChartDoc.is()) + xNewChartDoc->attachData(xOldChartDoc->getData()); + + // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc) + } } } } } - pOldObject = aIter.Next(); } diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx index 230833e8b7a6..49929f99da94 100644 --- a/sc/source/core/data/table7.cxx +++ b/sc/source/core/data/table7.cxx @@ -169,12 +169,10 @@ void ScTable::CopyOneCellFromClip( const ScAddress aSrcStartPos = rCxt.getClipDoc()->GetClipParam().getWholeRange().aStart; const ScAddress aSrcEndPos = rCxt.getClipDoc()->GetClipParam().getWholeRange().aEnd; - tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect( - aSrcStartPos.Col(), aSrcStartPos.Row(), aSrcEndPos.Col(), aSrcEndPos.Row(), - aSrcStartPos.Tab()); - tools::Rectangle aDestRect = GetDoc().GetMMRect(nCol1, nRow1, nCol2, nRow2, nTab); + ScRange aSourceRange(aSrcStartPos, aSrcEndPos); + ScRange aDestRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab); pDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), aSrcStartPos.Tab(), - aSourceRect, ScAddress(nCol1, nRow1, nTab), aDestRect); + aSourceRange, ScAddress(nCol1, nRow1, nTab), aDestRange); } }