sc: carry a table into the clipboard only from a copied range

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 <tomaz.vajngerl@collabora.co.uk>
Change-Id: I1c8663e5190247dae0edf3878efe37186a7f6a4d
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5095
Reviewed-by: Balázs Varga <balazs.varga@collabora.com>
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
(cherry picked from commit 37543af628671f723c9a661e2add91d6bc641591)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207944
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Tomaž Vajngerl
2026-07-10 23:04:00 +02:00
committed by Xisco Fauli
parent 2d2a39e989
commit 499ca3e23e
3 changed files with 48 additions and 9 deletions
+1 -1
View File
@@ -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 );
+41 -5
View File
@@ -10129,8 +10129,8 @@ CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyFromClipCrossDocSameNameNoBind)
auto pTable = std::make_unique<ScDBData>(u"MyTable"_ustr, 0, 0, 0, 2, 3, true, true);
pTable->SetTableColumnNames(std::vector<OUString>{ 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<ScDBData>(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<ScDBData>(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<ScDBData>(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);
+6 -3
View File
@@ -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<ScDBData>(*rxSrc);
rDest.insert(std::move(pClone));