[Libreoffice-commits] core.git: 24 commits - include/svl sc/inc sc/Library_sc.mk sc/qa sc/source svl/source

Kohei Yoshida kohei.yoshida at collabora.com
Tue Nov 18 05:33:06 PST 2014


 include/svl/broadcast.hxx                 |    3 
 sc/Library_sc.mk                          |    2 
 sc/inc/address.hxx                        |    3 
 sc/inc/bulkdatahint.hxx                   |   43 ++++
 sc/inc/calcmacros.hxx                     |    3 
 sc/inc/clipcontext.hxx                    |   55 +++--
 sc/inc/column.hxx                         |   24 +-
 sc/inc/columnspanset.hxx                  |    2 
 sc/inc/document.hxx                       |   29 +--
 sc/inc/formulacell.hxx                    |   12 +
 sc/inc/grouparealistener.hxx              |   63 ++++++
 sc/inc/sharedformula.hxx                  |   14 +
 sc/inc/simplehintids.hxx                  |    9 
 sc/inc/table.hxx                          |   19 +
 sc/qa/unit/ucalc.cxx                      |  106 +++++++++++
 sc/qa/unit/ucalc.hxx                      |    2 
 sc/source/core/data/bcaslot.cxx           |  286 +++++++++++++++++++++++++++---
 sc/source/core/data/clipcontext.cxx       |  232 +++++++++++++++++++++++-
 sc/source/core/data/colorscale.cxx        |    2 
 sc/source/core/data/column.cxx            |   24 ++
 sc/source/core/data/column3.cxx           |   88 ++++-----
 sc/source/core/data/column4.cxx           |  135 ++++++++++++--
 sc/source/core/data/columnspanset.cxx     |    5 
 sc/source/core/data/documen2.cxx          |    4 
 sc/source/core/data/documen7.cxx          |   79 ++++++--
 sc/source/core/data/document.cxx          |  137 ++++++++++----
 sc/source/core/data/document10.cxx        |  150 ++-------------
 sc/source/core/data/documentimport.cxx    |   22 +-
 sc/source/core/data/formulacell.cxx       |   58 +++++-
 sc/source/core/data/table2.cxx            |   23 +-
 sc/source/core/data/table3.cxx            |    8 
 sc/source/core/data/table4.cxx            |    4 
 sc/source/core/data/table7.cxx            |   17 +
 sc/source/core/inc/bcaslot.hxx            |  112 +++++++----
 sc/source/core/tool/address.cxx           |   15 +
 sc/source/core/tool/bulkdatahint.cxx      |   49 +++++
 sc/source/core/tool/chartlis.cxx          |    4 
 sc/source/core/tool/grouparealistener.cxx |  232 ++++++++++++++++++++++++
 sc/source/core/tool/sharedformula.cxx     |   89 +++++++++
 sc/source/ui/docshell/macromgr.cxx        |    2 
 sc/source/ui/docshell/servobj.cxx         |    6 
 sc/source/ui/unoobj/cellsuno.cxx          |    4 
 sc/source/ui/unoobj/chart2uno.cxx         |    4 
 svl/source/notify/broadcast.cxx           |    9 
 44 files changed, 1784 insertions(+), 405 deletions(-)

New commits:
commit 4cbe6f95e00d7a8b796b019a97d06e3b3ad52b47
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Tue Nov 18 00:11:04 2014 -0500

    Simplify DeleteCells to not require 2 out parameters for the same thing.
    
    One can easily get constructed from the other after returning from the method.
    
    Change-Id: Iafc52efa7714b7cf4d284effda8034cb6f36bf91

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index a1466ae..1e9c7d7 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -650,7 +650,7 @@ private:
 
     void DeleteCells(
         sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag,
-        std::vector<SCROW>& rDeleted, sc::ColumnSpanSet* pDeletedSpans = NULL );
+        sc::SingleColumnSpanSet& rDeleted );
 
     /**
      * Get all non-grouped formula cells and formula cell groups in the whole
diff --git a/sc/inc/columnspanset.hxx b/sc/inc/columnspanset.hxx
index f397c3e..48ae7c1 100644
--- a/sc/inc/columnspanset.hxx
+++ b/sc/inc/columnspanset.hxx
@@ -141,6 +141,8 @@ public:
 
     void getSpans(SpansType& rSpans) const;
 
+    void swap( SingleColumnSpanSet& r );
+
 private:
     ColumnSpansType maSpans;
 };
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index bba48f2..a58cd07 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -549,7 +549,7 @@ public:
         mrDoc.EndListeningFormulaCells(maFormulaCells);
     }
 
-    const sc::SingleColumnSpanSet& getSpans() const
+    sc::SingleColumnSpanSet& getSpans()
     {
         return maDeleteRanges;
     }
@@ -559,7 +559,6 @@ class EmptyCells
 {
     ScColumn& mrColumn;
     sc::ColumnBlockPosition& mrPos;
-    sc::ColumnSpanSet* mpCellSpans;
 
     void splitFormulaGrouping(const sc::CellStoreType::position_type& rPos)
     {
@@ -571,8 +570,8 @@ class EmptyCells
     }
 
 public:
-    EmptyCells( sc::ColumnBlockPosition& rPos, ScColumn& rColumn, sc::ColumnSpanSet* pCellSpans ) :
-        mrColumn(rColumn), mrPos(rPos), mpCellSpans(pCellSpans) {}
+    EmptyCells( sc::ColumnBlockPosition& rPos, ScColumn& rColumn ) :
+        mrColumn(rColumn), mrPos(rPos) {}
 
     void operator() (const sc::RowSpan& rSpan)
     {
@@ -587,9 +586,6 @@ public:
 
         mrPos.miCellPos = rCells.set_empty(mrPos.miCellPos, rSpan.mnRow1, rSpan.mnRow2);
         mrPos.miCellTextAttrPos = mrColumn.GetCellAttrStore().set_empty(mrPos.miCellTextAttrPos, rSpan.mnRow1, rSpan.mnRow2);
-
-        if (mpCellSpans)
-            mpCellSpans->set(mrColumn.GetTab(), mrColumn.GetCol(), rSpan.mnRow1, rSpan.mnRow2, true);
     }
 };
 
@@ -597,7 +593,7 @@ public:
 
 void ScColumn::DeleteCells(
     sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nDelFlag,
-    std::vector<SCROW>& rDeleted, sc::ColumnSpanSet* pDeletedSpans )
+    sc::SingleColumnSpanSet& rDeleted )
 {
     // Determine which cells to delete based on the deletion flags.
     DeleteAreaHandler aFunc(*pDocument, nDelFlag);
@@ -605,17 +601,15 @@ void ScColumn::DeleteCells(
     sc::ProcessBlock(itPos, maCells, aFunc, nRow1, nRow2);
     aFunc.endFormulas(); // Have the formula cells stop listening.
 
-    std::vector<SCROW> aDeletedRows;
-    aFunc.getSpans().getRows(aDeletedRows);
-    std::copy(aDeletedRows.begin(), aDeletedRows.end(), std::back_inserter(rDeleted));
-
     // Get the deletion spans.
     sc::SingleColumnSpanSet::SpansType aSpans;
     aFunc.getSpans().getSpans(aSpans);
 
     // Delete the cells for real.
-    std::for_each(aSpans.begin(), aSpans.end(), EmptyCells(rBlockPos, *this, pDeletedSpans));
+    std::for_each(aSpans.begin(), aSpans.end(), EmptyCells(rBlockPos, *this));
     CellStorageModified();
+
+    aFunc.getSpans().swap(rDeleted);
 }
 
 void ScColumn::DeleteArea(
@@ -628,13 +622,23 @@ void ScColumn::DeleteArea(
         nContMask |= IDF_NOCAPTIONS;
     InsertDeleteFlags nContFlag = nDelFlag & nContMask;
 
-    std::vector<SCROW> aDeletedRows;
+    sc::SingleColumnSpanSet aDeletedRows;
 
     sc::ColumnBlockPosition aBlockPos;
     InitBlockPosition(aBlockPos);
 
     if (!IsEmptyData() && nContFlag)
-        DeleteCells(aBlockPos, nStartRow, nEndRow, nDelFlag, aDeletedRows, pBroadcastSpans);
+    {
+        DeleteCells(aBlockPos, nStartRow, nEndRow, nDelFlag, aDeletedRows);
+        if (pBroadcastSpans)
+        {
+            sc::SingleColumnSpanSet::SpansType aSpans;
+            aDeletedRows.getSpans(aSpans);
+            sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
+            for (; it != itEnd; ++it)
+                pBroadcastSpans->set(nTab, nCol, it->mnRow1, it->mnRow2, true);
+        }
+    }
 
     if (nDelFlag & IDF_NOTE)
         DeleteCellNotes(aBlockPos, nStartRow, nEndRow);
@@ -655,7 +659,9 @@ void ScColumn::DeleteArea(
     {
         // Broadcast on only cells that were deleted; no point broadcasting on
         // cells that were already empty before the deletion.
-        BroadcastCells(aDeletedRows, SC_HINT_DATACHANGED);
+        std::vector<SCROW> aRows;
+        aDeletedRows.getRows(aRows);
+        BroadcastCells(aRows, SC_HINT_DATACHANGED);
     }
 }
 
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 40f18bf..2316a26 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -96,7 +96,6 @@ void ScColumn::DeleteBeforeCopyFromClip(
         nDestOffset += nClipRowLen;
     }
 
-    std::vector<SCROW> aDeletedRows;
     InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag();
     sc::ColumnBlockPosition aBlockPos;
     InitBlockPosition(aBlockPos);
@@ -108,7 +107,15 @@ void ScColumn::DeleteBeforeCopyFromClip(
         SCROW nRow2 = it->mnRow2;
 
         if (nDelFlag & IDF_CONTENTS)
-            DeleteCells(aBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows, &rBroadcastSpans);
+        {
+            sc::SingleColumnSpanSet aDeletedRows;
+            DeleteCells(aBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows);
+            aDeletedRows.getSpans(aSpans);
+            it = aSpans.begin();
+            itEnd = aSpans.end();
+            for (; it != itEnd; ++it)
+                rBroadcastSpans.set(nTab, nCol, it->mnRow1, it->mnRow2, true);
+        }
 
         if (nDelFlag & IDF_NOTE)
             DeleteCellNotes(aBlockPos, nRow1, nRow2);
diff --git a/sc/source/core/data/columnspanset.cxx b/sc/source/core/data/columnspanset.cxx
index faf99ad..fe10528 100644
--- a/sc/source/core/data/columnspanset.cxx
+++ b/sc/source/core/data/columnspanset.cxx
@@ -324,6 +324,11 @@ void SingleColumnSpanSet::getSpans(SpansType& rSpans) const
     rSpans.swap(aSpans);
 }
 
+void SingleColumnSpanSet::swap( SingleColumnSpanSet& r )
+{
+    maSpans.swap(r.maSpans);
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 33751e97642c190dcd8692c46b4df2e7dce504d1
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 23:34:42 2014 -0500

    Extend the test to cover insertion / deletion of rows.
    
    Change-Id: I9c5f46af446f8384265d91ce5dfe269e7a090d09

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 960b180..248a0e9 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -4011,6 +4011,45 @@ void Test::testCopyPasteRepeatOneFormula()
     CPPUNIT_ASSERT_EQUAL(ScRange(0,0,0,1,9,0), pListener->maArea);
     CPPUNIT_ASSERT_MESSAGE("This listener should be a group listener.", pListener->mbGroupListening);
 
+    // Insert a new row at row 1.
+    ScRange aRowOne(0,0,0,MAXCOL,0,0);
+    aMark.SetMarkArea(aRowOne);
+    ScDocFunc& rFunc = getDocShell().GetDocFunc();
+    rFunc.InsertCells(aRowOne, &aMark, INS_INSROWS, true, true, false);
+
+    CPPUNIT_ASSERT_MESSAGE("C1 should be empty.", m_pDoc->GetCellType(ScAddress(2,0,0)) == CELLTYPE_NONE);
+
+    // Make there we only have one group area listener listening on A2:B11.
+    aListeners = pBASM->GetAllListeners(aWholeSheet, sc::AreaInside);
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aListeners.size());
+    pListener = &aListeners[0];
+    CPPUNIT_ASSERT_EQUAL(ScRange(0,1,0,1,10,0), pListener->maArea);
+    CPPUNIT_ASSERT_MESSAGE("This listener should be a group listener.", pListener->mbGroupListening);
+
+    // Check the formula results.
+    for (SCROW i = 0; i < 10; ++i)
+    {
+        double fExpected = (i+1.0)*11.0;
+        CPPUNIT_ASSERT_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(2,i+1,0)));
+    }
+
+    // Delete row at row 1 to shift the cells up.
+    rFunc.DeleteCells(aRowOne, &aMark, DEL_DELROWS, true, true);
+
+    // Check the formula results again.
+    for (SCROW i = 0; i < 10; ++i)
+    {
+        double fExpected = (i+1.0)*11.0;
+        CPPUNIT_ASSERT_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(2,i,0)));
+    }
+
+    // Check the group area listener again to make sure it's listening on A1:B10 once again.
+    aListeners = pBASM->GetAllListeners(aWholeSheet, sc::AreaInside);
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aListeners.size());
+    pListener = &aListeners[0];
+    CPPUNIT_ASSERT_EQUAL(ScRange(0,0,0,1,9,0), pListener->maArea);
+    CPPUNIT_ASSERT_MESSAGE("This listener should be a group listener.", pListener->mbGroupListening);
+
     m_pDoc->DeleteTab(0);
 }
 
commit 1acc10db8c6fd413567ced4feb2543ac8f150eb0
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 23:25:28 2014 -0500

    Register group area listeners as needed when inserting/deleting cells.
    
    Change-Id: I583ace5d134d526d660d4ff0bbf4a16aa10cbe5a

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 499825f..a1466ae 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -490,7 +490,7 @@ public:
     void StartListening( sc::StartListeningContext& rCxt, SCROW nRow, SvtListener& rListener );
     void EndListening( sc::EndListeningContext& rCxt, SCROW nRow, SvtListener& rListener );
     void        StartAllListeners();
-    void        StartNeededListeners(); // only for cells where NeedsListening()==true
+    void StartNeededListeners( sc::StartListeningContext& rCxt ); // only for cells where NeedsListening()==true
     void        SetDirtyIfPostponed();
     void BroadcastRecalcOnRefMove();
     void TransferListeners( ScColumn& rDestCol, SCROW nRow1, SCROW nRow2, SCROW nRowDelta );
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 7d802e3..074b5d08 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -916,7 +916,7 @@ public:
      * Have formula cells with NeedsListening() == true start listening to the
      * document.
      */
