summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKohei Yoshida <kohei@libreoffice.org>2023-02-21 22:16:30 -0500
committerKohei Yoshida <kohei@libreoffice.org>2023-03-02 23:35:56 +0000
commit687b950702c49c90cff9a43655ea97a0343799a0 (patch)
treede60765d191b62c0e92374534d3f836b8c406cbc
parente34074feeb1b918ab9f26a18c5fdb0b1f2e35f94 (diff)
Add a means to query the internal broadcaster state ...
... and use it in one unit test case. Also, remove the code inside DEBUG_AREA_BROADCASTER macro since it no longer builds & has been superceded by this new state query method. Change-Id: I38691a76df5c63034ff488522936dd566bf8b4e3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148079 Tested-by: Kohei Yoshida <kohei@libreoffice.org> Reviewed-by: Kohei Yoshida <kohei@libreoffice.org>
-rw-r--r--sc/Library_sc.mk1
-rw-r--r--sc/inc/broadcast.hxx86
-rw-r--r--sc/inc/calcmacros.hxx4
-rw-r--r--sc/inc/column.hxx3
-rw-r--r--sc/inc/document.hxx7
-rw-r--r--sc/inc/grouparealistener.hxx3
-rw-r--r--sc/inc/table.hxx3
-rw-r--r--sc/qa/unit/ucalc_formula.cxx85
-rw-r--r--sc/source/core/data/bcaslot.cxx59
-rw-r--r--sc/source/core/data/broadcast.cxx164
-rw-r--r--sc/source/core/data/column4.cxx31
-rw-r--r--sc/source/core/data/document.cxx8
-rw-r--r--sc/source/core/data/document10.cxx15
-rw-r--r--sc/source/core/data/table7.cxx6
-rw-r--r--sc/source/core/inc/bcaslot.hxx15
15 files changed, 400 insertions, 90 deletions
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 4d3c7398b43d..49c36cf27d3c 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -104,6 +104,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
sc/source/core/data/attrib \
sc/source/core/data/autonamecache \
sc/source/core/data/bcaslot \
+ sc/source/core/data/broadcast \
sc/source/core/data/bigrange \
sc/source/core/data/celltextattr \
sc/source/core/data/cellvalue \
diff --git a/sc/inc/broadcast.hxx b/sc/inc/broadcast.hxx
new file mode 100644
index 000000000000..b095f819acb7
--- /dev/null
+++ b/sc/inc/broadcast.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "address.hxx"
+
+#include <map>
+#include <variant>
+#include <ostream>
+
+class ScFormulaCell;
+class SvtListener;
+
+namespace sc
+{
+class FormulaGroupAreaListener;
+
+struct BroadcasterState
+{
+ enum class CellListenerType
+ {
+ FormulaCell,
+ Generic,
+ };
+
+ enum class AreaListenerType
+ {
+ FormulaCell,
+ FormulaGroup,
+ Generic,
+ };
+
+ struct CellListener
+ {
+ using DataType = std::variant<const ScFormulaCell*, const SvtListener*>;
+
+ CellListenerType eType;
+ DataType pData;
+
+ CellListener(const ScFormulaCell* p);
+ CellListener(const SvtListener* p);
+ };
+
+ struct AreaListener
+ {
+ using DataType = std::variant<const ScFormulaCell*, const sc::FormulaGroupAreaListener*,
+ const SvtListener*>;
+
+ AreaListenerType eType;
+ DataType pData;
+
+ AreaListener(const ScFormulaCell* p);
+ AreaListener(const sc::FormulaGroupAreaListener* p);
+ AreaListener(const SvtListener* p);
+ };
+
+ std::map<ScAddress, std::vector<CellListener>> aCellListenerStore;
+ std::map<ScRange, std::vector<AreaListener>> aAreaListenerStore;
+
+ /**
+ * Check if a formula cell listens on a single cell.
+ */
+ bool hasFormulaCellListener(const ScAddress& rBroadcasterPos,
+ const ScAddress& rFormulaPos) const;
+
+ /**
+ * Check if a formula cell listens on a single range.
+ */
+ bool hasFormulaCellListener(const ScRange& rBroadcasterRange,
+ const ScAddress& rFormulaPos) const;
+
+ /**
+ * Dump all broadcaster state in YAML format.
+ */
+ void dump(std::ostream& rStrm, const ScDocument* pDoc = nullptr) const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/calcmacros.hxx b/sc/inc/calcmacros.hxx
index c0c68d4487cf..ebe96654804a 100644
--- a/sc/inc/calcmacros.hxx
+++ b/sc/inc/calcmacros.hxx
@@ -12,7 +12,6 @@
#define DEBUG_COLUMN_STORAGE 0
#define DEBUG_PIVOT_TABLE 0
#define DEBUG_FORMULA_COMPILER 0
-#define DEBUG_AREA_BROADCASTER 0
#define DUMP_COLUMN_STORAGE 0
#define DUMP_PIVOT_TABLE 0
@@ -26,8 +25,7 @@
#if DUMP_PIVOT_TABLE || DEBUG_PIVOT_TABLE || \
DUMP_COLUMN_STORAGE || DEBUG_COLUMN_STORAGE || \
- DEBUG_FORMULA_COMPILER || \
- DEBUG_AREA_BROADCASTER
+ DEBUG_FORMULA_COMPILER
#include <iostream>
using std::cout;
using std::cerr;
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 949ca30dd137..5c42f02794ab 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -42,6 +42,7 @@ namespace formula { struct VectorRefArray; }
namespace sc {
+struct BroadcasterState;
struct FormulaGroupEntry;
class StartListeningContext;
class EndListeningContext;
@@ -864,6 +865,8 @@ private:
void EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow );
void SetNeedsListeningGroup( SCROW nRow );
+
+ void CollectBroadcasterState(sc::BroadcasterState& rState) const;
};
inline bool ScColumn::IsEmptyAttr() const
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 092d9ba0d9a9..09fcc126ed06 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -77,6 +77,7 @@ namespace tools { class Guid; }
namespace sc {
+struct BroadcasterState;
struct FormulaGroupContext;
class StartListeningContext;
class EndListeningContext;
@@ -2368,6 +2369,8 @@ public:
void EndListeningFormulaCells( std::vector<ScFormulaCell*>& rCells );
+ sc::BroadcasterState GetBroadcasterState() const;
+
void PutInFormulaTree( ScFormulaCell* pCell );
void RemoveFromFormulaTree( ScFormulaCell* pCell );
@@ -2611,10 +2614,6 @@ public:
SC_DLLPUBLIC void DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const;
#endif
-#if DEBUG_AREA_BROADCASTER
- SC_DLLPUBLIC void DumpAreaBroadcasters() const;
-#endif
-
SC_DLLPUBLIC void SetCalcConfig( const ScCalcConfig& rConfig );
const ScCalcConfig& GetCalcConfig() const { return maCalcConfig; }
void ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo );
diff --git a/sc/inc/grouparealistener.hxx b/sc/inc/grouparealistener.hxx
index d823fc987627..a52dc2fb8a74 100644
--- a/sc/inc/grouparealistener.hxx
+++ b/sc/inc/grouparealistener.hxx
@@ -60,10 +60,11 @@ public:
void collectFormulaCells( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const;
void collectFormulaCells( SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const;
+ const ScFormulaCell* getTopCell() const;
+
private:
void notifyCellChange( const SfxHint& rHint, const ScAddress& rPos, SCROW nNumRows );
void notifyBulkChange( const BulkDataHint& rHint );
- const ScFormulaCell* getTopCell() const;
};
}
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index f81e3925964e..b5fa4e9ec334 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -57,6 +57,7 @@ namespace com::sun::star {
namespace formula { struct VectorRefArray; }
namespace sc {
+struct BroadcasterState;
class StartListeningContext;
class EndListeningContext;
class CopyFromClipContext;
@@ -1156,6 +1157,8 @@ public:
void CheckIntegrity() const;
+ void CollectBroadcasterState(sc::BroadcasterState& rState) const;
+
private:
void FillFormulaVertical(
diff --git a/sc/qa/unit/ucalc_formula.cxx b/sc/qa/unit/ucalc_formula.cxx
index 059be9bbef3d..d6453cf0815d 100644
--- a/sc/qa/unit/ucalc_formula.cxx
+++ b/sc/qa/unit/ucalc_formula.cxx
@@ -25,6 +25,7 @@
#include <externalrefmgr.hxx>
#include <scmod.hxx>
#include <undomanager.hxx>
+#include <broadcast.hxx>
#include <formula/vectortoken.hxx>
#include <svl/broadcast.hxx>
@@ -6235,66 +6236,98 @@ CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaDepTracking)
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
+ const ScAddress aA5(0, 4, 0);
+ const ScAddress aB2(1, 1, 0);
+ const ScAddress aB5(1, 4, 0);
+ const ScAddress aC5(2, 4, 0);
+ const ScAddress aD2(3, 1, 0);
+ const ScAddress aD5(3, 4, 0);
+ const ScAddress aD6(3, 5, 0);
+ const ScAddress aE2(4, 1, 0);
+ const ScAddress aE3(4, 2, 0);
+ const ScAddress aE6(4, 5, 0);
+
// B2 listens on D2.
- m_pDoc->SetString(1, 1, 0, "=D2");
- double val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetString(aB2, "=D2");
+ double val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Referencing an empty cell should yield zero.", 0.0, val);
+ {
+ // Check the internal broadcaster state.
+ auto aState = m_pDoc->GetBroadcasterState();
+ aState.dump(std::cout, m_pDoc);
+ CPPUNIT_ASSERT(aState.hasFormulaCellListener(aD2, aB2));
+ }
+
// Changing the value of D2 should trigger recalculation of B2.
- m_pDoc->SetValue(3, 1, 0, 1.1);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aD2, 1.1);
+ val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 1.1, val);
// And again.
- m_pDoc->SetValue(3, 1, 0, 2.2);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aD2, 2.2);
+ val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on value change.", 2.2, val);
clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
+ {
+ // Make sure nobody is listening on anything.
+ auto aState = m_pDoc->GetBroadcasterState();
+ aState.dump(std::cout, m_pDoc);
+ CPPUNIT_ASSERT(aState.aCellListenerStore.empty());
+ }
+
// Now, let's test the range dependency tracking.
// B2 listens on D2:E6.
- m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetString(aB2, "=SUM(D2:E6)");
+ val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Summing an empty range should yield zero.", 0.0, val);
+ {
+ // Check the internal state to make sure it matches.
+ auto aState = m_pDoc->GetBroadcasterState();
+ aState.dump(std::cout, m_pDoc);
+ CPPUNIT_ASSERT(aState.hasFormulaCellListener({aD2, aE6}, aB2));
+ }
+
// Set value to E3. This should trigger recalc on B2.
- m_pDoc->SetValue(4, 2, 0, 2.4);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aE3, 2.4);
+ val = m_pDoc->GetValue(aB2);
ASSERT_DOUBLES_EQUAL_MESSAGE("Failed to recalculate on single value change.", 2.4, val);
// Set value to D5 to trigger recalc again. Note that this causes an
// addition of 1.2 + 2.4 which is subject to binary floating point
// rounding error. We need to use approxEqual to assess its value.
- m_pDoc->SetValue(3, 4, 0, 1.2);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aD5, 1.2);
+ val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
// Change the value of D2 (boundary case).
- m_pDoc->SetValue(3, 1, 0, 1.0);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aD2, 1.0);
+ val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
// Change the value of E6 (another boundary case).
- m_pDoc->SetValue(4, 5, 0, 2.0);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aE6, 2.0);
+ val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
// Change the value of D6 (another boundary case).
- m_pDoc->SetValue(3, 5, 0, 3.0);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aD6, 3.0);
+ val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
// Change the value of E2 (another boundary case).
- m_pDoc->SetValue(4, 1, 0, 0.4);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aE2, 0.4);
+ val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
// Change the existing non-empty value cell (E2).
- m_pDoc->SetValue(4, 1, 0, 2.4);
- val = m_pDoc->GetValue(1, 1, 0);
+ m_pDoc->SetValue(aE2, 2.4);
+ val = m_pDoc->GetValue(aB2);
CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
@@ -6328,10 +6361,10 @@ CPPUNIT_TEST_FIXTURE(TestFormula, testFormulaDepTracking)
// Intentionally insert a formula in column 1. This will break column 1's
// uniformity of consisting only of static value cells.
- m_pDoc->SetString(0, 4, 0, "=R2C3");
- ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(0, 4, 0));
- ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(1, 4, 0));
- ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc->GetValue(2, 4, 0));
+ m_pDoc->SetString(aA5, "=R2C3");
+ ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(aA5));
+ ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 2.0, m_pDoc->GetValue(aB5));
+ ASSERT_DOUBLES_EQUAL_MESSAGE("Unexpected formula value.", 4.0, m_pDoc->GetValue(aC5));
m_pDoc->DeleteTab(0);
}
diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx
index a39d5b4812b4..910eb46082e6 100644
--- a/sc/source/core/data/bcaslot.cxx
+++ b/sc/source/core/data/bcaslot.cxx
@@ -29,11 +29,9 @@
#include <refupdat.hxx>
#include <bulkdatahint.hxx>
#include <columnspanset.hxx>
-
-#if DEBUG_AREA_BROADCASTER
#include <formulacell.hxx>
#include <grouparealistener.hxx>
-#endif
+#include <broadcast.hxx>
ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) :
pUpdateChainNext(nullptr),
@@ -500,46 +498,32 @@ void ScBroadcastAreaSlot::GetAllListeners(
}
}
-#if DEBUG_AREA_BROADCASTER
-void ScBroadcastAreaSlot::Dump() const
+void ScBroadcastAreaSlot::CollectBroadcasterState(sc::BroadcasterState& rState) const
{
for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
{
- const ScBroadcastArea* pArea = rEntry.mpArea;
- const SvtBroadcaster& rBC = pArea->GetBroadcaster();
- const SvtBroadcaster::ListenersType& rListeners = rBC.GetAllListeners();
- size_t n = rListeners.size();
+ const ScRange& rRange = rEntry.mpArea->GetRange();
+ auto aRes = rState.aAreaListenerStore.try_emplace(rRange);
+ auto& rLisStore = aRes.first->second;
- cout << " * range: " << OUStringToOString(pArea->GetRange().Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
- << ", group: " << pArea->IsGroupListening()
- << ", listener count: " << n << endl;
-
- for (size_t i = 0; i < n; ++i)
+ for (const SvtListener* pLis : rEntry.mpArea->GetBroadcaster().GetAllListeners())
{
- const ScFormulaCell* pFC = dynamic_cast<const ScFormulaCell*>(rListeners[i]);
- if (pFC)
+ if (auto pFC = dynamic_cast<const ScFormulaCell*>(pLis); pFC)
{
- cout << " * listener: formula cell: "
- << OUStringToOString(pFC->aPos.Format(ScRefFlags::VALID|ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
- << endl;
+ rLisStore.emplace_back(pFC);
continue;
}
- const sc::FormulaGroupAreaListener* pFGListener = dynamic_cast<const sc::FormulaGroupAreaListener*>(rListeners[i]);
- if (pFGListener)
+ if (auto pFGL = dynamic_cast<const sc::FormulaGroupAreaListener*>(pLis); pFGL)
{
- cout << " * listener: formula group: (pos: "
- << OUStringToOString(pFGListener->getTopCellPos().Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc), RTL_TEXTENCODING_UTF8).getStr()
- << ", length: " << pFGListener->getGroupLength()
- << ")" << endl;
+ rLisStore.emplace_back(pFGL);
continue;
}
- cout << " * listener: unknown" << endl;
+ rLisStore.emplace_back(pLis);
}
}
}
-#endif
void ScBroadcastAreaSlot::FinallyEraseAreas()
{
@@ -1283,27 +1267,20 @@ std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
return aRet;
}
-#if DEBUG_AREA_BROADCASTER
-void ScBroadcastAreaSlotMachine::Dump() const
+void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState& rState) const
{
- cout << "slot distribution count: " << nBcaSlots << endl;
- for (const auto& [rIndex, pTabSlots] : aTableSlotsMap)
+ for (const auto& [rTab, rTabSlots] : aTableSlotsMap)
{
- cout << "-- sheet (index: " << rIndex << ")" << endl;
+ (void)rTab;
- assert(pTabSlots);
- ScBroadcastAreaSlot** ppSlots = pTabSlots->getSlots();
- for (SCSIZE i = 0; i < nBcaSlots; ++i)
+ ScBroadcastAreaSlot** pp = rTabSlots.getSlots();
+ for (SCSIZE i = 0; i < mnBcaSlots; ++i)
{
- const ScBroadcastAreaSlot* pSlot = ppSlots[i];
+ const ScBroadcastAreaSlot* pSlot = pp[i];
if (pSlot)
- {
- cout << "* slot " << i << endl;
- pSlot->Dump();
- }
+ pSlot->CollectBroadcasterState(rState);
}
}
}
-#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/broadcast.cxx b/sc/source/core/data/broadcast.cxx
new file mode 100644
index 000000000000..c0cd0c29edca
--- /dev/null
+++ b/sc/source/core/data/broadcast.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <broadcast.hxx>
+#include <address.hxx>
+#include <formulacell.hxx>
+
+namespace sc
+{
+BroadcasterState::CellListener::CellListener(const ScFormulaCell* p)
+ : eType(CellListenerType::FormulaCell)
+ , pData(p)
+{
+}
+
+BroadcasterState::CellListener::CellListener(const SvtListener* p)
+ : eType(CellListenerType::Generic)
+ , pData(p)
+{
+}
+
+BroadcasterState::AreaListener::AreaListener(const ScFormulaCell* p)
+ : eType(AreaListenerType::FormulaCell)
+ , pData(p)
+{
+}
+
+BroadcasterState::AreaListener::AreaListener(const sc::FormulaGroupAreaListener* p)
+ : eType(AreaListenerType::FormulaGroup)
+ , pData(p)
+{
+}
+
+BroadcasterState::AreaListener::AreaListener(const SvtListener* p)
+ : eType(AreaListenerType::Generic)
+ , pData(p)
+{
+}
+
+bool BroadcasterState::hasFormulaCellListener(const ScAddress& rBroadcasterPos,
+ const ScAddress& rFormulaPos) const
+{
+ auto it = aCellListenerStore.find(rBroadcasterPos);
+ if (it == aCellListenerStore.end())
+ return false;
+
+ for (const auto& rLis : it->second)
+ {
+ if (rLis.eType == CellListenerType::FormulaCell)
+ {
+ auto pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ if (pFC->aPos == rFormulaPos)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BroadcasterState::hasFormulaCellListener(const ScRange& rBroadcasterRange,
+ const ScAddress& rFormulaPos) const
+{
+ auto it = aAreaListenerStore.find(rBroadcasterRange);
+ if (it == aAreaListenerStore.end())
+ return false;
+
+ for (const auto& rLis : it->second)
+ {
+ if (rLis.eType == AreaListenerType::FormulaCell)
+ {
+ auto pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ if (pFC->aPos == rFormulaPos)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void BroadcasterState::dump(std::ostream& rStrm, const ScDocument* pDoc) const
+{
+ constexpr ScRefFlags nPosFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+
+ rStrm << "---" << std::endl;
+
+ for (const auto & [ rPos, rListeners ] : aCellListenerStore)
+ {
+ rStrm << "- type: cell-broadcaster\n";
+ rStrm << " position: " << rPos.Format(nPosFlags, pDoc) << std::endl;
+
+ if (!rListeners.empty())
+ rStrm << " listeners:\n";
+
+ for (const auto& rLis : rListeners)
+ {
+ switch (rLis.eType)
+ {
+ case BroadcasterState::CellListenerType::FormulaCell:
+ {
+ auto* pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ rStrm << " - type: formula-cell\n";
+ rStrm << " position: " << pFC->aPos.Format(nPosFlags, pDoc) << std::endl;
+ break;
+ }
+ case BroadcasterState::CellListenerType::Generic:
+ {
+ rStrm << " - type: unknown" << std::endl;
+ break;
+ }
+ }
+ }
+ }
+
+ for (const auto & [ rRange, rListeners ] : aAreaListenerStore)
+ {
+ rStrm << "- type: area-broadcaster\n";
+ rStrm << " range: " << rRange.Format(*pDoc, nPosFlags) << std::endl;
+
+ if (!rListeners.empty())
+ rStrm << " listeners:\n";
+
+ for (const auto& rLis : rListeners)
+ {
+ switch (rLis.eType)
+ {
+ case BroadcasterState::AreaListenerType::FormulaCell:
+ {
+ auto* pFC = std::get<const ScFormulaCell*>(rLis.pData);
+ rStrm << " - type: formula-cell\n";
+ rStrm << " position: " << pFC->aPos.Format(nPosFlags, pDoc) << std::endl;
+ break;
+ }
+ case BroadcasterState::AreaListenerType::FormulaGroup:
+ {
+ auto* pFGL = std::get<const sc::FormulaGroupAreaListener*>(rLis.pData);
+
+ auto pTopCell = pFGL->getTopCell();
+ if (auto xFG = pTopCell->GetCellGroup(); xFG)
+ {
+ ScRange aGR(pTopCell->aPos);
+ aGR.aEnd.IncRow(xFG->mnLength - 1);
+ rStrm << " - type: formula-group\n";
+ rStrm << " range: " << aGR.Format(*pDoc, nPosFlags) << std::endl;
+ }
+ break;
+ }
+ case BroadcasterState::AreaListenerType::Generic:
+ {
+ rStrm << " - type: unknown" << std::endl;
+ break;
+ }
+ }
+ }
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 49420642bf5f..a9b1b91857f8 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -31,6 +31,7 @@
#include <recursionhelper.hxx>
#include <docsh.hxx>
#include <editutil.hxx>
+#include <broadcast.hxx>
#include <SparklineGroup.hxx>
@@ -2233,4 +2234,34 @@ void ScColumn::CheckIntegrity() const
}
}
+void ScColumn::CollectBroadcasterState(sc::BroadcasterState& rState) const
+{
+ for (const auto& block : maBroadcasters)
+ {
+ if (block.type != sc::element_type_broadcaster)
+ continue;
+
+ auto itBeg = sc::broadcaster_block::begin(*block.data);
+ auto itEnd = sc::broadcaster_block::end(*block.data);
+
+ for (auto it = itBeg; it != itEnd; ++it)
+ {
+ ScAddress aBCPos(nCol, block.position + std::distance(itBeg, it), nTab);
+
+ auto aRes = rState.aCellListenerStore.try_emplace(aBCPos);
+ auto& rLisStore = aRes.first->second;
+
+ const SvtBroadcaster& rBC = **it;
+ for (const SvtListener* pLis : rBC.GetAllListeners())
+ {
+ const auto* pFC = dynamic_cast<const ScFormulaCell*>(pLis);
+ if (pFC)
+ rLisStore.emplace_back(pFC);
+ else
+ rLisStore.emplace_back(pLis);
+ }
+ }
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index e204acd3cd24..b53de471e776 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2529,14 +2529,6 @@ void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const
}
#endif
-#if DEBUG_AREA_BROADCASTER
-void ScDocument::DumpAreaBroadcasters() const
-{
- if (pBASM)
- pBASM->Dump();
-}
-#endif
-
bool ScDocument::TableExists( SCTAB nTab ) const
{
return ValidTab(nTab) && o3tl::make_unsigned(nTab) < maTabs.size() && maTabs[nTab];
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index e0eb8d46bd28..baee3603fd1f 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -26,6 +26,8 @@
#include <scitems.hxx>
#include <datamapper.hxx>
#include <docsh.hxx>
+#include <bcaslot.hxx>
+#include <broadcast.hxx>
// Add totally brand-new methods to this source file.
@@ -1101,4 +1103,17 @@ void ScDocument::CheckIntegrity( SCTAB nTab ) const
pTab->CheckIntegrity();
}
+sc::BroadcasterState ScDocument::GetBroadcasterState() const
+{
+ sc::BroadcasterState aState;
+
+ for (const auto& xTab : maTabs)
+ xTab->CollectBroadcasterState(aState);
+
+ if (pBASM)
+ pBASM->CollectBroadcasterState(aState);
+
+ return aState;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
index 9af01cba748e..7755bc6176de 100644
--- a/sc/source/core/data/table7.cxx
+++ b/sc/source/core/data/table7.cxx
@@ -649,4 +649,10 @@ void ScTable::CheckIntegrity() const
pCol->CheckIntegrity();
}
+void ScTable::CollectBroadcasterState(sc::BroadcasterState& rState) const
+{
+ for (const auto& pCol : aCol)
+ pCol->CollectBroadcasterState(rState);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx
index 4e1173596840..6e891e8aab5a 100644
--- a/sc/source/core/inc/bcaslot.hxx
+++ b/sc/source/core/inc/bcaslot.hxx
@@ -30,7 +30,12 @@
#include <document.hxx>
#include <global.hxx>
-namespace sc { class ColumnSpanSet; }
+namespace sc {
+
+struct BroadcasterState;
+class ColumnSpanSet;
+
+}
class ScHint;
namespace sc {
@@ -233,9 +238,7 @@ public:
const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
sc::AreaOverlapType eType, sc::ListenerGroupType eGroup );
-#if DEBUG_AREA_BROADCASTER
- void Dump() const;
-#endif
+ void CollectBroadcasterState(sc::BroadcasterState& rState) const;
};
/**
@@ -364,9 +367,7 @@ public:
const ScRange& rRange, sc::AreaOverlapType eType,
sc::ListenerGroupType eGroup = sc::ListenerGroupType::Both );
-#if DEBUG_AREA_BROADCASTER
- void Dump() const;
-#endif
+ void CollectBroadcasterState(sc::BroadcasterState& rState) const;
};
class ScBulkBroadcast