summaryrefslogtreecommitdiff
path: root/sc
diff options
context:
space:
mode:
authorKohei Yoshida <kohei@libreoffice.org>2023-02-21 22:16:30 -0500
committerKohei Yoshida <kohei@libreoffice.org>2023-02-28 21:13:25 +0000
commit33b6c065a1629afd36c9ae0fe5daa18b972620e5 (patch)
tree6a503ea0f138863f6484bb275e24030a960d269e /sc
parent567bd3c1dc9a568ffa70cf488600a1e0bbd1b411 (diff)
tdf#153669: Track formulas that stopped listening ...
... then have them start listening again after the copy from clipboard is complete. Note that in case the pasted cells are formula cells, those will be handled together as well. Change-Id: Ia4be814b888734267a39f7c89435011968570b7f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147940 Tested-by: Jenkins Reviewed-by: Kohei Yoshida <kohei@libreoffice.org> (cherry picked from commit e83c243018c1c7f6662f9a8ecdc731c5c071ea31) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147912
Diffstat (limited to 'sc')
-rw-r--r--sc/inc/clipcontext.hxx15
-rw-r--r--sc/inc/column.hxx19
-rw-r--r--sc/inc/document.hxx3
-rw-r--r--sc/source/core/data/clipcontext.cxx47
-rw-r--r--sc/source/core/data/column3.cxx55
-rw-r--r--sc/source/core/data/column4.cxx18
-rw-r--r--sc/source/core/data/document.cxx31
7 files changed, 157 insertions, 31 deletions
diff --git a/sc/inc/clipcontext.hxx b/sc/inc/clipcontext.hxx
index b09e1be78761..b3ce874a6a7f 100644
--- a/sc/inc/clipcontext.hxx
+++ b/sc/inc/clipcontext.hxx
@@ -12,6 +12,7 @@
#include "address.hxx"
#include "cellvalue.hxx"
#include "celltextattr.hxx"
+#include "columnspanset.hxx"
#include "Sparkline.hxx"
#include <memory>
@@ -45,6 +46,9 @@ public:
class SC_DLLPUBLIC CopyFromClipContext final : public ClipContextBase
{
+ /** Tracks modified formula group spans. */
+ sc::ColumnSpanSet maListeningFormulaSpans;
+
SCCOL mnDestCol1;
SCCOL mnDestCol2;
SCROW mnDestRow1;
@@ -102,6 +106,17 @@ public:
InsertDeleteFlags getDeleteFlag() const;
/**
+ * Record a range of formula cells that need to start listening after the
+ * copy-from-clip is complete.
+ */
+ void setListeningFormulaSpans( SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
+
+ /**
+ * Have the formula cells in the recorded ranges start listening.
+ */
+ void startListeningFormulas();
+
+ /**
* Set the column size of a "single cell" row, which is used when copying
* a single row of cells in a clip doc and pasting it into multiple
* rows by replicating it.
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 69f0d12d8273..949ca30dd137 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -22,6 +22,7 @@
#include "global.hxx"
#include "address.hxx"
#include "cellvalue.hxx"
+#include "columnspanset.hxx"
#include "rangelst.hxx"
#include "types.hxx"
#include "mtvelements.hxx"
@@ -48,8 +49,6 @@ class CopyFromClipContext;
class CopyToClipContext;
class CopyToDocContext;
class MixDocContext;
-class ColumnSpanSet;
-class SingleColumnSpanSet;
struct RefUpdateContext;
struct RefUpdateInsertTabContext;
struct RefUpdateDeleteTabContext;
@@ -837,9 +836,19 @@ private:
void CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const;
- void DeleteCells(
- sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag,
- sc::SingleColumnSpanSet& rDeleted );
+ struct DeleteCellsResult
+ {
+ /** cell ranges that have been deleted. */
+ sc::SingleColumnSpanSet aDeletedRows;
+ /** formula cell range that has stopped listening. */
+ std::vector<std::pair<SCROW, SCROW>> aFormulaRanges;
+
+ DeleteCellsResult( const ScDocument& rDoc );
+ DeleteCellsResult( const DeleteCellsResult& ) = delete;
+ };
+
+ std::unique_ptr<DeleteCellsResult> DeleteCells(
+ sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag );
/**
* Get all non-grouped formula cells and formula cell groups in the whole
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 20e42a999ac8..4873307ea01d 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1672,6 +1672,9 @@ public:
SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark, SCCOL nDx,
SCROW& rClipStartRow, SCROW nClipEndRow);
+ void StartListeningFromClip(
+ sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+ SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
void StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
SCCOL nCol2, SCROW nRow2,
const ScMarkData& rMark, InsertDeleteFlags nInsFlag );
diff --git a/sc/source/core/data/clipcontext.cxx b/sc/source/core/data/clipcontext.cxx
index d291c7c91f49..ce6974d42334 100644
--- a/sc/source/core/data/clipcontext.cxx
+++ b/sc/source/core/data/clipcontext.cxx
@@ -21,6 +21,7 @@
#include <svl/numformat.hxx>
#include <formula/errorcodes.hxx>
#include <refdata.hxx>
+#include <listenercontext.hxx>
namespace sc {
@@ -113,6 +114,52 @@ InsertDeleteFlags CopyFromClipContext::getDeleteFlag() const
return mnDeleteFlag;
}
+void CopyFromClipContext::setListeningFormulaSpans(
+ SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ maListeningFormulaSpans.set(mrDestDoc, nTab, nCol, nRow1, nRow2, true);
+}
+
+namespace {
+
+class StartListeningAction : public sc::ColumnSpanSet::Action
+{
+ ScDocument& mrDestDoc;
+ sc::StartListeningContext& mrStartCxt;
+ sc::EndListeningContext& mrEndCxt;
+
+public:
+ StartListeningAction( ScDocument& rDestDoc, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
+ mrDestDoc(rDestDoc), mrStartCxt(rStartCxt), mrEndCxt(rEndCxt)
+ {
+ }
+
+ virtual void execute( const ScAddress& rPos, SCROW nLength, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ SCROW nRow1 = rPos.Row();
+ SCROW nRow2 = nRow1 + nLength - 1;
+
+ mrDestDoc.StartListeningFromClip(
+ mrStartCxt, mrEndCxt, rPos.Tab(), rPos.Col(), nRow1, rPos.Col(), nRow2);
+ }
+};
+
+}
+
+void CopyFromClipContext::startListeningFormulas()
+{
+ auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(mrDestDoc);
+ sc::StartListeningContext aStartCxt(mrDestDoc, pSet);
+ sc::EndListeningContext aEndCxt(mrDestDoc, pSet, nullptr);
+
+ StartListeningAction aAction(mrDestDoc, aStartCxt, aEndCxt);
+ maListeningFormulaSpans.executeAction(mrDestDoc, aAction);
+}
+
void CopyFromClipContext::setSingleCellColumnSize( size_t nSize )
{
maSingleCells.resize(nSize);
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index d92318134098..900ed04fa795 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -990,6 +990,33 @@ public:
(nType == SvNumFormatType::DATETIME);
}
+ /**
+ * Query the formula ranges that may have stopped listening, accounting for
+ * the formula groups.
+ */
+ std::vector<std::pair<SCROW, SCROW>> getFormulaRanges()
+ {
+ std::vector<std::pair<SCROW, SCROW>> aRet;
+
+ for (const ScFormulaCell* pFC : maFormulaCells)
+ {
+ SCROW nTopRow = pFC->aPos.Row();
+ SCROW nBottomRow = pFC->aPos.Row();
+
+ auto xGroup = pFC->GetCellGroup();
+ if (xGroup)
+ {
+ pFC = xGroup->mpTopCell;
+ nTopRow = pFC->aPos.Row();
+ nBottomRow = nTopRow + xGroup->mnLength - 1;
+ }
+
+ aRet.emplace_back(nTopRow, nBottomRow);
+ }
+
+ return aRet;
+ }
+
void endFormulas()
{
mrDoc.EndListeningFormulaCells(maFormulaCells);
@@ -1037,14 +1064,21 @@ public:
}
-void ScColumn::DeleteCells(
- sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag,
- sc::SingleColumnSpanSet& rDeleted )
+ScColumn::DeleteCellsResult::DeleteCellsResult( const ScDocument& rDoc ) :
+ aDeletedRows( rDoc.GetSheetLimits() )
+{
+}
+
+std::unique_ptr<ScColumn::DeleteCellsResult> ScColumn::DeleteCells(
+ sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag )
{
+ std::unique_ptr<DeleteCellsResult> xResult = std::make_unique<DeleteCellsResult>(GetDoc());
+
// Determine which cells to delete based on the deletion flags.
DeleteAreaHandler aFunc(GetDoc(), nDelFlag, *this);
sc::CellStoreType::iterator itPos = maCells.position(rBlockPos.miCellPos, nRow1).first;
sc::ProcessBlock(itPos, maCells, aFunc, nRow1, nRow2);
+ xResult->aFormulaRanges = aFunc.getFormulaRanges();
aFunc.endFormulas(); // Have the formula cells stop listening.
// Get the deletion spans.
@@ -1056,7 +1090,9 @@ void ScColumn::DeleteCells(
std::for_each(aSpans.rbegin(), aSpans.rend(), EmptyCells(rBlockPos, *this));
CellStorageModified();
- aFunc.getSpans().swap(rDeleted);
+ aFunc.getSpans().swap(xResult->aDeletedRows);
+
+ return xResult;
}
void ScColumn::DeleteArea(
@@ -1069,18 +1105,17 @@ void ScColumn::DeleteArea(
nContMask |= InsertDeleteFlags::NOCAPTIONS;
InsertDeleteFlags nContFlag = nDelFlag & nContMask;
- sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits());
-
sc::ColumnBlockPosition aBlockPos;
InitBlockPosition(aBlockPos);
+ std::unique_ptr<DeleteCellsResult> xResult;
if (!IsEmptyData() && nContFlag != InsertDeleteFlags::NONE)
{
- DeleteCells(aBlockPos, nStartRow, nEndRow, nDelFlag, aDeletedRows);
+ xResult = DeleteCells(aBlockPos, nStartRow, nEndRow, nDelFlag);
if (pBroadcastSpans)
{
sc::SingleColumnSpanSet::SpansType aSpans;
- aDeletedRows.getSpans(aSpans);
+ xResult->aDeletedRows.getSpans(aSpans);
for (const auto& rSpan : aSpans)
pBroadcastSpans->set(GetDoc(), nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, true);
}
@@ -1109,12 +1144,12 @@ void ScColumn::DeleteArea(
else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
pAttrArray->DeleteHardAttr( nStartRow, nEndRow );
- if (bBroadcast)
+ if (xResult && bBroadcast)
{
// Broadcast on only cells that were deleted; no point broadcasting on
// cells that were already empty before the deletion.
std::vector<SCROW> aRows;
- aDeletedRows.getRows(aRows);
+ xResult->aDeletedRows.getRows(aRows);
BroadcastCells(aRows, SfxHintId::ScDataChanged);
}
}
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 0de5deff335c..49420642bf5f 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -111,9 +111,12 @@ void ScColumn::DeleteBeforeCopyFromClip(
if (nDelFlag & InsertDeleteFlags::CONTENTS)
{
- sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits());
- DeleteCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2, nDelFlag, aDeletedRows);
- rBroadcastSpans.set(GetDoc(), nTab, nCol, aDeletedRows, true);
+ auto xResult = DeleteCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2, nDelFlag);
+ rBroadcastSpans.set(GetDoc(), nTab, nCol, xResult->aDeletedRows, true);
+
+ for (const auto& rRange : xResult->aFormulaRanges)
+ rCxt.setListeningFormulaSpans(
+ nTab, nCol, rRange.first, nCol, rRange.second);
}
if (nDelFlag & InsertDeleteFlags::NOTE)
@@ -202,9 +205,12 @@ void ScColumn::DeleteBeforeCopyFromClip(
if (nDelFlag & InsertDeleteFlags::CONTENTS)
{
- sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits());
- DeleteCells(*pBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows);
- rBroadcastSpans.set(GetDoc(), nTab, nCol, aDeletedRows, true);
+ auto xResult = DeleteCells(*pBlockPos, nRow1, nRow2, nDelFlag);
+ rBroadcastSpans.set(GetDoc(), nTab, nCol, xResult->aDeletedRows, true);
+
+ for (const auto& rRange : xResult->aFormulaRanges)
+ rCxt.setListeningFormulaSpans(
+ nTab, nCol, rRange.first, nCol, rRange.second);
}
if (nDelFlag & InsertDeleteFlags::NOTE)
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index a907c9184754..e204acd3cd24 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2621,6 +2621,17 @@ bool ScDocument::IsClipboardSource() const
mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool();
}
+void ScDocument::StartListeningFromClip(
+ sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+ SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->StartListeningFormulaCells(rStartCxt, rEndCxt, nCol1, nRow1, nCol2, nRow2);
+}
+
void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
SCCOL nCol2, SCROW nRow2,
const ScMarkData& rMark, InsertDeleteFlags nInsFlag )
@@ -2633,14 +2644,8 @@ void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
sc::StartListeningContext aStartCxt(*this, pSet);
sc::EndListeningContext aEndCxt(*this, pSet, nullptr);
- SCTAB nMax = static_cast<SCTAB>(maTabs.size());
- for (const auto& rTab : rMark)
- {
- if (rTab >= nMax)
- break;
- if (maTabs[rTab])
- maTabs[rTab]->StartListeningFormulaCells(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
- }
+ for (SCTAB nTab : rMark)
+ StartListeningFromClip(aStartCxt, aEndCxt, nTab, nCol1, nRow1, nCol2, nRow2);
}
void ScDocument::SetDirtyFromClip(
@@ -2946,7 +2951,7 @@ void ScDocument::CopyFromClip(
SCROW nRow2 = rRange.aEnd.Row();
aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
- DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans);
+ DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans); // <- this removes existing formula listeners
if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2))
continue;
@@ -3042,8 +3047,14 @@ void ScDocument::CopyFromClip(
bInsertingFromOtherDoc = false;
+ if (nInsFlag & InsertDeleteFlags::CONTENTS)
+ {
+ for (SCTAB nTab : rMark)
+ aCxt.setListeningFormulaSpans(nTab, nAllCol1, nAllRow1, nAllCol2, nAllRow2);
+ }
+
// Create Listener after everything has been inserted
- StartListeningFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag );
+ aCxt.startListeningFormulas();
{
ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);