-    void StartNeededListeners();
+    void StartNeededListeners( sc::StartListeningContext& rCxt );
 
     /**
      * Mark formula cells dirty that have the mbPostponedDirty flag set or
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 67c51be..bba48f2 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1506,16 +1506,34 @@ public:
     }
 };
 
-class StartNeededListenerHandler
+class StartNeededListenersHandler
 {
-    ScDocument* mpDoc;
+    sc::StartListeningContext* mpCxt;
 public:
-    StartNeededListenerHandler(ScDocument* pDoc) : mpDoc(pDoc) {}
+    StartNeededListenersHandler( sc::StartListeningContext& rCxt ) : mpCxt(&rCxt) {}
 
-    void operator() (size_t, ScFormulaCell* p)
+    void operator() ( sc::CellStoreType::value_type& aBlk )
     {
-        if (p->NeedsListening())
-            p->StartListeningTo(mpDoc);
+        if (aBlk.type != sc::element_type_formula)
+            return;
+
+        ScFormulaCell** pp = &sc::formula_block::at(*aBlk.data, 0);
+        ScFormulaCell** ppEnd = pp + aBlk.size;
+
+        for (; pp != ppEnd; ++pp)
+        {
+            ScFormulaCell& rFC = **pp;
+            if (!rFC.NeedsListening())
+                continue;
+
+            if (rFC.IsSharedTop())
+            {
+                sc::SharedFormulaUtil::startListeningAsGroup(*mpCxt, pp);
+                pp += rFC.GetSharedLength() - 1; // Move to the last cell in the group.
+            }
+            else
+                rFC.StartListeningTo(*mpCxt);
+        }
     }
 };
 
@@ -1527,10 +1545,9 @@ void ScColumn::StartAllListeners()
     sc::ProcessFormula(maCells, aFunc);
 }
 
-void ScColumn::StartNeededListeners()
+void ScColumn::StartNeededListeners( sc::StartListeningContext& rCxt )
 {
-    StartNeededListenerHandler aFunc(pDocument);
-    sc::ProcessFormula(maCells, aFunc);
+    std::for_each(maCells.begin(), maCells.end(), StartNeededListenersHandler(rCxt));
 }
 
 namespace {
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index f23da4a..fb6e240 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -1164,12 +1164,16 @@ bool ScDocument::CanInsertRow( const ScRange& rRange ) const
 
 namespace {
 
-struct StartNeededListenersHandler : std::unary_function<ScTable*, void>
+class StartNeededListenersHandler : std::unary_function<ScTable*, void>
 {
+    boost::shared_ptr<sc::StartListeningContext> mpCxt;
+public:
+    StartNeededListenersHandler( ScDocument& rDoc ) : mpCxt(new sc::StartListeningContext(rDoc)) {}
+
     void operator() (ScTable* p)
     {
         if (p)
-            p->StartNeededListeners();
+            p->StartNeededListeners(*mpCxt);
     }
 };
 
@@ -1264,14 +1268,12 @@ bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
         }
         else
         {   // Listeners have been removed in UpdateReference
-            TableContainer::iterator it = maTabs.begin();
-            for (; it != maTabs.end(); ++it)
-                if (*it)
-                    (*it)->StartNeededListeners();
+            std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this));
+
             // At least all cells using range names pointing relative to the
             // moved range must be recalculated, and all cells marked postponed
             // dirty.
-            it = maTabs.begin();
+            TableContainer::iterator it = maTabs.begin();
             for (; it != maTabs.end(); ++it)
                 if (*it)
                     (*it)->SetDirtyIfPostponed();
@@ -1357,14 +1359,13 @@ void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
             maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline);
 
     if ( ValidRow(nStartRow+nSize) )
-    {   // Listeners have been removed in UpdateReference
-        TableContainer::iterator it = maTabs.begin();
-        for (; it != maTabs.end(); ++it)
-            if (*it)
-                (*it)->StartNeededListeners();
+    {
+        // Listeners have been removed in UpdateReference
+        std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this));
+
         // At least all cells using range names pointing relative to the moved
         // range must be recalculated, and all cells marked postponed dirty.
-        it = maTabs.begin();
+        TableContainer::iterator it = maTabs.begin();
         for (; it != maTabs.end(); ++it)
             if (*it)
                 (*it)->SetDirtyIfPostponed();
@@ -1466,7 +1467,7 @@ bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
         else
         {
             // Listeners have been removed in UpdateReference
-            std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler());
+            std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this));
             // At least all cells using range names pointing relative to the
             // moved range must be recalculated, and all cells marked postponed
             // dirty.
@@ -1553,14 +1554,13 @@ void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTA
     }
 
     if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
-    {// Listeners have been removed in UpdateReference
-        TableContainer::iterator it = maTabs.begin();
-        for (; it != maTabs.end(); ++it)
-            if (*it)
-                (*it)->StartNeededListeners();
+    {
+        // Listeners have been removed in UpdateReference
+        std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this));
+
         // At least all cells using range names pointing relative to the moved
         // range must be recalculated, and all cells marked postponed dirty.
-        it = maTabs.begin();
+        TableContainer::iterator it = maTabs.begin();
         for (; it != maTabs.end(); ++it)
             if (*it)
                 (*it)->SetDirtyIfPostponed();
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 8855d65..f1d2d50 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1043,10 +1043,10 @@ void ScTable::DetachFormulaCells(
         aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2);
 }
 
-void ScTable::StartNeededListeners()
+void ScTable::StartNeededListeners( sc::StartListeningContext& rCxt )
 {
     for (SCCOL i=0; i<=MAXCOL; i++)
-        aCol[i].StartNeededListeners();
+        aCol[i].StartNeededListeners(rCxt);
 }
 
 void ScTable::SetDirtyFromClip(
commit 087b6041794a6a04c2e957ebf9a03fd223fa0fb3
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 22:46:34 2014 -0500

    Unused local variable.
    
    Change-Id: I6a3d191b10eec119eea5af6218e6c298f2826b33

diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index a019139..73c64f2 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -2057,7 +2057,6 @@ class UpdateRefOnNonCopy : std::unary_function<sc::FormulaGroupEntry, void>
 
         if (aRes.mbReferenceModified || aRes.mbNameModified)
         {
-            sc::StartListeningContext aStartCxt(mpCxt->mrDoc);
             sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pOldCode.get());
             aEndCxt.setPositionDelta(
                 ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
commit bdc2c32f41ca6ca011ba68200ffde23a0740fc85
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 21:37:48 2014 -0500

    Regroup formula cells in columns before initializing them.
    
    Because the initialization of area listeners now depend on the grouped
    status of formula cells.
    
    Change-Id: Idf61f57387ba62c57d87030c16544bc07836826f

diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx
index 02242cb..48878f5 100644
--- a/sc/source/core/data/documentimport.cxx
+++ b/sc/source/core/data/documentimport.cxx
@@ -604,10 +604,12 @@ void ScDocumentImport::finalize()
 
 void ScDocumentImport::initColumn(ScColumn& rCol)
 {
+    rCol.RegroupFormulaCells();
+
     CellStoreInitializer aFunc(*mpImpl, rCol.nTab, rCol.nCol);
     std::for_each(rCol.maCells.begin(), rCol.maCells.end(), aFunc);
     aFunc.swap(rCol.maCellTextAttrs);
-    rCol.RegroupFormulaCells();
+
     rCol.CellStorageModified();
 }
 
commit 930af2a8ed2e6154a13886a8eabfd1920161b1d3
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 21:23:30 2014 -0500

    New test for testing group area listener behaviors during formula pasting.
    
    Change-Id: Ie0c4e39e30a33ec5390c03221950d1cd01549c15

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 0c081c1..960b180 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -65,6 +65,7 @@
 #include <inputopt.hxx>
 
 #include <editable.hxx>
+#include <bcaslot.hxx>
 
 #include <formula/IFunctionDescription.hxx>
 
@@ -3947,6 +3948,72 @@ void Test::testCopyPasteRelativeFormula()
     m_pDoc->DeleteTab(0);
 }
 
+void Test::testCopyPasteRepeatOneFormula()
+{
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
+
+    m_pDoc->InsertTab(0, "Test");
+
+    ScDocument aClipDoc(SCDOCMODE_CLIP);
+    ScMarkData aMark;
+
+    // Insert values in A1:B10.
+    for (SCROW i = 0; i < 10; ++i)
+    {
+        m_pDoc->SetValue(ScAddress(0,i,0), i+1.0);        // column A
+        m_pDoc->SetValue(ScAddress(1,i,0), (i+1.0)*10.0); // column B
+    }
+
+    // Insert a formula in C1.
+    ScAddress aPos(2,0,0); // C1
+    m_pDoc->SetString(aPos, "=SUM(A1:B1)");
+    CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(aPos));
+
+    // At this point, there should be only one normal area listener listening
+    // on A1:B1.
+    ScRange aWholeSheet(0,0,0,MAXCOL,MAXROW,0);
+    ScBroadcastAreaSlotMachine* pBASM = m_pDoc->GetBASM();
+    CPPUNIT_ASSERT(pBASM);
+    std::vector<sc::AreaListener> aListeners = pBASM->GetAllListeners(aWholeSheet, sc::AreaInside);
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aListeners.size());
+    const sc::AreaListener* pListener = &aListeners[0];
+    CPPUNIT_ASSERT_EQUAL(ScRange(0,0,0,1,0,0), pListener->maArea);
+    CPPUNIT_ASSERT_MESSAGE("This listener shouldn't be a group listener.", !pListener->mbGroupListening);
+
+    // Copy C1 to clipboard.
+    ScClipParam aClipParam(aPos, false);
+    aMark.SetMarkArea(aPos);
+    m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
+
+    // Paste it to C2:C10.
+    InsertDeleteFlags nFlags = IDF_CONTENTS;
+    ScRange aDestRange(2,1,0,2,9,0);
+    aMark.SetMarkArea(aDestRange);
+    m_pDoc->CopyFromClip(aDestRange, aMark, nFlags, NULL, &aClipDoc);
+
+    // Make sure C1:C10 are grouped.
+    const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
+    CPPUNIT_ASSERT(pFC);
+    CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedLength());
+
+    // Check the formula results.
+    for (SCROW i = 0; i < 10; ++i)
+    {
+        double fExpected = (i+1.0)*11.0;
+        CPPUNIT_ASSERT_EQUAL(fExpected, m_pDoc->GetValue(ScAddress(2,i,0)));
+    }
+
+    // At this point, there should only be one area listener and it should be
+    // a group listener listening on A1:B10.
+    aListeners = pBASM->GetAllListeners(aWholeSheet, sc::AreaInside);
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aListeners.size());
+    pListener = &aListeners[0];
+    CPPUNIT_ASSERT_EQUAL(ScRange(0,0,0,1,9,0), pListener->maArea);
+    CPPUNIT_ASSERT_MESSAGE("This listener should be a group listener.", pListener->mbGroupListening);
+
+    m_pDoc->DeleteTab(0);
+}
+
 void Test::testMergedCells()
 {
     //test merge and unmerge
diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index 41416bd..99883c5 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -291,6 +291,7 @@ public:
     void testUndoCut();
     void testMoveBlock();
     void testCopyPasteRelativeFormula();
+    void testCopyPasteRepeatOneFormula();
     void testMergedCells();
     void testUpdateReference();
     void testSearchCells();
@@ -518,6 +519,7 @@ public:
     CPPUNIT_TEST(testUndoCut);
     CPPUNIT_TEST(testMoveBlock);
     CPPUNIT_TEST(testCopyPasteRelativeFormula);
+    CPPUNIT_TEST(testCopyPasteRepeatOneFormula);
     CPPUNIT_TEST(testMergedCells);
     CPPUNIT_TEST(testUpdateReference);
     CPPUNIT_TEST(testSearchCells);
commit 01a50d8e15d286ee35b7dbff0aa2724137351a50
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 18:35:53 2014 -0500

    Rename these to make it clear that they are only to be called from ...
    
    ScDocument::CopyFromClip.
    
    Change-Id: Ia95b65ca7982735b9c217a7b60d194117d64e774

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 6187d4e..499825f 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -255,7 +255,7 @@ public:
     void CopyFromClip(
         sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, long nDy, ScColumn& rColumn );
 
-    void StartListeningInArea(
+    void StartListeningFromClip(
         sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, SCROW nRow1, SCROW nRow2 );
 
     void        RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow );
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index b1e06e1..7d802e3 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -436,7 +436,7 @@ public:
         sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
         SCsCOL nDx, SCsROW nDy, ScTable* pTable );
 
-    void StartListeningInArea(
+    void StartListeningFromClip(
         sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
         SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
 
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 925bb4c..40f18bf 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -1137,13 +1137,13 @@ bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
 
 namespace {
 
-class StartListeningInAreaHandler
+class StartListeningFromClipHandler
 {
     sc::StartListeningContext& mrStartCxt;
     sc::EndListeningContext& mrEndCxt;
 
 public:
-    StartListeningInAreaHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
+    StartListeningFromClipHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
         mrStartCxt(rStartCxt), mrEndCxt(rEndCxt) {}
 
     void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
@@ -1221,10 +1221,10 @@ private:
 
 }
 
-void ScColumn::StartListeningInArea(
+void ScColumn::StartListeningFromClip(
     sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, SCROW nRow1, SCROW nRow2 )
 {
-    StartListeningInAreaHandler aFunc(rStartCxt, rEndCxt);
+    StartListeningFromClipHandler aFunc(rStartCxt, rEndCxt);
     sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
 }
 
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 8e3871f..f23da4a 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2430,7 +2430,7 @@ void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
         ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
         for (; itr != itrEnd && *itr < nMax; ++itr)
             if (maTabs[*itr])
-                maTabs[*itr]->StartListeningInArea(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
+                maTabs[*itr]->StartListeningFromClip(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
     }
 }
 
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 8d7ecc6..8855d65 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1059,7 +1059,7 @@ void ScTable::SetDirtyFromClip(
             aCol[i].SetDirtyFromClip(nRow1, nRow2, rBroadcastSpans);
 }
 
-void ScTable::StartListeningInArea(
+void ScTable::StartListeningFromClip(
     sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
 {
@@ -1067,7 +1067,7 @@ void ScTable::StartListeningInArea(
     if (nRow2 > MAXROW) nRow2 = MAXROW;
     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
         for (SCCOL i = nCol1; i <= nCol2; i++)
-            aCol[i].StartListeningInArea(rStartCxt, rEndCxt, nRow1, nRow2);
+            aCol[i].StartListeningFromClip(rStartCxt, rEndCxt, nRow1, nRow2);
 }
 
 void ScTable::CopyToTable(
commit 6b4e767cf90e4f384a6b3dd43aa51c09b500b97b
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 17:37:07 2014 -0500

    Use group-area listeners during copy & paste if we can.
    
    This should reduce the number of area listeners generated esp. when
    replicating tons of formula cells down the column.
    
    Change-Id: I1ea8f51f667e6b0e1a646f84d79f5e8430b478d5

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index adcd17a..6187d4e 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -255,7 +255,8 @@ public:
     void CopyFromClip(
         sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, long nDy, ScColumn& rColumn );
 
-    void StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 );
+    void StartListeningInArea(
+        sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, SCROW nRow1, SCROW nRow2 );
 
     void        RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow );
 
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index ea1e2a3..b1e06e1 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -437,7 +437,8 @@ public:
         SCsCOL nDx, SCsROW nDy, ScTable* pTable );
 
     void StartListeningInArea(
-        sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
+        sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+        SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
 
     void SetDirtyFromClip(
         SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans );
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 6f31cf5..67c51be 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1535,28 +1535,6 @@ void ScColumn::StartNeededListeners()
 
 namespace {
 
-class StartListeningInAreaHandler
-{
-    sc::StartListeningContext& mrCxt;
-public:
-    StartListeningInAreaHandler(sc::StartListeningContext& rCxt) : mrCxt(rCxt) {}
-
-    void operator() (size_t /*nRow*/, ScFormulaCell* p)
-    {
-        p->StartListeningTo(mrCxt);
-    }
-};
-
-}
-
-void ScColumn::StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 )
-{
-    StartListeningInAreaHandler aFunc(rCxt);
-    sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
-}
-
-namespace {
-
 void applyTextNumFormat( ScColumn& rCol, SCROW nRow, SvNumberFormatter* pFormatter )
 {
     sal_uInt32 nFormat = pFormatter->GetStandardFormat(NUMBERFORMAT_TEXT);
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index f0d28eb..925bb4c 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -1135,4 +1135,97 @@ bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
     return aRet.first != maCells.end();
 }
 
+namespace {
+
+class StartListeningInAreaHandler
+{
+    sc::StartListeningContext& mrStartCxt;
+    sc::EndListeningContext& mrEndCxt;
+
+public:
+    StartListeningInAreaHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
+        mrStartCxt(rStartCxt), mrEndCxt(rEndCxt) {}
+
+    void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
+    {
+        if (node.type != sc::element_type_formula)
+            // We are only interested in formulas.
+            return;
+
+        ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
+        ScFormulaCell** ppEnd = ppBeg + nDataSize;
+
+        ScFormulaCell** pp = ppBeg;
+
+        // If the first formula cell belongs to a group and it's not the top
+        // cell, move up to the top cell of the group, and have all the extra
+        // formula cells stop listening.
+
+        ScFormulaCell* pFC = *pp;
+        if (pFC->IsShared() && !pFC->IsSharedTop())
+        {
+            SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
+            if (nBackTrackSize > 0)
+            {
+                assert(static_cast<size_t>(nBackTrackSize) <= nOffset);
+                for (SCROW i = 0; i < nBackTrackSize; ++i)
+                    --pp;
+                endListening(pp, ppBeg);
+            }
+        }
+
+        for (; pp != ppEnd; ++pp)
+        {
+            pFC = *pp;
+
+            if (!pFC->IsSharedTop())
+            {
+                pFC->StartListeningTo(mrStartCxt);
+                continue;
+            }
+
+            // If This is the last group in the range, see if the group
+            // extends beyond the range, in which case have the excess
+            // formula cells stop listening.
+            size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
+            if (nEndGroupPos > nDataSize)
+            {
+                size_t nExcessSize = nEndGroupPos - nDataSize;
+                ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
+                ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
+                endListening(ppGrp, ppGrpEnd);
+
+                // Register formula cells as a group.
+                sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
+                pp = ppEnd - 1; // Move to the one before the end position.
+            }
+            else
+            {
+                // Register formula cells as a group.
+                sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
+                pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
+            }
+        }
+    }
+
+private:
+    void endListening( ScFormulaCell** pp, ScFormulaCell** ppEnd )
+    {
+        for (; pp != ppEnd; ++pp)
+        {
+            ScFormulaCell& rFC = **pp;
+            rFC.EndListeningTo(mrEndCxt);
+        }
+    }
+};
+
+}
+
+void ScColumn::StartListeningInArea(
+    sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt, SCROW nRow1, SCROW nRow2 )
+{
+    StartListeningInAreaHandler aFunc(rStartCxt, rEndCxt);
+    sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 8440e2b..8e3871f 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2420,12 +2420,17 @@ void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
 {
     if (nInsFlag & IDF_CONTENTS)
     {
-        sc::StartListeningContext aCxt(*this);
+        boost::shared_ptr<sc::ColumnBlockPositionSet> pSet(
+            new sc::ColumnBlockPositionSet(*this));
+
+        sc::StartListeningContext aStartCxt(*this, pSet);
+        sc::EndListeningContext aEndCxt(*this, pSet, NULL);
+
         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
         ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
         for (; itr != itrEnd && *itr < nMax; ++itr)
             if (maTabs[*itr])
-                maTabs[*itr]->StartListeningInArea(aCxt, nCol1, nRow1, nCol2, nRow2);
+                maTabs[*itr]->StartListeningInArea(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
     }
 }
 
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 6b3cbe4..788cb73 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3947,6 +3947,9 @@ void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
     if (pDoc->IsClipOrUndo() || IsInChangeTrack())
         return;
 
+    if (!HasBroadcaster())
+        return;
+
     pDoc->SetDetectiveDirty(true);  // It has changed something
 
     if ( GetCode()->IsRecalcModeAlways() )
@@ -3989,6 +3992,9 @@ void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
     if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
         return;
 
+    if (!HasBroadcaster())
+        return;
+
     ScDocument& rDoc = rCxt.getDoc();
     rDoc.SetDetectiveDirty(true);  // It has changed something
 
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 66db45d..8d7ecc6 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1060,13 +1060,14 @@ void ScTable::SetDirtyFromClip(
 }
 
 void ScTable::StartListeningInArea(
-    sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+    sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
 {
     if (nCol2 > MAXCOL) nCol2 = MAXCOL;
     if (nRow2 > MAXROW) nRow2 = MAXROW;
     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
         for (SCCOL i = nCol1; i <= nCol2; i++)
-            aCol[i].StartListeningInArea(rCxt, nRow1, nRow2);
+            aCol[i].StartListeningInArea(rStartCxt, rEndCxt, nRow1, nRow2);
 }
 
 void ScTable::CopyToTable(
commit 627bfb4e664a3fde23c06aa4dd0809de84ec97c5
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 14:16:28 2014 -0500

    Avoid unnecessary broadcasting when deleting cells prior to pasting.
    
    Change-Id: Id224fd6fdb804350e7e00634db802e80090ae6d7

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 9dfdbb2..adcd17a 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -247,7 +247,9 @@ public:
     bool InitBlockPosition( sc::ColumnBlockPosition& rBlockPos );
     bool InitBlockPosition( sc::ColumnBlockConstPosition& rBlockPos ) const;
 
-    void DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol );
+    void DeleteBeforeCopyFromClip(
+        sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans );
+
     void CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset );
 
     void CopyFromClip(
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index a094bc2..d904473 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1243,7 +1243,8 @@ public:
 
     bool InitColumnBlockPosition( sc::ColumnBlockPosition& rBlokPos, SCTAB nTab, SCCOL nCol );
 
-    void DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScMarkData& rMark );
+    void DeleteBeforeCopyFromClip(
+        sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans );
 
     bool CopyOneCellFromClip(
         sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index daacd60..ea1e2a3 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -426,7 +426,8 @@ public:
 
     bool InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol );
 
-    void DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScTable& rClipTab );
+    void DeleteBeforeCopyFromClip(
+        sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans );
 
     void CopyOneCellFromClip(
         sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 2d6609d..f0d28eb 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -41,7 +41,8 @@ bool ScColumn::IsMerged( SCROW nRow ) const
     return pAttrArray->IsMerged(nRow);
 }
 
-void ScColumn::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol )
+void ScColumn::DeleteBeforeCopyFromClip(
+    sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans )
 {
     sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
     if (!ValidRow(aRange.mnRow1) || !ValidRow(aRange.mnRow2))
@@ -107,7 +108,7 @@ void ScColumn::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const Sc
         SCROW nRow2 = it->mnRow2;
 
         if (nDelFlag & IDF_CONTENTS)
-            DeleteCells(aBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows);
+            DeleteCells(aBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows, &rBroadcastSpans);
 
         if (nDelFlag & IDF_NOTE)
             DeleteCellNotes(aBlockPos, nRow1, nRow2);
@@ -134,8 +135,6 @@ void ScColumn::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const Sc
         else if ((nDelFlag & IDF_HARDATTR) == IDF_HARDATTR)
             pAttrArray->DeleteHardAttr(nRow1, nRow2);
     }
-
-    BroadcastCells(aDeletedRows, SC_HINT_DATACHANGED);
 }
 
 void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset )
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index bca55c0..8440e2b 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2712,7 +2712,7 @@ void ScDocument::CopyFromClip( const ScRange& rDestRange, const ScMarkData& rMar
         {
             // Delete cells in the destination only if their corresponding clip cells are not empty.
             aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
-            DeleteBeforeCopyFromClip(aCxt, rMark);
+            DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans);
         }
         else
             DeleteArea(nCol1, nRow1, nCol2, nRow2, rMark, nDelFlag, false, &aBroadcastSpans);
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index 49c0a9f..1f14d5d 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -37,7 +37,8 @@ bool ScDocument::IsMerged( const ScAddress& rPos ) const
     return pTab->IsMerged(rPos.Col(), rPos.Row());
 }
 
