diff options
-rw-r--r-- | sc/inc/document.hxx | 3 | ||||
-rw-r--r-- | sc/inc/drwlayer.hxx | 14 | ||||
-rw-r--r-- | sc/inc/table.hxx | 4 | ||||
-rw-r--r-- | sc/qa/unit/data/ods/tdf137081_RTL_page_anchored.ods | bin | 0 -> 10079 bytes | |||
-rw-r--r-- | sc/qa/unit/data/ods/tdf137082_LTR_arrow_image.ods | bin | 0 -> 10430 bytes | |||
-rw-r--r-- | sc/qa/unit/data/ods/tdf137082_RTL_cell_anchored.ods | bin | 0 -> 9779 bytes | |||
-rw-r--r-- | sc/qa/unit/scshapetest.cxx | 155 | ||||
-rw-r--r-- | sc/source/core/data/documen9.cxx | 4 | ||||
-rw-r--r-- | sc/source/core/data/document.cxx | 14 | ||||
-rw-r--r-- | sc/source/core/data/drwlayer.cxx | 162 | ||||
-rw-r--r-- | sc/source/core/data/table2.cxx | 6 | ||||
-rw-r--r-- | sc/source/filter/xml/xmlexprt.cxx | 78 | ||||
-rw-r--r-- | sc/source/ui/docshell/docfunc.cxx | 2 | ||||
-rw-r--r-- | sc/source/ui/undo/undotab.cxx | 2 |
14 files changed, 375 insertions, 69 deletions
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index a8bfba521437..79091e89504c 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -53,6 +53,7 @@ #include <vector> #include "markdata.hxx" +#include "drwlayer.hxx" namespace com::sun::star::chart2 { class XChartDocument; } @@ -963,7 +964,7 @@ public: bool IsStreamValidLocked() const { return mbStreamValidLocked; } bool IsPendingRowHeights( SCTAB nTab ) const; void SetPendingRowHeights( SCTAB nTab, bool bSet ); - SC_DLLPUBLIC void SetLayoutRTL( SCTAB nTab, bool bRTL ); + SC_DLLPUBLIC void SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling = ScObjectHandling::RecalcPosMode); SC_DLLPUBLIC bool IsLayoutRTL( SCTAB nTab ) const; SC_DLLPUBLIC bool IsNegativePage( SCTAB nTab ) const; SC_DLLPUBLIC void SetScenario( SCTAB nTab, bool bFlag ); diff --git a/sc/inc/drwlayer.hxx b/sc/inc/drwlayer.hxx index 030fd3855ec8..008e56f8b8c6 100644 --- a/sc/inc/drwlayer.hxx +++ b/sc/inc/drwlayer.hxx @@ -86,6 +86,14 @@ public: virtual void Redo() override; }; +// for ScDrawLayer::SetPageSize +enum class ScObjectHandling +{ + RecalcPosMode, // used for row height or col width changes + MoveRTLMode, // used for switch to RTL during import of right-to-left sheet + MirrorRTLMode // used for switch between RTL and LTR by .uno:SheetRightToLeft +}; + class SC_DLLPUBLIC ScDrawLayer final : public FmFormModel { private: @@ -150,10 +158,12 @@ public: SCTAB nSourceTab, const tools::Rectangle& rSourceRange, const ScAddress& rDestPos, const tools::Rectangle& rDestRange ); - void SetPageSize( sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos ); + void SetPageSize(sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos, + const ScObjectHandling eObjectHandling = ScObjectHandling::RecalcPosMode); // mirror or move between positive and negative positions for RTL void MirrorRTL( SdrObject* pObj ); + void MoveRTL(SdrObject* pObj); static void MirrorRectRTL( tools::Rectangle& rRect ); // for bounding rectangles etc. /** Returns the rectangle for the passed cell address in 1/100 mm. @@ -174,7 +184,7 @@ public: static bool IsResizeWithCell( const SdrObject& rObj ); static void SetPageAnchored( SdrObject& ); static void SetCellAnchored( SdrObject&, const ScDrawObjData &rAnchor ); - static void SetVisualCellAnchored( SdrObject&, const ScDrawObjData &rAnchor ); + static void SetNonRotatedAnchor( SdrObject&, const ScDrawObjData &rAnchor ); // Updates rAnchor based on position of rObj static void GetCellAnchorFromPosition( diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index a09781366821..309d49d4f140 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -34,6 +34,7 @@ #include "calcmacros.hxx" #include <formula/errorcodes.hxx> #include "document.hxx" +#include "drwlayer.hxx" #include <set> #include <memory> @@ -965,7 +966,8 @@ public: bool IsSortCollatorGlobal() const; void InitSortCollator( const ScSortParam& rPar ); void DestroySortCollator(); - void SetDrawPageSize( bool bResetStreamValid = true, bool bUpdateNoteCaptionPos = true ); + void SetDrawPageSize( bool bResetStreamValid = true, bool bUpdateNoteCaptionPos = true, + const ScObjectHandling eObjectHandling = ScObjectHandling::RecalcPosMode); void SetRangeName(std::unique_ptr<ScRangeName> pNew); ScRangeName* GetRangeName() const; diff --git a/sc/qa/unit/data/ods/tdf137081_RTL_page_anchored.ods b/sc/qa/unit/data/ods/tdf137081_RTL_page_anchored.ods Binary files differnew file mode 100644 index 000000000000..f0d0583d5934 --- /dev/null +++ b/sc/qa/unit/data/ods/tdf137081_RTL_page_anchored.ods diff --git a/sc/qa/unit/data/ods/tdf137082_LTR_arrow_image.ods b/sc/qa/unit/data/ods/tdf137082_LTR_arrow_image.ods Binary files differnew file mode 100644 index 000000000000..ed315c8f6996 --- /dev/null +++ b/sc/qa/unit/data/ods/tdf137082_LTR_arrow_image.ods diff --git a/sc/qa/unit/data/ods/tdf137082_RTL_cell_anchored.ods b/sc/qa/unit/data/ods/tdf137082_RTL_cell_anchored.ods Binary files differnew file mode 100644 index 000000000000..7b39927b1317 --- /dev/null +++ b/sc/qa/unit/data/ods/tdf137082_RTL_cell_anchored.ods diff --git a/sc/qa/unit/scshapetest.cxx b/sc/qa/unit/scshapetest.cxx index 1c54c0e5ac5c..6d8fa9f1f09a 100644 --- a/sc/qa/unit/scshapetest.cxx +++ b/sc/qa/unit/scshapetest.cxx @@ -40,6 +40,9 @@ public: ScShapeTest(); void saveAndReload(css::uno::Reference<css::lang::XComponent>& xComponent, const OUString& rFilter); + void testTdf137082_LTR_to_RTL(); + void testTdf137082_RTL_cell_anchored(); + void testTdf137081_RTL_page_anchored(); void testTdf139583_Rotate180deg(); void testTdf137033_FlipHori_Resize(); void testTdf137033_RotShear_ResizeHide(); @@ -60,6 +63,9 @@ public: void testCustomShapeCellAnchoredRotatedShape(); CPPUNIT_TEST_SUITE(ScShapeTest); + CPPUNIT_TEST(testTdf137082_LTR_to_RTL); + CPPUNIT_TEST(testTdf137082_RTL_cell_anchored); + CPPUNIT_TEST(testTdf137081_RTL_page_anchored); CPPUNIT_TEST(testTdf139583_Rotate180deg); CPPUNIT_TEST(testTdf137033_FlipHori_Resize); CPPUNIT_TEST(testTdf137033_RotShear_ResizeHide); @@ -186,6 +192,155 @@ static SdrObject* lcl_getSdrObjectWithAssert(ScDocument& rDoc, sal_uInt16 nObjNu return pObj; } +void ScShapeTest::testTdf137082_LTR_to_RTL() +{ + // Before the fix for tdf137081 and tdf137082, when flipping sheet from LTR to RTL, page anchored + // shapes were mirrored, but cell anchored shapes not. This was changed so, that shapes are always + // mirrored. Graphics are still not mirrored but shifted. This test makes sure a shape is mirrored + // and an image is not mirrored. + + OUString aFileURL; + createFileURL(u"tdf137082_LTR_arrow_image.ods", aFileURL); + uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL); + CPPUNIT_ASSERT(xComponent.is()); + + // Get document + ScDocShell* pDocSh = lcl_getScDocShellWithAssert(xComponent); + ScDocument& rDoc = pDocSh->GetDocument(); + + // Get objects and their transformation angles + SdrObject* pObjCS = lcl_getSdrObjectWithAssert(rDoc, 0); + const Degree100 nRotateLTR = pObjCS->GetRotateAngle(); + SdrObject* pObjImage = lcl_getSdrObjectWithAssert(rDoc, 1); + const Degree100 nShearLTR = pObjImage->GetShearAngle(); + + // Switch to RTL + ScTabViewShell* pViewShell = lcl_getScTabViewShellWithAssert(pDocSh); + pViewShell->GetViewData().GetDispatcher().Execute(FID_TAB_RTL); + + // Check custom shape is mirrored, image not. + const Degree100 nShearRTLActual = pObjImage->GetShearAngle(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("image should not be mirrored", nShearLTR.get(), + nShearRTLActual.get()); + const Degree100 nRotateRTLExpected = 36000_deg100 - nRotateLTR; + const Degree100 nRotateRTLActual = pObjCS->GetRotateAngle(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("custom shape should be mirrored", nRotateRTLExpected.get(), + nRotateRTLActual.get()); + + pDocSh->DoClose(); +} + +void ScShapeTest::testTdf137082_RTL_cell_anchored() +{ + // Error was, that cell anchored custom shapes wrote wrong offsets to file and thus were wrong on + // reloading. The file contains one custome shape with "resize" and another one without. + OUString aFileURL; + createFileURL(u"tdf137082_RTL_cell_anchored.ods", aFileURL); + uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL); + CPPUNIT_ASSERT(xComponent.is()); + + // Get document + ScDocShell* pDocSh = lcl_getScDocShellWithAssert(xComponent); + ScDocument& rDoc = pDocSh->GetDocument(); + + // Expected values. + const Point aTopLeftA(-20500, 3500); // shape A without "resize" + const Point aTopLeftB(-9500, 3500); // shape B with "resize" + const Size aSize(2278, 5545); // both + const tools::Rectangle aSnapRectA(aTopLeftA, aSize); + const tools::Rectangle aSnapRectB(aTopLeftB, aSize); + + // Test reading was correct + SdrObject* pObj = lcl_getSdrObjectWithAssert(rDoc, 0); + lcl_AssertRectEqualWithTolerance("load shape A: ", aSnapRectA, pObj->GetSnapRect(), 1); + pObj = lcl_getSdrObjectWithAssert(rDoc, 1); + lcl_AssertRectEqualWithTolerance("load shape B: ", aSnapRectB, pObj->GetSnapRect(), 1); + + // Save and reload. + saveAndReload(xComponent, "calc8"); + CPPUNIT_ASSERT(xComponent); + + // Get document + pDocSh = lcl_getScDocShellWithAssert(xComponent); + ScDocument& rDoc2 = pDocSh->GetDocument(); + + // And test again + pObj = lcl_getSdrObjectWithAssert(rDoc2, 0); + lcl_AssertRectEqualWithTolerance("reload shape A: ", aSnapRectA, pObj->GetSnapRect(), 1); + pObj = lcl_getSdrObjectWithAssert(rDoc2, 1); + lcl_AssertRectEqualWithTolerance("reload shape B: ", aSnapRectB, pObj->GetSnapRect(), 1); + + pDocSh->DoClose(); +} + +void ScShapeTest::testTdf137081_RTL_page_anchored() +{ + // Error was, that page anchored lines and custom shapes were mirrored on opening. The document + // contains measure line, polyline and transformed custom shape. + OUString aFileURL; + createFileURL(u"tdf137081_RTL_page_anchored.ods", aFileURL); + uno::Reference<css::lang::XComponent> xComponent = loadFromDesktop(aFileURL); + CPPUNIT_ASSERT(xComponent.is()); + + // Get document + ScDocShell* pDocSh = lcl_getScDocShellWithAssert(xComponent); + ScDocument& rDoc = pDocSh->GetDocument(); + + // Expected values. + // Measure line + const Point aStart(-3998, 2490); + const Point aEnd(-8488, 5490); + // Polyline + const Point aFirst(-10010, 2500); + const Point aSecond(-14032, 5543); + const Point aThird(-14500, 3500); + // Custom shape + const Point aTopLeft(-20500, 4583); + + // Test reading was correct + SdrObject* pObj = lcl_getSdrObjectWithAssert(rDoc, 0); + // Measure line + lcl_AssertPointEqualWithTolerance("measure line start", aStart, pObj->GetPoint(0), 1); + lcl_AssertPointEqualWithTolerance("measure line end", aEnd, pObj->GetPoint(1), 1); + // Polyline + pObj = lcl_getSdrObjectWithAssert(rDoc, 1); + lcl_AssertPointEqualWithTolerance("polyline 1: ", aFirst, pObj->GetPoint(0), 1); + lcl_AssertPointEqualWithTolerance("polyline 2: ", aSecond, pObj->GetPoint(1), 1); + lcl_AssertPointEqualWithTolerance("polyline 3: ", aThird, pObj->GetPoint(2), 1); + //Custom shape + SdrObjCustomShape* pObjCS + = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(rDoc, 2)); + CPPUNIT_ASSERT(!pObjCS->IsMirroredX()); + lcl_AssertPointEqualWithTolerance("custom shape top left: ", aTopLeft, + pObjCS->GetLogicRect().TopLeft(), 1); + + // Save and reload. + saveAndReload(xComponent, "calc8"); + CPPUNIT_ASSERT(xComponent); + + // Get document + pDocSh = lcl_getScDocShellWithAssert(xComponent); + ScDocument& rDoc2 = pDocSh->GetDocument(); + + // And test again + pObj = lcl_getSdrObjectWithAssert(rDoc2, 0); + // Measure line + lcl_AssertPointEqualWithTolerance("measure line start", aStart, pObj->GetPoint(0), 1); + lcl_AssertPointEqualWithTolerance("measure line end", aEnd, pObj->GetPoint(1), 1); + // Polyline + pObj = lcl_getSdrObjectWithAssert(rDoc2, 1); + lcl_AssertPointEqualWithTolerance("polyline 1: ", aFirst, pObj->GetPoint(0), 1); + lcl_AssertPointEqualWithTolerance("polyline 2: ", aSecond, pObj->GetPoint(1), 1); + lcl_AssertPointEqualWithTolerance("polyline 3: ", aThird, pObj->GetPoint(2), 1); + //Custom shape + pObjCS = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(rDoc2, 2)); + CPPUNIT_ASSERT(!pObjCS->IsMirroredX()); + lcl_AssertPointEqualWithTolerance("custom shape top left: ", aTopLeft, + pObjCS->GetLogicRect().TopLeft(), 1); + + pDocSh->DoClose(); +} + void ScShapeTest::testTdf139583_Rotate180deg() { // Load an empty document. diff --git a/sc/source/core/data/documen9.cxx b/sc/source/core/data/documen9.cxx index 46d1b1c3750d..f44159d6c436 100644 --- a/sc/source/core/data/documen9.cxx +++ b/sc/source/core/data/documen9.cxx @@ -600,8 +600,10 @@ void ScDocument::SetImportingXML( bool bVal ) for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]; nTab++ ) if ( maTabs[nTab]->IsLoadingRTL() ) { + // SetLayoutRTL => SetDrawPageSize => ScDrawLayer::SetPageSize, includes RTL-mirroring; + // bImportingXML must be cleared first maTabs[nTab]->SetLoadingRTL( false ); - SetLayoutRTL( nTab, true ); // includes mirroring; bImportingXML must be cleared first + SetLayoutRTL( nTab, true, ScObjectHandling::MoveRTLMode ); } } diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index be588062c5df..48282126b0fc 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -945,7 +945,7 @@ void ScDocument::SetPendingRowHeights( SCTAB nTab, bool bSet ) maTabs[nTab]->SetPendingRowHeights( bSet ); } -void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL ) +void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling) { if ( !(ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab]) ) return; @@ -961,10 +961,9 @@ void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL ) } maTabs[nTab]->SetLayoutRTL( bRTL ); // only sets the flag - maTabs[nTab]->SetDrawPageSize(); - - // mirror existing objects: + maTabs[nTab]->SetDrawPageSize(true, true, eObjectHandling); + // objects are already repositioned via SetDrawPageSize, only writing mode is missing if (!mpDrawLayer) return; @@ -977,14 +976,7 @@ void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL ) SdrObject* pObject = aIter.Next(); while (pObject) { - // objects with ScDrawObjData are re-positioned in SetPageSize, - // don't mirror again - ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject ); - if ( !pData ) - mpDrawLayer->MirrorRTL( pObject ); - pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB ); - pObject = aIter.Next(); } } diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx index 067b2bc38b0f..3d2eb834a6c0 100644 --- a/sc/source/core/data/drwlayer.cxx +++ b/sc/source/core/data/drwlayer.cxx @@ -570,7 +570,8 @@ void ScDrawLayer::MoveCells( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SC } } -void ScDrawLayer::SetPageSize( sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos ) +void ScDrawLayer::SetPageSize(sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos, + const ScObjectHandling eObjectHandling) { SdrPage* pPage = GetPage(nPageNo); if (!pPage) @@ -582,31 +583,65 @@ void ScDrawLayer::SetPageSize( sal_uInt16 nPageNo, const Size& rSize, bool bUpda Broadcast( ScTabSizeChangedHint( static_cast<SCTAB>(nPageNo) ) ); // SetWorkArea() on the views } - // Implement Detective lines (adjust to new heights / widths) - // even if size is still the same - // (individual rows/columns can have been changed)) - // Do not call RecalcPos while loading, because row height is not finished, when SetPageSize // is called first time. Instead the objects are initialized from ScXMLImport::endDocument() and // RecalcPos is called from there. if (!pDoc || pDoc->IsImportingXML()) return; + // Implement Detective lines (adjust to new heights / widths) + // even if size is still the same + // (individual rows/columns can have been changed)) + bool bNegativePage = pDoc && pDoc->IsNegativePage( static_cast<SCTAB>(nPageNo) ); // Disable mass broadcasts from drawing objects' position changes. bool bWasLocked = isLocked(); setLock(true); + const size_t nCount = pPage->GetObjCount(); for ( size_t i = 0; i < nCount; ++i ) { SdrObject* pObj = pPage->GetObj( i ); ScDrawObjData* pData = GetObjDataTab( pObj, static_cast<SCTAB>(nPageNo) ); - if( pData ) - RecalcPos( pObj, *pData, bNegativePage, bUpdateNoteCaptionPos ); + if( pData ) // cell anchored + { + if (pData->meType == ScDrawObjData::DrawingObject + || pData->meType == ScDrawObjData::ValidationCircle) + { + switch (eObjectHandling) + { + case ScObjectHandling::RecalcPosMode: + RecalcPos(pObj, *pData, bNegativePage, bUpdateNoteCaptionPos); + break; + case ScObjectHandling::MoveRTLMode: + MoveRTL(pObj); + break; + case ScObjectHandling::MirrorRTLMode: + MirrorRTL(pObj); + break; + } + } + else // DetectiveArrow and CellNote + RecalcPos(pObj, *pData, bNegativePage, bUpdateNoteCaptionPos); + } + else // page anchored + { + switch (eObjectHandling) + { + case ScObjectHandling::MoveRTLMode: + MoveRTL(pObj); + break; + case ScObjectHandling::MirrorRTLMode: + MirrorRTL(pObj); + break; + case ScObjectHandling::RecalcPosMode: // does not occur for page anchored shapes + break; + } + } } - setLock(bWasLocked); + setLock(bWasLocked); } namespace @@ -1177,9 +1212,9 @@ void ScDrawLayer::RecalcPos( SdrObject* pObj, ScDrawObjData& rData, bool bNegati else pObj->SetSnapRect(rData.getShapeRect()); - // update 'unrotated anchor' it's the anchor we persist, it must be kept in sync - // with the normal Anchor. - ResizeLastRectFromAnchor(pObj, rNoRotatedAnchor, true, bNegativePage, bCanResize); + // The shape rectangle in the 'unrotated' anchor needs to be updated to the changed + // object geometry. It is used in adjustAnchoredPosition() in ScDrawView::Notify(). + rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible()); } } else @@ -1190,6 +1225,7 @@ void ScDrawLayer::RecalcPos( SdrObject* pObj, ScDrawObjData& rData, bool bNegati if (bRecording) AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); pObj->SetRelativePos( aPos ); + rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible()); } } /* @@ -1954,6 +1990,10 @@ void ScDrawLayer::CopyFromClip( ScDrawLayer* pClipModel, SCTAB nSourceTab, const void ScDrawLayer::MirrorRTL( SdrObject* pObj ) { + OSL_ENSURE( pDoc, "ScDrawLayer::MirrorRTL - missing document" ); + if( !pDoc ) + return; + sal_uInt16 nIdent = pObj->GetObjIdentifier(); // don't mirror OLE or graphics, otherwise ask the object @@ -1968,23 +2008,87 @@ void ScDrawLayer::MirrorRTL( SdrObject* pObj ) if (bCanMirror) { - Point aRef1( 0, 0 ); - Point aRef2( 0, 1 ); - if (bRecording) - AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); - pObj->Mirror( aRef1, aRef2 ); + ScDrawObjData* pData = GetObjData(pObj); + if (pData) // cell anchored + { + // Remember values from positive side. + const tools::Rectangle aOldSnapRect = pObj->GetSnapRect(); + const tools::Rectangle aOldLogicRect = pObj->GetLogicRect(); + // Generate noRotate anchor if missing. + ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj); + if (!pNoRotatedAnchor) + { + ScDrawObjData aNoRotateAnchor; + const tools::Rectangle aLogicRect(pObj->GetLogicRect()); + GetCellAnchorFromPosition(aLogicRect, aNoRotateAnchor, + *pDoc, pData->maStart.Tab()); + aNoRotateAnchor.mbResizeWithCell = pData->mbResizeWithCell; + SetNonRotatedAnchor(*pObj, aNoRotateAnchor); + } + // Mirror object at vertical axis + Point aRef1( 0, 0 ); + Point aRef2( 0, 1 ); + if (bRecording) + AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); + pObj->Mirror( aRef1, aRef2 ); + + // Adapt offsets in pNoRotatedAnchor so, that object will be moved to current position in + // save and reload. + const tools::Long nInverseShift = aOldSnapRect.Left() + aOldSnapRect.Right(); + const Point aLogicLT = pObj->GetLogicRect().TopLeft(); + const Point aMirroredLogicLT = aLogicLT + Point(nInverseShift, 0); + const Point aOffsetDiff = aMirroredLogicLT - aOldLogicRect.TopLeft(); + // new Offsets + pNoRotatedAnchor->maStartOffset += aOffsetDiff; + pNoRotatedAnchor->maEndOffset += aOffsetDiff; + } + else // page anchored + { + Point aRef1( 0, 0 ); + Point aRef2( 0, 1 ); + if (bRecording) + AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); + pObj->Mirror( aRef1, aRef2 ); + } } else { // Move instead of mirroring: // New start position is negative of old end position // -> move by sum of start and end position - tools::Rectangle aObjRect = pObj->GetLogicRect(); + tools::Rectangle aObjRect = pObj->GetSnapRect(); Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); if (bRecording) AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) ); pObj->Move( aMoveSize ); } + + // for cell anchored objects adapt rectangles in anchors + ScDrawObjData* pData = GetObjData(pObj); + if (pData) + { + pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible()); + ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true /*bCreate*/); + pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible()); + } +} + +void ScDrawLayer::MoveRTL(SdrObject* pObj) +{ + tools::Rectangle aObjRect = pObj->GetSnapRect(); + Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); + if (bRecording) + AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) ); + pObj->Move( aMoveSize ); + + // for cell anchored objects adapt rectangles in anchors + ScDrawObjData* pData = GetObjData(pObj); + if (pData) + { + pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible()); + ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true /*bCreate*/); + pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible()); + } } void ScDrawLayer::MirrorRectRTL( tools::Rectangle& rRect ) @@ -2167,7 +2271,7 @@ namespace } } -void ScDrawLayer::SetVisualCellAnchored( SdrObject &rObj, const ScDrawObjData &rAnchor ) +void ScDrawLayer::SetNonRotatedAnchor(SdrObject& rObj, const ScDrawObjData& rAnchor) { ScDrawObjData* pAnchor = GetNonRotatedObjData( &rObj, true ); pAnchor->maStart = rAnchor.maStart; @@ -2235,19 +2339,29 @@ void ScDrawLayer::SetCellAnchoredFromPosition( SdrObject &rObj, const ScDocument else aObjRect2 = rObj.GetLogicRect(); - ScDrawObjData aVisAnchor; + // Values in XML are so as if it is a LTR sheet. The object is shifted to negative page on loading + // so that the snap rectangle appears mirrored. For transformed objects the shifted logic rectangle + // is not the mirrored LTR rectangle. We calculate the mirrored LTR rectangle here. + if (rDoc.IsNegativePage(nTab)) + { + const tools::Rectangle aSnapRect(rObj.GetSnapRect()); + aObjRect2.Move(Size(-aSnapRect.Left() - aSnapRect.Right(), 0)); + MirrorRectRTL(aObjRect2); + } + + ScDrawObjData aNoRotatedAnchor; GetCellAnchorFromPosition( aObjRect2, - aVisAnchor, + aNoRotatedAnchor, rDoc, - nTab, false); + nTab); - aVisAnchor.mbResizeWithCell = bResizeWithCell; - SetVisualCellAnchored( rObj, aVisAnchor ); + aNoRotatedAnchor.mbResizeWithCell = bResizeWithCell; + SetNonRotatedAnchor( rObj, aNoRotatedAnchor); // And update maShapeRect. It is used in adjustAnchoredPosition() in ScDrawView::Notify(). if (ScDrawObjData* pAnchor = GetNonRotatedObjData(&rObj)) { - pAnchor->setShapeRect(&rDoc, rObj.GetSnapRect()); + pAnchor->setShapeRect(&rDoc, rObj.GetLogicRect()); } } diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx index 0ba9421d8265..d25e7ce7c7f4 100644 --- a/sc/source/core/data/table2.cxx +++ b/sc/source/core/data/table2.cxx @@ -3910,7 +3910,8 @@ void ScTable::GetUpperCellString(SCCOL nCol, SCROW nRow, OUString& rStr) // Calculate the size of the sheet and set the size on DrawPage -void ScTable::SetDrawPageSize(bool bResetStreamValid, bool bUpdateNoteCaptionPos) +void ScTable::SetDrawPageSize(bool bResetStreamValid, bool bUpdateNoteCaptionPos, + ScObjectHandling eObjectHandling) { ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer(); if( pDrawLayer ) @@ -3926,7 +3927,8 @@ void ScTable::SetDrawPageSize(bool bResetStreamValid, bool bUpdateNoteCaptionPos if ( IsLayoutRTL() ) // IsNegativePage x = -x; - pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( x, y ), bUpdateNoteCaptionPos ); + pDrawLayer->SetPageSize(static_cast<sal_uInt16>(nTab), Size(x, y), bUpdateNoteCaptionPos, + eObjectHandling); } // #i102616# actions that modify the draw page size count as sheet modification diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx index d69fdd784ef7..a7a61365a08c 100644 --- a/sc/source/filter/xml/xmlexprt.cxx +++ b/sc/source/filter/xml/xmlexprt.cxx @@ -3476,7 +3476,10 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) if( !(rMyCell.bHasShape && !rMyCell.aShapeList.empty() && pDoc) ) return; - // Reference point + // Reference point to turn absolute coordinates in reference point + offset. That happens in most + // cases in XMLShapeExport::ImpExportNewTrans_DecomposeAndRefPoint, which gets the absolute + // coordinates as translation from matrix in property "Transformation". For cell anchored shapes + // the reference point is left-top (in LTR mode) of that cell, which contains the shape. tools::Rectangle aCellRectFull = pDoc->GetMMRect( rMyCell.maCellAddress.Col(), rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Col(), rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Tab(), false /*bHiddenAsZero*/); @@ -3488,7 +3491,6 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) aPoint.X = aCellRectFull.Left(); aPoint.Y = aCellRectFull.Top(); - // ToDo: Adapt the solutions for RTL sheets. for (const auto& rShape : rMyCell.aShapeList) { if (rShape.xShape.is()) @@ -3548,9 +3550,17 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) const tools::Rectangle aEndCellRect = pDoc->GetMMRect( aSnapEndAddress.Col(), aSnapEndAddress.Row(), aSnapEndAddress.Col(), aSnapEndAddress.Row(), aSnapEndAddress.Tab(), false /*bHiddenAsZero*/); - aRectFull.SetLeft(aStartCellRect.Left() + aSnapStartOffset.X()); + if (bNegativePage) + { + aRectFull.SetLeft(aEndCellRect.Right() - aSnapEndOffset.X()); + aRectFull.SetRight(aStartCellRect.Right() - aSnapStartOffset.X()); + } + else + { + aRectFull.SetLeft(aStartCellRect.Left() + aSnapStartOffset.X()); + aRectFull.SetRight(aEndCellRect.Left() + aSnapEndOffset.X()); + } aRectFull.SetTop(aStartCellRect.Top() + aSnapStartOffset.Y()); - aRectFull.SetRight(aEndCellRect.Left() + aSnapEndOffset.X()); aRectFull.SetBottom(aEndCellRect.Top() + aSnapEndOffset.Y()); aRectReduced = pObjData->getShapeRect(); if(abs(aRectFull.getWidth() - aRectReduced.getWidth()) > 1 @@ -3583,25 +3593,31 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) AddAttribute(XML_NAMESPACE_TABLE, XML_END_Y, sBuffer.makeStringAndClear()); } - // The general method XMLShapeExport::ImpExportNewTrans_DecomposeAndRefPoint calculates - // offset = translate - refPoint. But in case of a horizontal mirrored, 'resize with cell' - // anchored custom shape, translate has wrong values. So we use refPoint = translate - // - startOffset which removes translate and sets startOffset directly. - // FixMe: Why is translate wrong? - if (rShape.bResizeWithCell && pObj && pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE - && static_cast<SdrObjCustomShape*>(pObj)->IsMirroredX()) + // Correct above calculated reference point for some cases: + // a) For a RTL-sheet translate from matrix is not suitable, because the shape + // from xml (which is always LTR) is not mirrored to negative page but shifted. + // b) In case of horizontal mirrored, 'resize with cell' anchored custom shape, translate + // has wrong values. FixMe: Why is translate wrong? + // c) Measure lines do not use transformation matrix but use start and end point directly. + ScDrawObjData* pNRObjData = nullptr; + if (pObj && bNegativePage + && rShape.xShape->getShapeType() == "com.sun.star.drawing.MeasureShape") { - ScDrawObjData* pNRObjData = ScDrawLayer::GetNonRotatedObjData(pObj); - if (pNRObjData) - { - aPoint.X = rShape.xShape->getPosition().X - pNRObjData->maStartOffset.X(); - aPoint.Y = rShape.xShape->getPosition().Y - pNRObjData->maStartOffset.Y(); - } + // invers of shift when import + tools::Rectangle aSnapRect = pObj->GetSnapRect(); + aPoint.X = aSnapRect.Left() + aSnapRect.Right() - aPoint.X; + } + else if (pObj && (pNRObjData = ScDrawLayer::GetNonRotatedObjData(pObj)) + && ((rShape.bResizeWithCell && pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE + && static_cast<SdrObjCustomShape*>(pObj)->IsMirroredX()) + || bNegativePage)) + { + //In these cases we set reference Point = matrix translate - startOffset. + awt::Point aMatrixTranslate = rShape.xShape->getPosition(); + aPoint.X = aMatrixTranslate.X - pNRObjData->maStartOffset.X(); + aPoint.Y = aMatrixTranslate.Y - pNRObjData->maStartOffset.Y(); } - if (bNegativePage) - aPoint.X = 2 * rShape.xShape->getPosition().X + rShape.xShape->getSize().Width - - aPoint.X; ExportShape(rShape.xShape, &aPoint); // Restore object geometry @@ -3625,11 +3641,23 @@ void ScXMLExport::WriteTableShapes() { if (pDoc->IsNegativePage(static_cast<SCTAB>(nCurrentTable))) { - awt::Point aPoint(rxShape->getPosition()); - awt::Size aSize(rxShape->getSize()); - aPoint.X += aPoint.X + aSize.Width; - aPoint.Y = 0; - ExportShape(rxShape, &aPoint); + // RTL-mirroring refers to snap rectangle, not to logic rectangle, therefore cannot use + // getPosition() and getSize(), but need property "FrameRect" from rxShape or + // GetSnapRect() from associated SdrObject. + uno::Reference<beans::XPropertySet> xShapeProp(rxShape, uno::UNO_QUERY); + awt::Rectangle aFrameRect; + if (xShapeProp.is() && (xShapeProp->getPropertyValue("FrameRect") >>= aFrameRect)) + { + // file format uses shape in LTR mode. newLeft = - oldRight = - (oldLeft + width). + // newTranslate = oldTranslate - refPoint, oldTranslate from transformation matrix, + // calculated in XMLShapeExport::exportShape common for all modules. + // oldTranslate.X = oldLeft ==> refPoint.X = 2 * oldLeft + width + awt::Point aRefPoint; + aRefPoint.X = 2 * aFrameRect.X + aFrameRect.Width - 1; + aRefPoint.Y = 0; + ExportShape(rxShape, &aRefPoint); + } + // else should not happen } else ExportShape(rxShape, nullptr); diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx index 42ac96e8eea3..2949e92390f1 100644 --- a/sc/source/ui/docshell/docfunc.cxx +++ b/sc/source/ui/docshell/docfunc.cxx @@ -3465,7 +3465,7 @@ bool ScDocFunc::SetLayoutRTL( SCTAB nTab, bool bRTL ) ScDocShellModificator aModificator( rDocShell ); - rDoc.SetLayoutRTL( nTab, bRTL ); + rDoc.SetLayoutRTL( nTab, bRTL, ScObjectHandling::MirrorRTLMode); if (bUndo) { diff --git a/sc/source/ui/undo/undotab.cxx b/sc/source/ui/undo/undotab.cxx index 4118b1bcbd5d..85c2708367e4 100644 --- a/sc/source/ui/undo/undotab.cxx +++ b/sc/source/ui/undo/undotab.cxx @@ -1509,7 +1509,7 @@ void ScUndoLayoutRTL::DoChange( bool bNew ) pDocShell->SetInUndo( true ); ScDocument& rDoc = pDocShell->GetDocument(); - rDoc.SetLayoutRTL( nTab, bNew ); + rDoc.SetLayoutRTL(nTab, bNew, ScObjectHandling::MirrorRTLMode); ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); if (pViewShell) |