From d0921aa753c43600272865602df3c7c2a8f13196 Mon Sep 17 00:00:00 2001 From: Regina Henschel Date: Tue, 1 Dec 2020 00:05:43 +0100 Subject: tdf#137576 Improve cell anchored measure line in Calc Measure lines do not always have a logic rectangle. It might be empty or the 1cm x 1cm default square. But Calc needs it to calculate NonRotatedAnchor. The latter is needed for cell anchored shapes when saving in ODF. Always generating a logic rectangle in class SdrMeasureObj is difficult (I got crashes in Draw) and not necessary. Calc now forces the calculation of the logic rectangle where it is needed by Calc. Change-Id: I8689bc95985db1619eb5e72df99901bd52086cb2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/106990 Tested-by: Jenkins Reviewed-by: Regina Henschel --- sc/qa/unit/data/ods/ManualColWidthRowHeight.ods | Bin 0 -> 8962 bytes sc/qa/unit/scshapetest.cxx | 121 ++++++++++++++++++++++++ sc/source/core/data/drwlayer.cxx | 22 +++-- 3 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 sc/qa/unit/data/ods/ManualColWidthRowHeight.ods diff --git a/sc/qa/unit/data/ods/ManualColWidthRowHeight.ods b/sc/qa/unit/data/ods/ManualColWidthRowHeight.ods new file mode 100644 index 000000000000..1cc738e05244 Binary files /dev/null and b/sc/qa/unit/data/ods/ManualColWidthRowHeight.ods differ diff --git a/sc/qa/unit/scshapetest.cxx b/sc/qa/unit/scshapetest.cxx index 236cbc0879b4..c13ec7189622 100644 --- a/sc/qa/unit/scshapetest.cxx +++ b/sc/qa/unit/scshapetest.cxx @@ -13,10 +13,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -35,6 +37,8 @@ public: ScShapeTest(); void saveAndReload(css::uno::Reference& xComponent, const OUString& rFilter); + void testTdf137576_LogicRectInDefaultMeasureline(); + void testTdf137576_LogicRectInNewMeasureline(); void testMeasurelineHideColSave(); void testHideColsShow(); void testTdf138138_MoveCellWithRotatedShape(); @@ -46,6 +50,8 @@ public: void testCustomShapeCellAnchoredRotatedShape(); CPPUNIT_TEST_SUITE(ScShapeTest); + CPPUNIT_TEST(testTdf137576_LogicRectInDefaultMeasureline); + CPPUNIT_TEST(testTdf137576_LogicRectInNewMeasureline); CPPUNIT_TEST(testMeasurelineHideColSave); CPPUNIT_TEST(testHideColsShow); CPPUNIT_TEST(testTdf138138_MoveCellWithRotatedShape); @@ -121,6 +127,121 @@ static void lcl_AssertPointEqualWithTolerance(const OString& sInfo, const Point CPPUNIT_ASSERT_MESSAGE(sMsg.getStr(), std::abs(rExpected.Y() - rActual.Y()) <= nTolerance); } +void ScShapeTest::testTdf137576_LogicRectInDefaultMeasureline() +{ + // Error was, that the empty logical rectangle of a default measure line (Ctrl+Click) + // resulted in zeros in NonRotatedAnchor and a wrong position when reloading. + + // Load an empty document. + OUString aFileURL; + createFileURL("ManualColWidthRowHeight.ods", aFileURL); + uno::Reference xComponent = loadFromDesktop(aFileURL); + CPPUNIT_ASSERT(xComponent.is()); + + // Get ScDocShell + SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent); + CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell); + ScDocShell* pDocSh = dynamic_cast(pFoundShell); + CPPUNIT_ASSERT_MESSAGE("No ScDocShell", pDocSh); + + // Create default measureline by SfxRequest that corresponds to Ctrl+Click + ScTabViewShell* pTabViewShell = pDocSh->GetBestViewShell(false); + CPPUNIT_ASSERT_MESSAGE("No ScTabViewShell", pTabViewShell); + SfxRequest aReq(pTabViewShell->GetViewFrame(), SID_DRAW_MEASURELINE); + aReq.SetModifier(KEY_MOD1); // Ctrl + pTabViewShell->ExecDraw(aReq); + + // Get document and newly created measure line. + ScDocument& rDoc = pDocSh->GetDocument(); + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer); + const SdrPage* pPage = pDrawLayer->GetPage(0); + CPPUNIT_ASSERT_MESSAGE("No draw page", pPage); + SdrObject* pObj = pPage->GetObj(0); + CPPUNIT_ASSERT_MESSAGE("No object found", pObj); + + // Anchor "to Cell (resize with cell)" + ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/); + // Deselect shape and switch to object selection type "Cell". + pTabViewShell->SetDrawShell(false); + + // Hide column A. + uno::Sequence aPropertyValues = { + comphelper::makePropertyValue("ToPoint", OUString("$A$1")), + }; + dispatchCommand(xComponent, ".uno:GoToCell", aPropertyValues); + dispatchCommand(xComponent, ".uno:HideColumn", {}); + + // Get current position. I will not use absolute values for comparison, because document is loaded + // in full screen mode of unknown size and default object is placed in center of window. + Point aOldPos = pObj->GetRelativePos(); + + // Save and reload, get ScDocShell + saveAndReload(xComponent, "calc8"); + CPPUNIT_ASSERT(xComponent); + pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent); + CPPUNIT_ASSERT_MESSAGE("Reload: Failed to access document shell", pFoundShell); + pDocSh = dynamic_cast(pFoundShell); + CPPUNIT_ASSERT(pDocSh); + + // Get document and object + ScDocument& rDoc2 = pDocSh->GetDocument(); + pDrawLayer = rDoc2.GetDrawLayer(); + CPPUNIT_ASSERT_MESSAGE("Reload: No ScDrawLayer", pDrawLayer); + pPage = pDrawLayer->GetPage(0); + CPPUNIT_ASSERT_MESSAGE("Reload: No draw page", pPage); + pObj = pPage->GetObj(0); + CPPUNIT_ASSERT_MESSAGE("Measure line lost", pObj); + + // Assert object position is unchanged, besides Twips<->Hmm inaccuracy. + Point aNewPos = pObj->GetRelativePos(); + lcl_AssertPointEqualWithTolerance("after reload", aOldPos, aNewPos, 1); + + pDocSh->DoClose(); +} + +void ScShapeTest::testTdf137576_LogicRectInNewMeasureline() +{ + // Error was, that a new measure line had no logical rectangle. This resulted in zeros in + // NonRotatedAnchor. As a result the position was wrong when reloading. + + // Load an empty document + OUString aFileURL; + createFileURL("ManualColWidthRowHeight.ods", aFileURL); + uno::Reference xComponent = loadFromDesktop(aFileURL); + CPPUNIT_ASSERT(xComponent.is()); + + // Get ScDocShell + SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(xComponent); + CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell); + ScDocShell* pDocSh = dynamic_cast(pFoundShell); + CPPUNIT_ASSERT(pDocSh); + + // Get SdrPage + ScDocument& rDoc = pDocSh->GetDocument(); + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer); + SdrPage* pPage = pDrawLayer->GetPage(0); + CPPUNIT_ASSERT_MESSAGE("No draw page", pPage); + + // Create a new measure line and insert it + Point aStartPoint(5000, 5500); + Point aEndPoint(13000, 8000); + SdrMeasureObj* pObj = new SdrMeasureObj(*pDrawLayer, aStartPoint, aEndPoint); + CPPUNIT_ASSERT_MESSAGE("Could not create measure line", pObj); + pPage->InsertObject(pObj); + + // Anchor "to cell (resize with cell)" and examine NonRotatedAnchor + ScDrawLayer::SetCellAnchoredFromPosition(*pObj, rDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/); + ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData(pObj); + CPPUNIT_ASSERT_MESSAGE("Failed to get NonRotatedAnchor", pNData); + // Without the fix all four values would be zero. + CPPUNIT_ASSERT(pNData->maStart.Col() == 1 && pNData->maStart.Row() == 2); + CPPUNIT_ASSERT(pNData->maEnd.Col() == 7 && pNData->maEnd.Row() == 2); + + pDocSh->DoClose(); +} + void ScShapeTest::testMeasurelineHideColSave() { // The document contains a SdrMeasureObj anchored "To Cell (resive with cell)" with start in cell diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx index 16fbe9c7f51c..eb329379398a 100644 --- a/sc/source/core/data/drwlayer.cxx +++ b/sc/source/core/data/drwlayer.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -932,14 +933,13 @@ void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* pObj, ScDrawObjData& rDat else if (pObj->GetObjIdentifier() == OBJ_MEASURE) { // Measure lines might have got wrong start and end anchor from XML import. Recreate - // them from start and end point. - const Point aPoint0(pObj->GetPoint(0)); - const Point aPoint1(pObj->GetPoint(1)); - const Point aPointLT(std::min(aPoint0.X(), aPoint1.X()), - std::min(aPoint0.Y(), aPoint1.Y())); - const Point aPointRB(std::max(aPoint0.X(), aPoint1.X()), - std::max(aPoint0.Y(), aPoint1.Y())); - const tools::Rectangle aObjRect(aPointLT, aPointRB); + // anchor from start and end point. + SdrMeasureObj* pMeasureObj = dynamic_cast(pObj); + // tdf#137576. The logic rectangle has likely no current values here, but only the + // 1cm x 1cm default size. The call of TakeUnrotatedSnapRect is currently (LO 7.2) + // the only way to force a recalc of the logic rectangle. + tools::Rectangle aObjRect; + pMeasureObj->TakeUnrotatedSnapRect(aObjRect); GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, rData.maStart.Tab(), false /*bHiddenAsZero*/); } @@ -2227,6 +2227,12 @@ void ScDrawLayer::SetCellAnchoredFromPosition( SdrObject &rObj, const ScDocument aObjRect2 = rObj.GetLogicRect(); rObj.NbcMirror(aLeft, aRight); } + else if (rObj.GetObjIdentifier() == OBJ_MEASURE) + { + // tdf#137576. A SdrMeasureObj might have a wrong logic rect here. TakeUnrotatedSnapRect + // calculates the current unrotated snap rectangle, sets logic rectangle and returns it. + static_cast(&rObj)->TakeUnrotatedSnapRect(aObjRect2); + } else aObjRect2 = rObj.GetLogicRect(); -- cgit