-void ScDocument::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScMarkData& rMark )
+void ScDocument::DeleteBeforeCopyFromClip(
+    sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans )
 {
     SCTAB nClipTab = 0;
     const TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
@@ -55,7 +56,7 @@ void ScDocument::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const
         while (!rClipTabs[nClipTab])
             nClipTab = (nClipTab+1) % nClipTabCount;
 
-        pTab->DeleteBeforeCopyFromClip(rCxt, *rClipTabs[nClipTab]);
+        pTab->DeleteBeforeCopyFromClip(rCxt, *rClipTabs[nClipTab], rBroadcastSpans);
 
         nClipTab = (nClipTab+1) % nClipTabCount;
     }
diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx
index 5686921..79a645c 100644
--- a/sc/source/core/data/table7.cxx
+++ b/sc/source/core/data/table7.cxx
@@ -24,7 +24,8 @@ bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const
     return aCol[nCol].IsMerged(nRow);
 }
 
-void ScTable::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScTable& rClipTab )
+void ScTable::DeleteBeforeCopyFromClip(
+    sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans )
 {
     sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
     if (!ValidCol(aRange.mnCol1) || !ValidCol(aRange.mnCol2))
@@ -37,15 +38,13 @@ void ScTable::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScT
     ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
     SCCOL nClipCol = aClipRange.aStart.Col();
     {
-        ScBulkBroadcast aBulkBroadcast(pDocument->GetBASM());
-
         for (SCCOL nCol = aRange.mnCol1; nCol <= aRange.mnCol2; ++nCol, ++nClipCol)
         {
             if (nClipCol > aClipRange.aEnd.Col())
                 nClipCol = aClipRange.aStart.Col(); // loop through columns.
 
             const ScColumn& rClipCol = rClipTab.aCol[nClipCol];
-            aCol[nCol].DeleteBeforeCopyFromClip(rCxt, rClipCol);
+            aCol[nCol].DeleteBeforeCopyFromClip(rCxt, rClipCol, rBroadcastSpans);
         }
     }
 
commit de0b80aaaf1a01ee6106e7e63ef3d8e32a164f24
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 13:45:35 2014 -0500

    Rename methods whose name no longer represent what they do.
    
    We now do all broadcasting at the end of CopyFromClip, and nowhere else
    during pasting from clip.
    
    Change-Id: I1cb2c529f127d6e4080e49e4827f048ce5a19f95

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 2f29441..9dfdbb2 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -349,7 +349,7 @@ public:
     bool IsFormulaDirty( SCROW nRow ) const;
 
     void SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt );
