From 499ca3e23eaa6ee7c1940cf31f08d5719432c6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Vajngerl?= Date: Sat, 20 Jun 2026 03:02:24 +0000 Subject: [PATCH] sc: carry a table into the clipboard only from a copied range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CopyToClip() copies named database ranges into the clipboard document through CopyDBsToClip(), which decided whether to carry a table by testing it against the bounding box of the copied area. For a non-contiguous selection the bounding box also covers the gaps between the selected ranges, so a table sitting in such a gap was carried even though none of its cells were copied. Test the table against the actually copied ranges instead of their bounding box, so a table that lies entirely in a gap is left out. Signed-off-by: Tomaž Vajngerl Change-Id: I1c8663e5190247dae0edf3878efe37186a7f6a4d Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5095 Reviewed-by: Balázs Varga Tested-by: Tomaž Vajngerl (cherry picked from commit 37543af628671f723c9a661e2add91d6bc641591) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207944 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- sc/inc/document.hxx | 2 +- sc/qa/unit/ucalc_copypaste.cxx | 46 ++++++++++++++++++++++++++++---- sc/source/core/data/document.cxx | 9 ++++--- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index c06a71a3e865..bd8c07d2f706 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -2928,7 +2928,7 @@ private: const ScRange& r, SCCOL nDx, SCROW nDy, SCTAB nDz ); void CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks); - void CopyDBsToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks); + void CopyDBsToClip(ScDocument* pClipDoc, const ScRangeList& rClipRanges, const ScMarkData* pMarks); void CopyDBsFromClip(const ScRange& rDestRange, const ScRange& rClipRange, const ScDocument* pClipDoc); bool HasPartOfMerged( const ScRange& rRange ); diff --git a/sc/qa/unit/ucalc_copypaste.cxx b/sc/qa/unit/ucalc_copypaste.cxx index 2bdde152b4a3..2d40b97a781c 100644 --- a/sc/qa/unit/ucalc_copypaste.cxx +++ b/sc/qa/unit/ucalc_copypaste.cxx @@ -10129,8 +10129,8 @@ CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipCrossDocSameNameNoBind) auto pTable = std::make_unique(u"MyTable"_ustr, 0, 0, 0, 2, 3, true, true); pTable->SetTableColumnNames(std::vector{ u"H1"_ustr, u"H2"_ustr, u"H3"_ustr }); CPPUNIT_ASSERT(m_pDoc->GetDBCollection()->getNamedDBs().insert(std::move(pTable))); - m_pDoc->SetValue(0, 1, 0, 1.0); // A2 - m_pDoc->SetValue(0, 2, 0, 4.0); // A3 + m_pDoc->SetValue(0, 1, 0, 1.0); // A2 + m_pDoc->SetValue(0, 2, 0, 4.0); // A3 m_pDoc->SetValue(0, 3, 0, 16.0); // A4 m_pDoc->SetString(2, 1, 0, u"=SUM(MyTable[H1])"_ustr); // C2 m_pDoc->CalcAll(); @@ -10148,7 +10148,8 @@ CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipCrossDocSameNameNoBind) // Destination document with its OWN unrelated MyTable at F11:H14 // carrying distinct H1 data. ScDocShellRef xDestDocSh = new ScDocShell; - xDestDocSh->DoLoad(new SfxMedium(u"file:///crossdocnameclash.fake"_ustr, StreamMode::STD_READWRITE)); + xDestDocSh->DoLoad( + new SfxMedium(u"file:///crossdocnameclash.fake"_ustr, StreamMode::STD_READWRITE)); ScDocument& rDestDoc = xDestDocSh->GetDocument(); rDestDoc.InsertTab(0, u"Dest"_ustr); auto pDestTable = std::make_unique(u"MyTable"_ustr, 0, 5, 10, 7, 13, true, true); @@ -10166,8 +10167,7 @@ CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipCrossDocSameNameNoBind) rDestDoc.CalcAll(); // The pasted formula resolves to FormulaError::NoRef. - CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), - int(rDestDoc.GetErrCode(ScAddress(2, 1, 0)))); + CPPUNIT_ASSERT_EQUAL(int(FormulaError::NoRef), int(rDestDoc.GetErrCode(ScAddress(2, 1, 0)))); // The source's H1 data landed in the destination at A2:A4. CPPUNIT_ASSERT_EQUAL(1.0, rDestDoc.GetValue(0, 1, 0)); @@ -10190,6 +10190,42 @@ CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipCrossDocSameNameNoBind) m_pDoc->DeleteTab(0); } +CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyToClipMultiRangeGapTable) +{ + // A non-contiguous copy must carry only the tables that lie inside a copied + // range, not one sitting in the gap between two ranges. The gap table is + // inside the bounding box of the selection but none of its cells are copied. + m_pDoc->InsertTab(0, u"Test"_ustr); + + // Column-direction multi-selection A1:B3 and E1:F3; the gap is columns C, D. + + // InRangeTable at A1:B3 lies inside the first range. The clip carries it. + auto pInRange = std::make_unique(u"InRangeTable"_ustr, 0, 0, 0, 1, 2, true, true); + CPPUNIT_ASSERT(m_pDoc->GetDBCollection()->getNamedDBs().insert(std::move(pInRange))); + + // GapTable at C1:D3 lies in the gap. The clip leaves it out. + auto pGap = std::make_unique(u"GapTable"_ustr, 0, 2, 0, 3, 2, true, true); + CPPUNIT_ASSERT(m_pDoc->GetDBCollection()->getNamedDBs().insert(std::move(pGap))); + + ScMarkData aMark(m_pDoc->GetSheetLimits()); + aMark.SelectOneTable(0); + ScClipParam aClipParam; + aClipParam.meDirection = ScClipParam::Column; + aClipParam.maRanges.push_back(ScRange(0, 0, 0, 1, 2, 0)); // A1:B3 + aClipParam.maRanges.push_back(ScRange(4, 0, 0, 5, 2, 0)); // E1:F3 + + ScDocument aClipDoc(SCDOCMODE_CLIP); + m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark, false, false); + + ScDBCollection::NamedDBs& rClipDBs = aClipDoc.GetDBCollection()->getNamedDBs(); + CPPUNIT_ASSERT_MESSAGE("a table inside a copied range is carried", + rClipDBs.findByUpperName(u"INRANGETABLE"_ustr)); + CPPUNIT_ASSERT_MESSAGE("a table in the gap between copied ranges must not be carried", + !rClipDBs.findByUpperName(u"GAPTABLE"_ustr)); + + m_pDoc->DeleteTab(0); +} + CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteFormulasExternalDoc) { SfxMedium* pMedium = new SfxMedium(u"file:///source.fake"_ustr, StreamMode::STD_READWRITE); diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index e2be22b7065e..2a4c9f3e2b65 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -2320,7 +2320,7 @@ void ScDocument::CopyToClip(const ScClipParam& rClipParam, sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags); CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks); - CopyDBsToClip(pClipDoc, aClipRange, pMarks); + CopyDBsToClip(pClipDoc, rClipParam.maRanges, pMarks); // 1. Copy selected cells for (SCTAB i = 0; i < nEndTab; ++i) @@ -2588,7 +2588,7 @@ void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClip // copied formulas have no ScDBData to resolve against on the clip side, // so any later stringification of those formulas writes #REF! in place // of the column name. -void ScDocument::CopyDBsToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks) +void ScDocument::CopyDBsToClip(ScDocument* pClipDoc, const ScRangeList& rClipRanges, const ScMarkData* pMarks) { if (!pDBCollection || pDBCollection->getNamedDBs().empty()) return; @@ -2604,7 +2604,10 @@ void ScDocument::CopyDBsToClip(ScDocument* pClipDoc, const ScRange& rClipRange, rxSrc->GetArea(aArea); if (pMarks && !pMarks->GetTableSelect(aArea.aStart.Tab())) continue; - if (!aArea.Intersects(rClipRange)) + // Carry only a table that overlaps a copied range. A table sitting in + // the gap of a non-contiguous selection falls inside the bounding box + // but in none of the copied ranges, and its cells are not copied. + if (!rClipRanges.Intersects(aArea)) continue; auto pClone = std::make_unique(*rxSrc); rDest.insert(std::move(pClone));