-    void BroadcastInArea( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans );
+    void SetDirtyFromClip( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans );
     void SetDirty( SCROW nRow1, SCROW nRow2 );
     void        SetDirtyVar();
     void        SetDirtyAfterLoad();
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index f766f54..a094bc2 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1258,7 +1258,7 @@ public:
                                         SCCOL nCol2, SCROW nRow2,
                                         const ScMarkData& rMark, InsertDeleteFlags nInsFlag );
 
-    void BroadcastFromClip(
+    void SetDirtyFromClip(
         SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
         InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans );
 
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 5a6f015..daacd60 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -438,7 +438,7 @@ public:
     void StartListeningInArea(
         sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
 
-    void BroadcastInArea(
+    void SetDirtyFromClip(
         SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans );
 
     void CopyToTable(
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index 86faef8..a019139 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -3018,7 +3018,7 @@ void ScColumn::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
     sc::ProcessFormula(maCells, aFunc);
 }
 
-void ScColumn::BroadcastInArea( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
+void ScColumn::SetDirtyFromClip( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
 {
     // Set all formula cells in the range dirty, and pick up all non-formula
     // cells for later broadcasting.  We don't broadcast here.
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 8b58217..bca55c0 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2429,18 +2429,17 @@ void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
     }
 }
 
-void ScDocument::BroadcastFromClip(
+void ScDocument::SetDirtyFromClip(
     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
     InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans )
 {
     if (nInsFlag & IDF_CONTENTS)
     {
-        ScBulkBroadcast aBulkBroadcast( GetBASM());
         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
         ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
         for (; itr != itrEnd && *itr < nMax; ++itr)
             if (maTabs[*itr])
-                maTabs[*itr]->BroadcastInArea(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
+                maTabs[*itr]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
     }
 }
 
@@ -2815,8 +2814,10 @@ void ScDocument::CopyFromClip( const ScRange& rDestRange, const ScMarkData& rMar
 
     // Listener aufbauen nachdem alles inserted wurde
     StartListeningFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag );
-    // nachdem alle Listener aufgebaut wurden, kann gebroadcastet werden
-    BroadcastFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans);
+
+    // Set all formula cells dirty, and collect non-empty non-formula cell
+    // positions so that we can broadcast on them below.
+    SetDirtyFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans);
 
     {
         ScBulkBroadcast aBulkBroadcast( GetBASM());
@@ -2901,7 +2902,7 @@ void ScDocument::CopyMultiRangeFromClip(
     StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(),
                            aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag );
     // nachdem alle Listener aufgebaut wurden, kann gebroadcastet werden
-    BroadcastFromClip(
+    SetDirtyFromClip(
         aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aEnd.Col(), aDestRange.aEnd.Row(),
         rMark, nInsFlag, aBroadcastSpans);
 
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index c46977e..66db45d 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1049,14 +1049,14 @@ void ScTable::StartNeededListeners()
         aCol[i].StartNeededListeners();
 }
 
-void ScTable::BroadcastInArea(
+void ScTable::SetDirtyFromClip(
     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
 {
     if (nCol2 > MAXCOL) nCol2 = MAXCOL;
     if (nRow2 > MAXROW) nRow2 = MAXROW;
     if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
         for (SCCOL i = nCol1; i <= nCol2; i++)
-            aCol[i].BroadcastInArea(nRow1, nRow2, rBroadcastSpans);
+            aCol[i].SetDirtyFromClip(nRow1, nRow2, rBroadcastSpans);
 }
 
 void ScTable::StartListeningInArea(
commit 550f0797e64e38a4ef5721857f5057f692680118
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Mon Nov 17 13:06:51 2014 -0500

    Avoid area listeners and such during formula cell paste replication.
    
    All formula cells get marked dirty and start area-listening at the very
    end of CopyFromClip.  No need to do it in CloneFormulaCells.
    
    Change-Id: I9faf48fd722c2ebcf4b74d5e523317b5d9c71a22

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 57f42df..2f29441 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -314,7 +314,8 @@ public:
 
     bool HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const;
 
-    void CloneFormulaCell( const ScFormulaCell& rSrc, const std::vector<sc::RowSpan>& rRanges );
+    void CloneFormulaCell(
+        const ScFormulaCell& rSrc, const std::vector<sc::RowSpan>& rRanges, sc::StartListeningContext* pCxt );
 
     svl::SharedString GetSharedString( SCROW nRow ) const;
 
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index 94634dd..2d6609d 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -213,7 +213,7 @@ void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1,
                 std::vector<sc::RowSpan> aRanges;
                 aRanges.reserve(1);
                 aRanges.push_back(sc::RowSpan(nRow1, nRow2));
-                CloneFormulaCell(*rSrcCell.mpFormula, aRanges);
+                CloneFormulaCell(*rSrcCell.mpFormula, aRanges, NULL);
             }
             break;
             default:
@@ -473,11 +473,11 @@ void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDele
         DeleteArea(itSpan->mnRow1, itSpan->mnRow2, nDelFlag, bBroadcast);
 }
 
-void ScColumn::CloneFormulaCell( const ScFormulaCell& rSrc, const std::vector<sc::RowSpan>& rRanges )
+void ScColumn::CloneFormulaCell(
+    const ScFormulaCell& rSrc, const std::vector<sc::RowSpan>& rRanges, sc::StartListeningContext* pCxt )
 {
     sc::CellStoreType::iterator itPos = maCells.begin();
     sc::CellTextAttrStoreType::iterator itAttrPos = maCellTextAttrs.begin();
-    sc::StartListeningContext aCxt(*pDocument);
 
     std::vector<ScFormulaCell*> aFormulas;
     std::vector<sc::RowSpan>::const_iterator itSpan = rRanges.begin(), itSpanEnd = rRanges.end();
@@ -495,8 +495,11 @@ void ScColumn::CloneFormulaCell( const ScFormulaCell& rSrc, const std::vector<sc
         {
             // Single, ungrouped formula cell.
             ScFormulaCell* pCell = new ScFormulaCell(rSrc, *pDocument, aPos);
-            pCell->StartListeningTo(aCxt);
-            pCell->SetDirty();
+            if (pCxt)
+            {
+                pCell->StartListeningTo(*pCxt);
+                pCell->SetDirty();
+            }
             aFormulas.push_back(pCell);
         }
         else
@@ -513,8 +516,11 @@ void ScColumn::CloneFormulaCell( const ScFormulaCell& rSrc, const std::vector<sc
                     xGroup->mpTopCell = pCell;
                     xGroup->mnLength = nLen;
                 }
-                pCell->StartListeningTo(aCxt);
-                pCell->SetDirty();
+                if (pCxt)
+                {
+                    pCell->StartListeningTo(*pCxt);
+                    pCell->SetDirty();
+                }
                 aFormulas.push_back(pCell);
             }
         }
diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx
index 701e6d5..bfc29bd 100644
--- a/sc/source/core/data/table4.cxx
+++ b/sc/source/core/data/table4.cxx
@@ -61,6 +61,7 @@
 #include "conditio.hxx"
 #include "editutil.hxx"
 #include <columnspanset.hxx>
+#include <listenercontext.hxx>
 
 #include <math.h>
 #include <boost/scoped_ptr.hpp>
@@ -1163,7 +1164,8 @@ void ScTable::FillFormulaVertical(
     }
 
     aCol[nCol].DeleteRanges(aSpans, IDF_CONTENTS, false);
-    aCol[nCol].CloneFormulaCell(rSrcCell, aSpans);
+    sc::StartListeningContext aCxt(*pDocument);
+    aCol[nCol].CloneFormulaCell(rSrcCell, aSpans, &aCxt);
 
     rProgress += nRow2 - nRow1 + 1;
     if (pProgress)
commit 34c8c0bb878be82a25cdaf1d7e623843f00fed6c
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Nov 14 21:51:39 2014 -0500

    Send broadcast range to the area broadcast slot machine.
    
    Rather than iterating through cells in the range and broadcasting by
    cells individually.  This way we can take advantage of the new group
    based area listeners and it's much faster this way.
    
    Change-Id: I8a4b49bce69d89b5b4698790befe4390871c755d

diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx
index 07ebe02..bf41328 100644
--- a/sc/inc/address.hxx
+++ b/sc/inc/address.hxx
@@ -563,6 +563,9 @@ public:
     SC_DLLPUBLIC void Justify();
     SC_DLLPUBLIC void ExtendTo( const ScRange& rRange );
     SC_DLLPUBLIC bool Intersects( const ScRange& rRange ) const;    // do two ranges intersect?
+
+    ScRange Union( const ScRange& rOther ) const;
+
     void PutInOrder();
     inline bool operator==( const ScRange& rRange ) const;
     inline bool operator!=( const ScRange& rRange ) const;
diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx
index 9af7c19..ee6f17a 100644
--- a/sc/source/core/data/bcaslot.cxx
+++ b/sc/source/core/data/bcaslot.cxx
@@ -258,6 +258,83 @@ ScBroadcastAreas::const_iterator ScBroadcastAreaSlot::FindBroadcastArea(
     return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
 }
 
+namespace {
+
+void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, sal_uLong nHint )
+{
+    ScHint aHint(nHint, ScAddress());
+    ScAddress& rPos = aHint.GetAddress();
+    for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+    {
+        rPos.SetTab(nTab);
+        for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+        {
+            rPos.SetCol(nCol);
+            for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+            {
+                rPos.SetRow(nRow);
+                rBC.Broadcast(aHint);
+            }
+        }
+    }
+}
+
+}
+
+bool ScBroadcastAreaSlot::AreaBroadcast( const ScRange& rRange, sal_uLong nHint )
+{
+    if (aBroadcastAreaTbl.empty())
+        return false;
+
+    bool bInBroadcast = mbInBroadcastIteration;
+    mbInBroadcastIteration = true;
+    bool bIsBroadcasted = false;
+
+    mbHasErasedArea = false;
+
+    for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
+            aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
+    {
+        if (mbHasErasedArea && isMarkedErased( aIter))
+            continue;
+
+        ScBroadcastArea* pArea = (*aIter).mpArea;
+        const ScRange& rAreaRange = pArea->GetRange();
+
+        // Take the union of the area range and the broadcast range.
+        ScRange aUnion = rAreaRange.Union(rRange);
+        if (!aUnion.IsValid())
+            continue;
+
+        if (pArea->IsGroupListening())
+        {
+            if (pBASM->IsInBulkBroadcast())
+            {
+                pBASM->InsertBulkGroupArea(pArea, aUnion);
+            }
+            else
+            {
+                broadcastRangeByCell(pArea->GetBroadcaster(), aUnion, nHint);
+                bIsBroadcasted = true;
+            }
+        }
+        else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
+        {
+            broadcastRangeByCell(pArea->GetBroadcaster(), aUnion, nHint);
+            bIsBroadcasted = true;
+        }
+    }
+
+    mbInBroadcastIteration = bInBroadcast;
+
+    // A Notify() during broadcast may call EndListeningArea() and thus dispose
+    // an area if it was the last listener, which would invalidate an iterator
+    // pointing to it, hence the real erase is done afterwards.
+    FinallyEraseAreas();
+
+    return bIsBroadcasted;
+}
+
 bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
 {
     if (aBroadcastAreaTbl.empty())
@@ -758,6 +835,29 @@ void ScBroadcastAreaSlotMachine::EndListeningArea(
     }
 }
 
+bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScRange& rRange, sal_uLong nHint )
+{
+    bool bBroadcasted = false;
+    SCTAB nEndTab = rRange.aEnd.Tab();
+    for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
+            iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
+    {
+        ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots();
+        SCSIZE nStart, nEnd, nRowBreak;
+        ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
+        SCSIZE nOff = nStart;
+        SCSIZE nBreak = nOff + nRowBreak;
+        ScBroadcastAreaSlot** pp = ppSlots + nOff;
+        while ( nOff <= nEnd )
+        {
+            if ( *pp )
+                bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
+            ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
+        }
+    }
+    return bBroadcasted;
+}
+
 bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
 {
     const ScAddress& rAddress = rHint.GetAddress();
diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx
index 5ea4ce5..b7bdf30 100644
--- a/sc/source/core/data/documen7.cxx
+++ b/sc/source/core/data/documen7.cxx
@@ -100,24 +100,72 @@ void ScDocument::BroadcastCells( const ScRange& rRange, sal_uLong nHint )
 {
     ClearFormulaContext();
 
-    ScBulkBroadcast aBulkBroadcast(pBASM);
+    if (!pBASM)
+        return;    // Clipboard or Undo
+
+    SCTAB nTab1 = rRange.aStart.Tab();
+    SCTAB nTab2 = rRange.aEnd.Tab();
+    SCROW nRow1 = rRange.aStart.Row();
+    SCROW nRow2 = rRange.aEnd.Row();
+    SCCOL nCol1 = rRange.aStart.Col();
+    SCCOL nCol2 = rRange.aEnd.Col();
 
     ScHint aHint(nHint, ScAddress());
     ScAddress& rPos = aHint.GetAddress();
-    for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+
+    if (!bHardRecalcState)
     {
-        rPos.SetTab(nTab);
-        for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+        ScBulkBroadcast aBulkBroadcast( pBASM);     // scoped bulk broadcast
+        bool bIsBroadcasted = false;
+
+        for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
         {
-            rPos.SetCol(nCol);
-            for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+            rPos.SetTab(nTab);
+            for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
             {
                 rPos.SetRow(nRow);
-                Broadcast(aHint);
+                for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+                {
+                    rPos.SetCol(nCol);
+                    SvtBroadcaster* pBC = GetBroadcaster(rPos);
+                    if (pBC)
+                    {
+                        pBC->Broadcast(aHint);
+                        bIsBroadcasted = true;
+                    }
+                }
+            }
+        }
+
+        if (pBASM->AreaBroadcast(rRange, nHint) || bIsBroadcasted)
+            TrackFormulas(nHint);
+    }
+
+    //  Repaint fuer bedingte Formate mit relativen Referenzen:
+    for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
+    {
+        ScTable* pTab = FetchTable(nTab);
+        if (!pTab)
+            continue;
+
+        ScConditionalFormatList* pCondFormList = GetCondFormList(nTab);
+        if (pCondFormList)
+        {
+            for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+            {
+                for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+                    pCondFormList->SourceChanged(ScAddress(nCol,nRow,nTab));
             }
         }
     }
 
+    for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
+    {
+        ScTable* pTab = FetchTable(nTab);
+        if (pTab)
+            pTab->SetStreamValid(false);
+    }
+
     BroadcastUno(SfxSimpleHint(SC_HINT_DATACHANGED));
 }
 
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 2b62192..8b58217 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2590,7 +2590,6 @@ class BroadcastAction : public sc::ColumnSpanSet::ColumnAction
 {
     ScDocument& mrDoc;
     ScColumn* mpCol;
-    std::vector<SCROW> maRows;
 
 public:
     BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(NULL) {}
@@ -2606,12 +2605,9 @@ public:
             return;
 
         assert(mpCol);
-        maRows.clear();
-        maRows.reserve(nRow2-nRow1+1);
-        for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
-            maRows.push_back(nRow);
-
-        mpCol->BroadcastCells(maRows, SC_HINT_DATACHANGED);
+        ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab());
+        aRange.aEnd.SetRow(nRow2);
+        mrDoc.BroadcastCells(aRange, SC_HINT_DATACHANGED);
     };
 };
 
diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx
index bf98908..27c87f8 100644
--- a/sc/source/core/inc/bcaslot.hxx
+++ b/sc/source/core/inc/bcaslot.hxx
@@ -211,6 +211,7 @@ public:
     void EndListeningArea(
         const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea );
 
+    bool AreaBroadcast( const ScRange& rRange, sal_uLong nHint );
     bool                AreaBroadcast( const ScHint& rHint );
     /// @return true if at least one broadcast occurred.
     bool                AreaBroadcastInRange( const ScRange& rRange,
@@ -312,6 +313,7 @@ public:
     void EndListeningArea(
         const ScRange& rRange, bool bGroupListening, SvtListener* pListener );
 
+    bool AreaBroadcast( const ScRange& rRange, sal_uLong nHint );
     bool                AreaBroadcast( const ScHint& rHint ) const;
         // return: at least one broadcast occurred
     bool                AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) const;
diff --git a/sc/source/core/tool/address.cxx b/sc/source/core/tool/address.cxx
index 6ed9449..7cb2935 100644
--- a/sc/source/core/tool/address.cxx
+++ b/sc/source/core/tool/address.cxx
@@ -1374,6 +1374,21 @@ bool ScRange::Intersects( const ScRange& rRange ) const
         );
 }
 
+ScRange ScRange::Union( const ScRange& rOther ) const
+{
+    SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
+    SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
+    SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
+    SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
+    SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
+    SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
+
+    if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
+        return ScRange(ScAddress::INITIALIZE_INVALID);
+
+    return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+}
+
 void ScRange::PutInOrder()
 {
     SCCOL nCol1 = aStart.Col(), nCol2 = aEnd.Col();
commit c55a5b242585824d3c6c217f813dc9acfc3228b2
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Nov 14 17:47:42 2014 -0500

    We don't need 2 variants of this method.  One is enough.
    
    Change-Id: I001c388669737e9cf1d92f1fbb1e64fb9f4b05c3

diff --git a/sc/inc/grouparealistener.hxx b/sc/inc/grouparealistener.hxx
index 6301023..28e8f43 100644
--- a/sc/inc/grouparealistener.hxx
+++ b/sc/inc/grouparealistener.hxx
@@ -39,16 +39,6 @@ public:
     virtual void Notify( const SfxHint& rHint ) SAL_OVERRIDE;
 
     /**
-     * Given the position of a changed cell, collect all formula cells that
-     * need to be notified of the change.
-     *
-     * @param rPos position of changed cell.
-     * @param rCells all formula cells that need to be notified are put into
-     *               this container.
-     */
-    void collectFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells ) const;
-
-    /**
      * Given the row span of changed cells within a single column, collect all
      * formula cells that need to be notified of the change.
      *
diff --git a/sc/source/core/tool/grouparealistener.cxx b/sc/source/core/tool/grouparealistener.cxx
index e682d61..7a83461 100644
--- a/sc/source/core/tool/grouparealistener.cxx
+++ b/sc/source/core/tool/grouparealistener.cxx
@@ -125,95 +125,6 @@ void FormulaGroupAreaListener::notifyBulkChange( const BulkDataHint& rHint )
     std::for_each(aCells.begin(), aCells.end(), Notifier(aHint));
 }
 
-void FormulaGroupAreaListener::collectFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells ) const
-{
-    if (rPos.Tab() < maRange.aStart.Tab() || maRange.aEnd.Tab() < rPos.Tab())
-        // Wrong sheet.
-        return;
-
-    if (rPos.Col() < maRange.aStart.Col() || maRange.aEnd.Col() < rPos.Col())
-        // Outside the column range.
-        return;
-
-    ScFormulaCell** pp = mppTopCell;
-    ScFormulaCell** ppEnd = pp + mnGroupLen;
-
-    if (mbStartFixed)
-    {
-        if (mbEndFixed)
-        {
-            // Both top and bottom row positions are absolute.  Use the original range as-is.
-            SCROW nRow1 = maRange.aStart.Row();
-            SCROW nRow2 = maRange.aEnd.Row();
-            if (rPos.Row() < nRow1 || nRow2 < rPos.Row())
-                return;
-
-            for (; pp != ppEnd; ++pp)
-                rCells.push_back(*pp);
-        }
-        else
-        {
-            // Only the end row is relative.
-            SCROW nRow1 = maRange.aStart.Row();
-            SCROW nRow2 = maRange.aEnd.Row();
-            SCROW nMaxRow = nRow2 + mnGroupLen - 1;
-            if (rPos.Row() < nRow1 || nMaxRow < rPos.Row())
-                return;
-
-            if (nRow2 < rPos.Row())
-            {
-                // Skip ahead to the first hit.
-                SCROW nSkip = rPos.Row() - nRow2;
-                pp += nSkip;
-                nRow2 += nSkip;
-            }
-
-            // Notify the first hit cell and all subsequent ones.
-            for (; pp != ppEnd; ++pp)
-                rCells.push_back(*pp);
-        }
-    }
-    else if (mbEndFixed)
-    {
-        // Only the start row is relative.
-        SCROW nRow1 = maRange.aStart.Row();
-        SCROW nRow2 = maRange.aEnd.Row();
-
-        if (rPos.Row() < nRow1 || nRow2 < rPos.Row())
-            return;
-
-        for (; pp != ppEnd && nRow1 <= nRow2; ++pp, ++nRow1)
-            rCells.push_back(*pp);
-    }
-    else
-    {
-        // Both top and bottom row positions are relative.
-        SCROW nRow1 = maRange.aStart.Row();
-        SCROW nRow2 = maRange.aEnd.Row();
-        SCROW nMaxRow = nRow2 + mnGroupLen - 1;
-        if (nMaxRow < rPos.Row())
-            return;
-
-        if (nRow2 < rPos.Row())
-        {
-            // Skip ahead.
-            SCROW nSkip = rPos.Row() - nRow2;
-            pp += nSkip;
-            nRow1 += nSkip;
-            nRow2 += nSkip;
-        }
-
-        for (; pp != ppEnd; ++pp, ++nRow1, ++nRow2)
-        {
-            if (rPos.Row() < nRow1 || nRow2 < rPos.Row())
-                // Changed cell is outside the range.
-                continue;
-
-            rCells.push_back(*pp);
-        }
-    }
-}
-
 void FormulaGroupAreaListener::collectFormulaCells(
     SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const
 {
@@ -312,7 +223,7 @@ void FormulaGroupAreaListener::notifyCellChange( const SfxHint& rHint, const ScA
 {
     // Determine which formula cells within the group need to be notified of this change.
     std::vector<ScFormulaCell*> aCells;
-    collectFormulaCells(rPos, aCells);
+    collectFormulaCells(rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Row(), aCells);
     std::for_each(aCells.begin(), aCells.end(), Notifier(rHint));
 }
 
commit 2030b9ac6c68ba6f15b0283e0b4e57ae49bd67b0
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Wed Nov 12 22:18:49 2014 -0500

    Dedicated listener type tailored for formula groups.
    
    Right now, it's only used when loading an xlsx file. But eventually
    this one should be used everywhere.
    
    Change-Id: I216c3a9a33c4b8040e8284d59299e0637471fb50

diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 3f79a12..898284e 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -196,6 +196,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/core/tool/adiasync \
     sc/source/core/tool/appoptio \
     sc/source/core/tool/autoform \
+    sc/source/core/tool/bulkdatahint \
     sc/source/core/tool/brdcst \
     sc/source/core/tool/calcconfig \
     sc/source/core/tool/callform \
@@ -226,6 +227,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/core/tool/formulaopt \
     sc/source/core/tool/formulaparserpool \
     sc/source/core/tool/formularesult \
+    sc/source/core/tool/grouparealistener \
     sc/source/core/tool/hints \
     sc/source/core/tool/inputopt \
     sc/source/core/tool/interpr1 \
diff --git a/sc/inc/bulkdatahint.hxx b/sc/inc/bulkdatahint.hxx
new file mode 100644
index 0000000..31c13fb
--- /dev/null
+++ b/sc/inc/bulkdatahint.hxx
@@ -0,0 +1,43 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_SC_BULKDATAHINT_HXX
+#define INCLUDED_SC_BULKDATAHINT_HXX
+
+#include <simplehintids.hxx>
+
+class ScDocument;
+
+namespace sc {
+
+class ColumnSpanSet;
+
+class BulkDataHint : public SfxSimpleHint
+{
+    struct Impl;
+    Impl* mpImpl;
+
+    BulkDataHint( const BulkDataHint& );
+    BulkDataHint& operator= ( const BulkDataHint& );
+
+public:
+    BulkDataHint( ScDocument& rDoc, const ColumnSpanSet* pSpans );
+    virtual ~BulkDataHint();
+
+    void setSpans( const ColumnSpanSet* pSpans );
+    const ColumnSpanSet* getSpans() const;
+
+    ScDocument& getDoc();
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 3124a4c..f766f54 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1853,10 +1853,9 @@ private:
     static ScRecursionHelper*   CreateRecursionHelperInstance();
 
 public:
-    void                StartListeningArea( const ScRange& rRange,
-                                            SvtListener* pListener );
-    void                EndListeningArea( const ScRange& rRange,
-                                            SvtListener* pListener );
+    void StartListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener );
+
+    void EndListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener );
                         /** Broadcast wrapper, calls
                             rHint.GetCell()->Broadcast() and AreaBroadcast()
                             and TrackFormulas() and conditional format list
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index b65504e..67381a0 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -23,6 +23,7 @@
 #include <set>
 
 #include <boost/noncopyable.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
 
 #include <formula/tokenarray.hxx>
 #include <osl/conditn.hxx>
@@ -47,16 +48,18 @@ struct RefUpdateInsertTabContext;
 struct RefUpdateDeleteTabContext;
 struct RefUpdateMoveTabContext;
 class CompileFormulaContext;
+class FormulaGroupAreaListener;
 
 }
 
 class ScFormulaCell;
 class ScProgress;
 class ScTokenArray;
-struct ScSimilarFormulaDelta;
 
 struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
 {
+    typedef boost::ptr_vector<sc::FormulaGroupAreaListener> AreaListenersType;
+
     mutable size_t mnRefCount;
 
     ScTokenArray* mpCode;
@@ -70,6 +73,8 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
     sal_uInt8 meCalcState;
     sal_uInt8 meKernelState;
 
+    AreaListenersType maAreaListeners;
+
     ScFormulaCellGroup();
     ~ScFormulaCellGroup();
 
@@ -81,6 +86,11 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
         ScDocument& rDoc, const ScAddress& rPos, formula::FormulaGrammar::Grammar eGram );
     void compileOpenCLKernel();
 
+    sc::FormulaGroupAreaListener* getAreaListener(
+        ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed );
+
+    void endAllGroupListening( ScDocument& rDoc );
+
 #if ENABLE_THREADED_OPENCL_KERNEL_COMPILATION
     static int snCount;
     static rtl::Reference<sc::CLBuildKernelThread> sxCompilationThread;
diff --git a/sc/inc/grouparealistener.hxx b/sc/inc/grouparealistener.hxx
new file mode 100644
index 0000000..6301023
--- /dev/null
+++ b/sc/inc/grouparealistener.hxx
@@ -0,0 +1,73 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_SC_GROUPAREALISTENER_HXX
+#define INCLUDED_SC_GROUPAREALISTENER_HXX
+
+#include <address.hxx>
+
+#include <svl/listener.hxx>
+
+class ScFormulaCell;
+
+namespace sc {
+
+class BulkDataHint;
+
+class FormulaGroupAreaListener : public SvtListener
+{
+    ScRange maRange;
+    ScFormulaCell** mppTopCell;
+    SCROW mnGroupLen;
+    bool mbStartFixed;
+    bool mbEndFixed;
+
+    FormulaGroupAreaListener(); // disabled
+
+public:
+    FormulaGroupAreaListener(
+        const ScRange& rRange, ScFormulaCell** ppTopCell, SCROW nGroupLen, bool bStartFixed, bool bEndFixed );
+
+    ScRange getListeningRange() const;
+
+    virtual void Notify( const SfxHint& rHint ) SAL_OVERRIDE;
+
+    /**
+     * Given the position of a changed cell, collect all formula cells that
+     * need to be notified of the change.
+     *
+     * @param rPos position of changed cell.
+     * @param rCells all formula cells that need to be notified are put into
+     *               this container.
+     */
+    void collectFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells ) const;
+
+    /**
+     * Given the row span of changed cells within a single column, collect all
+     * formula cells that need to be notified of the change.
+     *
+     * @param nTab sheet position of the changed cell span.
+     * @param nCol column position of the changed cell span.
+     * @param nRow1 top row position of the changed cell span.
+     * @param nRow2 bottom row position of the changed cell span.
+     * @param rCells all formula cells that need to be notified are put into
+     *               this container.
+     */
+    void collectFormulaCells( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const;
+
+private:
+    void notifyCellChange( const SfxHint& rHint, const ScAddress& rPos );
+    void notifyBulkChange( const BulkDataHint& rHint );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/simplehintids.hxx b/sc/inc/simplehintids.hxx
index 34900c0..64d2b08 100644
--- a/sc/inc/simplehintids.hxx
+++ b/sc/inc/simplehintids.hxx
@@ -12,10 +12,11 @@
 
 #include <svl/smplhint.hxx>
 
-#define SC_HINT_DATACHANGED     SFX_HINT_DATACHANGED
-#define SC_HINT_TABLEOPDIRTY    SFX_HINT_USER00
-#define SC_HINT_CALCALL         SFX_HINT_USER01
-#define SC_HINT_REFERENCE       SFX_HINT_USER02
+#define SC_HINT_DATACHANGED         SFX_HINT_DATACHANGED
+#define SC_HINT_TABLEOPDIRTY        SFX_HINT_USER00
+#define SC_HINT_CALCALL             SFX_HINT_USER01
+#define SC_HINT_REFERENCE           SFX_HINT_USER02
+#define SC_HINT_BULK_DATACHANGED    SFX_HINT_USER03
 
 #endif
 
diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx
index 8229d68..9af7c19 100644
--- a/sc/source/core/data/bcaslot.cxx
+++ b/sc/source/core/data/bcaslot.cxx
@@ -27,6 +27,7 @@
 #include "docoptio.hxx"
 #include "refupdat.hxx"
 #include "table.hxx"
+#include <bulkdatahint.hxx>
 
 #if DEBUG_AREA_BROADCASTER
 #include <formulacell.hxx>
@@ -107,6 +108,13 @@ static SCSIZE nBcaSlots = initSlotDistribution( aSlotDistribution, nBcaSlotsRow)
 // Ensure that all static variables are initialized with this one call.
 #endif
 
+ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) :
+    pUpdateChainNext(NULL),
+    aRange(rRange),
+    nRefCount(0),
+    mbInUpdateChain(false),
+    mbGroupListening(false) {}
+
 ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument,
         ScBroadcastAreaSlotMachine* pBASMa ) :
     aTmpSeekBroadcastArea( ScRange()),
@@ -155,8 +163,8 @@ bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
     return false;
 }
 
-bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
-        SvtListener* pListener, ScBroadcastArea*& rpArea )
+bool ScBroadcastAreaSlot::StartListeningArea(
+    const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
 {
     bool bNewArea = false;
     OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
@@ -168,12 +176,13 @@ bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange,
         // to new and insert it would save an attempt to find it, on mass
         // operations like identical large [HV]LOOKUP() areas the new/delete
         // would add quite some penalty for all but the first formula cell.
-        ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
+        ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
         if (aIter != aBroadcastAreaTbl.end())
             rpArea = (*aIter).mpArea;
         else
         {
             rpArea = new ScBroadcastArea( rRange);
+            rpArea->SetGroupListening(bGroupListening);
             if (aBroadcastAreaTbl.insert( rpArea).second)
             {
                 rpArea->IncRef();
@@ -208,13 +217,13 @@ void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
 
 // If rpArea != NULL then no listeners are stopped, only the area is removed
 // and the reference count decremented.
-void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
-        SvtListener* pListener, ScBroadcastArea*& rpArea )
+void ScBroadcastAreaSlot::EndListeningArea(
+    const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
 {
     OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
     if ( !rpArea )
     {
-        ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
+        ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
         if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
             return;
         rpArea = (*aIter).mpArea;
@@ -230,7 +239,7 @@ void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
     {
         if (rpArea && !rpArea->GetBroadcaster().HasListeners())
         {
-            ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange));
+            ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
             if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
                 return;
             OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
@@ -242,9 +251,10 @@ void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange,
 }
 
 ScBroadcastAreas::const_iterator ScBroadcastAreaSlot::FindBroadcastArea(
-        const ScRange& rRange ) const
+        const ScRange& rRange, bool bGroupListening ) const
 {
     aTmpSeekBroadcastArea.UpdateRange( rRange);
+    aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
     return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
 }
 
@@ -270,7 +280,19 @@ bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
         const ScRange& rAreaRange = pArea->GetRange();
         if (rAreaRange.In( rAddress))
         {
-            if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
+            if (pArea->IsGroupListening())
+            {
+                if (pBASM->IsInBulkBroadcast())
+                {
+                    pBASM->InsertBulkGroupArea(pArea, rAddress);
+                }
+                else
+                {
+                    pArea->GetBroadcaster().Broadcast( rHint);
+                    bIsBroadcasted = true;
+                }
+            }
+            else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
             {
                 pArea->GetBroadcaster().Broadcast( rHint);
                 bIsBroadcasted = true;
@@ -296,27 +318,46 @@ bool ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange,
     bool bInBroadcast = mbInBroadcastIteration;
     mbInBroadcastIteration = true;
     bool bIsBroadcasted = false;
+
+    mbHasErasedArea = false;
+
     for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
             aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
     {
-        if (isMarkedErased( aIter))
+        if (mbHasErasedArea && isMarkedErased( aIter))
             continue;
+
         ScBroadcastArea* pArea = (*aIter).mpArea;
         const ScRange& rAreaRange = pArea->GetRange();
         if (rAreaRange.Intersects( rRange ))
         {
-            if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
+            if (pArea->IsGroupListening())
+            {
+                if (pBASM->IsInBulkBroadcast())
+                {
+                    pBASM->InsertBulkGroupArea(pArea, rRange);
+                }
+                else
+                {
+                    pArea->GetBroadcaster().Broadcast( rHint);
+                    bIsBroadcasted = true;
+                }
+            }
+            else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
             {
                 pArea->GetBroadcaster().Broadcast( rHint);
                 bIsBroadcasted = true;
             }
         }
     }
+
     mbInBroadcastIteration = bInBroadcast;
+
     // A Notify() during broadcast may call EndListeningArea() and thus dispose
     // an area if it was the last listener, which would invalidate an iterator
     // pointing to it, hence the real erase is done afterwards.
     FinallyEraseAreas();
+
     return bIsBroadcasted;
 }
 
@@ -487,6 +528,7 @@ void ScBroadcastAreaSlot::GetAllListeners(
         {
             sc::AreaListener aEntry;
             aEntry.maArea = rAreaRange;
+            aEntry.mbGroupListening = pArea->IsGroupListening();
             aEntry.mpListener = *itLst;
             rListeners.push_back(aEntry);
         }
@@ -617,8 +659,8 @@ inline void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot
     }
 }
 
-void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
-        SvtListener* pListener )
+void ScBroadcastAreaSlotMachine::StartListeningArea(
+    const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
 {
     if ( rRange == BCA_LISTEN_ALWAYS  )
     {
@@ -653,7 +695,7 @@ void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
                     // ScBroadcastArea, listeners were added to an already
                     // existing identical area that doesn't need to be inserted
                     // to slots again.
-                    if (!(*pp)->StartListeningArea( rRange, pListener, pArea))
+                    if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
                         bDone = true;
                 }
                 else
@@ -664,8 +706,8 @@ void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange,
     }
 }
 
-void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
-        SvtListener* pListener )
+void ScBroadcastAreaSlotMachine::EndListeningArea(
+    const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
 {
     if ( rRange == BCA_LISTEN_ALWAYS  )
     {
@@ -700,7 +742,7 @@ void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
                 do
                 {
                     if ( *pp )
-                        (*pp)->EndListeningArea( rRange, pListener, pArea );
+                        (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
                 } while (++pp < pStop);
             }
             else
@@ -708,7 +750,7 @@ void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange,
                 while ( nOff <= nEnd )
                 {
                     if ( *pp )
-                        (*pp)->EndListeningArea( rRange, pListener, pArea );
+                        (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
                     ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
                 }
             }
@@ -988,7 +1030,10 @@ void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast()
     if (nInBulkBroadcast > 0)
     {
         if (--nInBulkBroadcast == 0)
+        {
             ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
+            BulkBroadcastGroupAreas();
+        }
     }
 }
 
@@ -997,6 +1042,41 @@ bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
     return aBulkBroadcastAreas.insert( pArea ).second;
 }
 
+void ScBroadcastAreaSlotMachine::InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange )
+{
+    BulkGroupAreasType::iterator it = maBulkGroupAreas.lower_bound(pArea);
+    if (it == maBulkGroupAreas.end() || maBulkGroupAreas.key_comp()(pArea, it->first))
+    {
+        // Insert a new one.
+        it = maBulkGroupAreas.insert(it, pArea, new sc::ColumnSpanSet(false));
+    }
+
+    sc::ColumnSpanSet* pSet = it->second;
+    assert(pSet);
+    pSet->set(rRange, true);
+}
+
+void ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas()
+{
+    if (maBulkGroupAreas.empty())
+        return;
+
+    sc::BulkDataHint aHint(*pDoc, NULL);
+
+    BulkGroupAreasType::iterator it = maBulkGroupAreas.begin(), itEnd = maBulkGroupAreas.end();
+    for (; it != itEnd; ++it)
+    {
+        ScBroadcastArea* pArea = it->first;
+        const sc::ColumnSpanSet* pSpans = it->second;
+        assert(pArea);
+        assert(pSpans);
+        aHint.setSpans(pSpans);
+        pArea->GetBroadcaster().Broadcast(aHint);
+    }
+
+    maBulkGroupAreas.clear();
+}
+
 size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
 {
     return aBulkBroadcastAreas.erase( pArea );
diff --git a/sc/source/core/data/colorscale.cxx b/sc/source/core/data/colorscale.cxx
index b915e25..9de8919 100644
--- a/sc/source/core/data/colorscale.cxx
+++ b/sc/source/core/data/colorscale.cxx
@@ -81,7 +81,7 @@ void ScFormulaListener::startListening(ScTokenArray* pArr, const ScAddress& rPos
                             aCell2.SetCol(MAXCOL);
                         }
                     }
-                    mpDoc->StartListeningArea(ScRange(aCell1, aCell2), this);
+                    mpDoc->StartListeningArea(ScRange(aCell1, aCell2), false, this);
                     maCells.push_back(ScRange(aCell1, aCell2));
                 }
             }
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index 1693ad7..8938925 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -1187,7 +1187,7 @@ void ScDocument::AddLookupCache( ScLookupCache & rCache )
         OSL_FAIL( "ScDocument::AddLookupCache: couldn't add to hash map");
     }
     else
-        StartListeningArea( rCache.getRange(), &rCache);
+        StartListeningArea( rCache.getRange(), false, &rCache);
 }
 
 void ScDocument::RemoveLookupCache( ScLookupCache & rCache )
@@ -1202,7 +1202,7 @@ void ScDocument::RemoveLookupCache( ScLookupCache & rCache )
     {
         ScLookupCache* pCache = (*it).second;
         pLookupCacheMapImpl->aCacheMap.erase( it);
-        EndListeningArea( pCache->getRange(), &rCache);
+        EndListeningArea( pCache->getRange(), false, &rCache);
     }
 }
 
diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx
index af09c83..5ea4ce5 100644
--- a/sc/source/core/data/documen7.cxx
+++ b/sc/source/core/data/documen7.cxx
@@ -45,20 +45,17 @@ extern const ScFormulaCell* pLastFormulaTreeTop;    // cellform.cxx Err527 WorkA
 
 // STATIC DATA -----------------------------------------------------------
 
-void ScDocument::StartListeningArea( const ScRange& rRange,
-        SvtListener* pListener
-    )
+void ScDocument::StartListeningArea(
+    const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
 {
     if ( pBASM )
-        pBASM->StartListeningArea( rRange, pListener );
+        pBASM->StartListeningArea(rRange, bGroupListening, pListener);
 }
 
-void ScDocument::EndListeningArea( const ScRange& rRange,
-        SvtListener* pListener
-    )
+void ScDocument::EndListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
 {
     if ( pBASM )
-        pBASM->EndListeningArea( rRange, pListener );
+        pBASM->EndListeningArea(rRange, bGroupListening, pListener);
 }
 
 void ScDocument::Broadcast( const ScHint& rHint )
@@ -155,7 +152,7 @@ void ScDocument::BroadcastRefMoved( const sc::RefMovedHint& rHint )
         std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
         for (; it != itEnd; ++it)
         {
-            pBASM->EndListeningArea(it->maArea, it->mpListener);
+            pBASM->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener);
             it->mpListener->Notify(rHint); // Adjust the references.
         }
     }
@@ -206,7 +203,7 @@ void ScDocument::BroadcastRefMoved( const sc::RefMovedHint& rHint )
         {
             ScRange aNewRange = it->maArea;
             aNewRange.Move(rDelta.Col(), rDelta.Row(), rDelta.Tab());
-            pBASM->StartListeningArea(aNewRange, it->mpListener);
+            pBASM->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener);
         }
     }
 }
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 1d9b20f..6b3cbe4 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -58,6 +58,7 @@
 #include <refhint.hxx>
 #include <listenerquery.hxx>
 #include <listenerqueryids.hxx>
+#include <grouparealistener.hxx>
 
 #include <boost/scoped_ptr.hpp>
 
@@ -508,6 +509,29 @@ void ScFormulaCellGroup::compileOpenCLKernel()
     meKernelState = sc::OpenCLKernelBinaryCreated;
 }
 
+sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener(
+    ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
+{
+    // TODO : Find existing one with the same criteria.
+    maAreaListeners.push_back(new sc::FormulaGroupAreaListener(rRange, ppTopCell, mnLength, bStartFixed, bEndFixed));
+    return &maAreaListeners.back();
+}
+
+void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc )
+{
+    AreaListenersType::iterator it = maAreaListeners.begin(), itEnd = maAreaListeners.end();
+    for (; it != itEnd; ++it)
+    {
+        sc::FormulaGroupAreaListener* pListener = &(*it);
+        ScRange aListenRange = pListener->getListeningRange();
+        // This "always listen" special range is never grouped.
+        bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
+        rDoc.EndListeningArea(aListenRange, bGroupListening, pListener);
+    }
+
+    maAreaListeners.clear();
+}
+
 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos ) :
     eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
     pCode(new ScTokenArray),
@@ -1854,14 +1878,14 @@ void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam )
                 if (pCode->IsRecalcModeAlways())
                 {
                     // The formula was previously volatile, but no more.
-                    pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
+                    pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
                     pCode->SetExclusiveRecalcModeNormal();
                 }
                 else
                 {
                     // non-volatile formula.  End listening to the area in case
                     // it's listening due to macro module change.
-                    pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, this);
+                    pDocument->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
                 }
                 pDocument->RemoveFromFormulaTree(this);
             break;
@@ -3799,7 +3823,7 @@ void startListeningArea(
                 aCell2.SetCol(MAXCOL);
             }
         }
-        rDoc.StartListeningArea(ScRange(aCell1, aCell2), pCell);
+        rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell);
     }
 }
 
@@ -3807,6 +3831,9 @@ void startListeningArea(
 
 void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
 {
+    if (mxGroup)
+        mxGroup->endAllGroupListening(*pDoc);
+
     if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
         return;
 
@@ -3815,7 +3842,7 @@ void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
     ScTokenArray* pArr = GetCode();
     if( pArr->IsRecalcModeAlways() )
     {
-        pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, this);
+        pDoc->StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
     }
 
     pArr->Reset();
@@ -3845,6 +3872,9 @@ void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
 {
     ScDocument& rDoc = rCxt.getDoc();
 
+    if (mxGroup)
+        mxGroup->endAllGroupListening(rDoc);
+
     if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
         return;
 
@@ -3853,7 +3883,7 @@ void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
     ScTokenArray* pArr = GetCode();
     if( pArr->IsRecalcModeAlways() )
     {
-        rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, this);
+        rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this);
     }
 
     pArr->Reset();
@@ -3902,7 +3932,7 @@ void endListeningArea(
             }
         }
 
-        rDoc.EndListeningArea(ScRange(aCell1, aCell2), pCell);
+        rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell);
     }
 }
 
@@ -3911,6 +3941,9 @@ void endListeningArea(
 void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
         ScAddress aCellPos )
 {
+    if (mxGroup)
+        mxGroup->endAllGroupListening(*pDoc);
+
     if (pDoc->IsClipOrUndo() || IsInChangeTrack())
         return;
 
@@ -3918,7 +3951,7 @@ void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
 
     if ( GetCode()->IsRecalcModeAlways() )
     {
-        pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, this );
+        pDoc->EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
     }
 
     if (!pArr)
@@ -3950,6 +3983,9 @@ void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
 
 void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
 {
+    if (mxGroup)
+        mxGroup->endAllGroupListening(rCxt.getDoc());
+
     if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack())
         return;
 
@@ -3963,7 +3999,7 @@ void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
 
     if (pArr->IsRecalcModeAlways())
     {
-        rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, this);
+        rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this);
     }
 
     pArr->Reset();
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index f7c6118..c42f777 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -739,7 +739,7 @@ void ScTable::SortReorderByColumn(
         std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
         for (; it != itEnd; ++it)
         {
-            pDocument->EndListeningArea(it->maArea, it->mpListener);
+            pDocument->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener);
             aListeners.push_back( it->mpListener);
         }
     }
@@ -770,7 +770,7 @@ void ScTable::SortReorderByColumn(
                 aNewRange.aStart.SetCol( itCol->second);
                 aNewRange.aEnd.SetCol( itCol->second);
             }
-            pDocument->StartListeningArea(aNewRange, it->mpListener);
+            pDocument->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener);
         }
     }
 
@@ -1020,7 +1020,7 @@ void ScTable::SortReorderByRow(
         std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
         for (; it != itEnd; ++it)
         {
-            pDocument->EndListeningArea(it->maArea, it->mpListener);
+            pDocument->EndListeningArea(it->maArea, it->mbGroupListening, it->mpListener);
             aListeners.push_back( it->mpListener);
         }
     }
@@ -1082,7 +1082,7 @@ void ScTable::SortReorderByRow(
                 aNewRange.aStart.SetRow( itRow->second);
                 aNewRange.aEnd.SetRow( itRow->second);
             }
-            pDocument->StartListeningArea(aNewRange, it->mpListener);
+            pDocument->StartListeningArea(aNewRange, it->mbGroupListening, it->mpListener);
         }
     }
 
diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx
index 2f63207..bf98908 100644
--- a/sc/source/core/inc/bcaslot.hxx
+++ b/sc/source/core/inc/bcaslot.hxx
@@ -22,17 +22,23 @@
 
 #include <set>
 #include <boost/unordered_set.hpp>
+#include <boost/ptr_container/ptr_map.hpp>
 #include <functional>
+
 #include <svl/broadcast.hxx>
 
 #include "global.hxx"
 #include "brdcst.hxx"
+#include <columnspanset.hxx>
+
+class ScBroadcastArea;
 
 namespace sc {
 
 struct AreaListener
 {
     ScRange maArea;
+    bool mbGroupListening;
     SvtListener* mpListener;
 };
 
@@ -42,19 +48,20 @@ struct AreaListener
     Used in a Unique Associative Container.
  */
 
-class ScBroadcastArea
+class ScBroadcastArea : boost::noncopyable
 {
 private:
     ScBroadcastArea*    pUpdateChainNext;
     SvtBroadcaster      aBroadcaster;
     ScRange             aRange;
     sal_uLong               nRefCount;
-    bool                bInUpdateChain;
+
+    bool mbInUpdateChain:1;
+    bool mbGroupListening:1;
 
 public:
-            ScBroadcastArea( const ScRange& rRange )
-                : pUpdateChainNext( NULL ), aRange( rRange ),
-                nRefCount( 0 ), bInUpdateChain( false ) {}
+    ScBroadcastArea( const ScRange& rRange );
+
     inline SvtBroadcaster&       GetBroadcaster()       { return aBroadcaster; }
     inline const SvtBroadcaster& GetBroadcaster() const { return aBroadcaster; }
     inline void         UpdateRange( const ScRange& rNewRange )
@@ -67,8 +74,11 @@ public:
     inline sal_uLong        GetRef() { return nRefCount; }
     inline ScBroadcastArea* GetUpdateChainNext() const { return pUpdateChainNext; }
     inline void         SetUpdateChainNext( ScBroadcastArea* p ) { pUpdateChainNext = p; }
-    inline bool         IsInUpdateChain() const { return bInUpdateChain; }
-    inline void         SetInUpdateChain( bool b ) { bInUpdateChain = b; }
+    inline bool         IsInUpdateChain() const { return mbInUpdateChain; }

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list